This commit was manufactured by cvs2svn to create tag
'jaxp-ri-1_2_0-fcs-06'.
git-svn-id: https://svn.apache.org/repos/asf/xalan/java/tags/jaxp-ri-1_2_0-fcs-06@336540 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build.xml b/build.xml
index 0e09c0f..0378c20 100644
--- a/build.xml
+++ b/build.xml
@@ -361,7 +361,19 @@
</javac>
</target>
- <target name="xsltc.jar" depends="xsltc.compile"
+ <target name="xsltc.fcompile"
+ description="Compile just the XSLTC classes w/o JLex, JCUP" >
+ <echo message="Compiling remaining XSLTC classes"/>
+ <javac srcdir="${src.dir}"
+ destdir="${build.classes}"
+ includes="${xsltc.reldir}/**/*.java"
+ debug="${build.debug}">
+ <classpath refid="xsltc.class.path" />
+ <bootclasspath refid="xslt.boot.class.path" />
+ </javac>
+ </target>
+
+ <target name="xsltc.unbundledjar" depends="xsltc.compile"
description="Jar just the xsltc.jar file" >
<!-- Copy over the manifest, with filtering (for version number) -->
<filter token="impl.version" value="${impl.version}"/>
@@ -374,7 +386,7 @@
</target>
- <target name="xsltc.bundledjar" depends="xsltc.compile"
+ <target name="xsltc.jar" depends="xsltc.compile"
description="Jar xsltc, BCEL,JLex,java_cup,runtime and jakarta regexp">
<!-- make a tmp directory to work in -->
<delete dir="${build.dir}/xsltctmp" includeEmptyDirs="true" quiet="true"/>
@@ -449,11 +461,14 @@
includeEmptyDirs="true" quiet="true"/>
<!-- create new META-INF dir w/ transformer factory default -->
+ <!-- GTM: comment this out so that bundled xsltc.jar does not have
+ service provider default until further notice 2/20/2002
<mkdir dir="${build.dir}/xsltctmp/META-INF"/>
<mkdir dir="${build.dir}/xsltctmp/META-INF/services"/>
<copy todir="${build.dir}/xsltctmp/META-INF/services"
file="${src.dir}/${xsltc.reldir}/javax.xml.transform.TransformerFactory"
/>
+ -->
<!-- Copy over the manifest, with filtering (for version number) -->
<filter token="impl.version" value="${impl.version}"/>
diff --git a/src/org/apache/xalan/serialize/SerializerToXML.java b/src/org/apache/xalan/serialize/SerializerToXML.java
index f9705d8..5d38068 100644
--- a/src/org/apache/xalan/serialize/SerializerToXML.java
+++ b/src/org/apache/xalan/serialize/SerializerToXML.java
@@ -323,6 +323,9 @@
/** Indicate whether running in Debug mode */
private static final boolean DEBUG = false;
+ /** This flag is set while receiving events from the external DTD */
+ private boolean m_inExternalDTD = false;
+
/**
* Default constructor.
*/
@@ -1864,6 +1867,8 @@
*/
public void startEntity(String name) throws org.xml.sax.SAXException
{
+ if (name.equals("[dtd]"))
+ m_inExternalDTD = true;
m_inEntityRef = true;
}
@@ -1876,7 +1881,9 @@
*/
public void endEntity(String name) throws org.xml.sax.SAXException
{
- m_inEntityRef = false;
+ if (name.equals("[dtd]"))
+ m_inExternalDTD = false;
+ m_inEntityRef = false;
}
/**
@@ -1925,7 +1932,10 @@
*/
public void elementDecl(String name, String model) throws SAXException
{
- try
+ // Do not inline external DTD
+ if (m_inExternalDTD) return;
+
+ try
{
final Writer writer = m_writer;
if (m_inDoctype)
@@ -1975,6 +1985,8 @@
String eName, String aName, String type, String valueDefault, String value)
throws SAXException
{
+ // Do not inline external DTD
+ if (m_inExternalDTD) return;
try
{
@@ -1987,26 +1999,19 @@
m_inDoctype = false;
}
- if (!eName.equals(m_elemName))
- {
- writer.write("<!ATTLIST ");
- writer.write(eName);
- writer.write(" ");
+ writer.write("<!ATTLIST ");
+ writer.write(eName);
+ writer.write(" ");
- m_elemName = eName;
- }
- else
- {
- m_pos -= 3;
-
- writer.write(m_lineSep, 0, m_lineSepLen);
- }
writer.write(aName);
writer.write(" ");
writer.write(type);
- writer.write(" ");
- writer.write(valueDefault);
+ if (valueDefault != null)
+ {
+ writer.write(" ");
+ writer.write(valueDefault);
+ }
//m_writer.write(" ");
//m_writer.write(value);
@@ -2035,6 +2040,8 @@
public void internalEntityDecl(String name, String value)
throws SAXException
{
+ // Do not inline external DTD
+ if (m_inExternalDTD) return;
try
{
diff --git a/src/org/apache/xalan/xsltc/DOM.java b/src/org/apache/xalan/xsltc/DOM.java
index 040e74e..97e1d47 100644
--- a/src/org/apache/xalan/xsltc/DOM.java
+++ b/src/org/apache/xalan/xsltc/DOM.java
@@ -132,4 +132,6 @@
public void setupMapping(String[] names, String[] namespaces);
public boolean isElement(final int node);
public boolean isAttribute(final int node);
+ public String lookupNamespace(int node, String prefix)
+ throws TransletException;
}
diff --git a/src/org/apache/xalan/xsltc/compiler/ApplyTemplates.java b/src/org/apache/xalan/xsltc/compiler/ApplyTemplates.java
index b0143cb..985f683 100644
--- a/src/org/apache/xalan/xsltc/compiler/ApplyTemplates.java
+++ b/src/org/apache/xalan/xsltc/compiler/ApplyTemplates.java
@@ -139,6 +139,7 @@
* some template in the stylesheet uses parameters.
*/
public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
+ boolean setStartNodeCalled = false;
final Stylesheet stylesheet = classGen.getStylesheet();
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
@@ -165,6 +166,7 @@
translateContents(classGen, methodGen);
}
+
il.append(classGen.loadTranslet());
// The 'select' expression is a result-tree
@@ -192,6 +194,7 @@
NODE_ITERATOR_SIG);
il.append(methodGen.loadCurrentNode());
il.append(new INVOKEINTERFACE(setStartNode,2));
+ setStartNodeCalled = true;
}
else {
if (_select == null)
@@ -201,7 +204,7 @@
}
}
- if (_select != null) {
+ if (_select != null && !setStartNodeCalled) {
_select.startResetIterator(classGen, methodGen);
}
diff --git a/src/org/apache/xalan/xsltc/compiler/AttributeValue.java b/src/org/apache/xalan/xsltc/compiler/AttributeValue.java
index 64c0340..8f33670 100644
--- a/src/org/apache/xalan/xsltc/compiler/AttributeValue.java
+++ b/src/org/apache/xalan/xsltc/compiler/AttributeValue.java
@@ -71,16 +71,16 @@
AttributeValue result;
if (text.indexOf('{') != -1) {
- result = new AttributeValueTemplate(text, parser);
+ result = new AttributeValueTemplate(text, parser, parent);
}
else if (text.indexOf('}') != -1) {
- result = new AttributeValueTemplate(text, parser);
+ result = new AttributeValueTemplate(text, parser, parent);
}
else {
result = new SimpleAttributeValue(text);
result.setParser(parser);
+ result.setParent(parent);
}
- result.setParent(parent);
return result;
}
}
diff --git a/src/org/apache/xalan/xsltc/compiler/AttributeValueTemplate.java b/src/org/apache/xalan/xsltc/compiler/AttributeValueTemplate.java
index 49f2e20..00d981d 100644
--- a/src/org/apache/xalan/xsltc/compiler/AttributeValueTemplate.java
+++ b/src/org/apache/xalan/xsltc/compiler/AttributeValueTemplate.java
@@ -72,10 +72,14 @@
final class AttributeValueTemplate extends AttributeValue {
- public AttributeValueTemplate(String value, Parser parser) {
+ public AttributeValueTemplate(String value, Parser parser,
+ SyntaxTreeNode parent)
+ {
+ setParent(parent);
setParser(parser);
- if (check(value, parser))
+ if (check(value, parser)) {
parseAVTemplate(0, value, parser);
+ }
}
private void parseAVTemplate(final int start, String text, Parser parser) {
diff --git a/src/org/apache/xalan/xsltc/compiler/CastExpr.java b/src/org/apache/xalan/xsltc/compiler/CastExpr.java
index cec15f5..e028905 100644
--- a/src/org/apache/xalan/xsltc/compiler/CastExpr.java
+++ b/src/org/apache/xalan/xsltc/compiler/CastExpr.java
@@ -140,6 +140,7 @@
InternalTypeMap.put(Type.Reference, Type.String);
InternalTypeMap.put(Type.Reference, Type.Node);
InternalTypeMap.put(Type.Reference, Type.NodeSet);
+ InternalTypeMap.put(Type.Reference, Type.ResultTree);
InternalTypeMap.put(Type.Void, Type.String);
}
diff --git a/src/org/apache/xalan/xsltc/compiler/Constants.java b/src/org/apache/xalan/xsltc/compiler/Constants.java
index 6772185..4e6acc7 100644
--- a/src/org/apache/xalan/xsltc/compiler/Constants.java
+++ b/src/org/apache/xalan/xsltc/compiler/Constants.java
@@ -486,6 +486,10 @@
public static final int POSITION_INDEX = 2;
public static final int LAST_INDEX = 3;
+ public static final String XMLNS_PREFIX = "xmlns";
+ public static final String XMLNS_STRING = "xmlns:";
+ public static final String XMLNS_URI
+ = "http://www.w3.org/2000/xmlns/";
public static final String XSLT_URI
= "http://www.w3.org/1999/XSL/Transform";
public static final String XHTML_URI
diff --git a/src/org/apache/xalan/xsltc/compiler/DocumentCall.java b/src/org/apache/xalan/xsltc/compiler/DocumentCall.java
index 0817a06..8ff7f49 100644
--- a/src/org/apache/xalan/xsltc/compiler/DocumentCall.java
+++ b/src/org/apache/xalan/xsltc/compiler/DocumentCall.java
@@ -120,7 +120,15 @@
// Parse the second argument - the document URI base
if (ac == 2) {
_base = argument(1);
- if (!_base.typeCheck(stable).identicalTo(Type.NodeSet)) {
+ final Type baseType = _base.typeCheck(stable);
+
+ if (baseType.identicalTo(Type.Node)) {
+ _base = new CastExpr(_base, Type.NodeSet);
+ }
+ else if (baseType.identicalTo(Type.NodeSet)) {
+ // falls through
+ }
+ else {
ErrorMsg msg = new ErrorMsg(ErrorMsg.DOCUMENT_ARG_ERR, this);
throw new TypeCheckError(msg);
}
diff --git a/src/org/apache/xalan/xsltc/compiler/ElementAvailableCall.java b/src/org/apache/xalan/xsltc/compiler/ElementAvailableCall.java
index 97e3f3c..cc14b58 100644
--- a/src/org/apache/xalan/xsltc/compiler/ElementAvailableCall.java
+++ b/src/org/apache/xalan/xsltc/compiler/ElementAvailableCall.java
@@ -91,10 +91,11 @@
* Returns the result that this function will return
*/
public boolean getResult() {
- final Parser parser = getParser();
- final LiteralExpr arg = (LiteralExpr)argument();
- final QName qname = parser.getQName(arg.getValue());
- return(parser.elementSupported(qname));
+ final LiteralExpr arg = (LiteralExpr) argument();
+ final String qname = arg.getValue();
+ final int index = qname.indexOf(':');
+ final String localName = (index > 0) ? qname.substring(index + 1) : qname;
+ return getParser().elementSupported(arg.getNamespace(), localName);
}
/**
diff --git a/src/org/apache/xalan/xsltc/compiler/Expression.java b/src/org/apache/xalan/xsltc/compiler/Expression.java
index 3da80c6..727dc3b 100644
--- a/src/org/apache/xalan/xsltc/compiler/Expression.java
+++ b/src/org/apache/xalan/xsltc/compiler/Expression.java
@@ -103,7 +103,7 @@
public abstract String toString();
public boolean hasPositionCall() {
- return true;
+ return false; // default should be 'false' for StepPattern
}
public boolean hasLastCall() {
diff --git a/src/org/apache/xalan/xsltc/compiler/FormatNumberCall.java b/src/org/apache/xalan/xsltc/compiler/FormatNumberCall.java
index 9558f1a..124576d 100644
--- a/src/org/apache/xalan/xsltc/compiler/FormatNumberCall.java
+++ b/src/org/apache/xalan/xsltc/compiler/FormatNumberCall.java
@@ -97,7 +97,7 @@
_format = new CastExpr(_format, Type.String);
}
if (argumentCount() == 3) {
- final Type tname = _format.typeCheck(stable);
+ final Type tname = _name.typeCheck(stable);
if (tname instanceof StringType == false) {
_name = new CastExpr(_name, Type.String);
}
diff --git a/src/org/apache/xalan/xsltc/compiler/FunctionAvailableCall.java b/src/org/apache/xalan/xsltc/compiler/FunctionAvailableCall.java
index 1e6aafc..1987549 100644
--- a/src/org/apache/xalan/xsltc/compiler/FunctionAvailableCall.java
+++ b/src/org/apache/xalan/xsltc/compiler/FunctionAvailableCall.java
@@ -56,7 +56,7 @@
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
- * @author Jacek Ambroziak
+ * @author G. Todd Miller
* @author Santiago Pericas-Geertsen
*
*/
@@ -64,38 +64,188 @@
package org.apache.xalan.xsltc.compiler;
import java.util.Vector;
-import java.util.HashSet;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Method;
import org.apache.xalan.xsltc.compiler.util.Type;
import org.apache.bcel.generic.*;
import org.apache.xalan.xsltc.compiler.util.*;
+import org.apache.xalan.xsltc.runtime.TransletLoader;
final class FunctionAvailableCall extends FunctionCall {
+ private boolean _isFunctionAvailable = false;
+ private Expression _arg;
+ private String _namespaceOfFunct =null;
+ private String _nameOfFunct =null;
+
+
+ /**
+ * Constructs a FunctionAvailableCall FunctionCall. Takes the
+ * function name qname, for example, 'function-available', and a list
+ * of arguments where the arguments must be instances of
+ * LiteralExpression. The test for availability considers
+ * internal xsl functions such as 'floor' as well as external
+ * Java functions, such as 'java.lang.Math.sin'. The case of
+ * external functions is handled here, the case of internal
+ * functions is handled in getResult.
+ */
public FunctionAvailableCall(QName fname, Vector arguments) {
super(fname, arguments);
+ _arg = (Expression)arguments.elementAt(0);
+ _type = null;
+ if (_arg instanceof LiteralExpr) {
+ LiteralExpr arg = (LiteralExpr)_arg;
+ _namespaceOfFunct = arg.getNamespace();
+ _nameOfFunct = arg.getValue();
+ if ((_namespaceOfFunct != null) &&
+ (!_namespaceOfFunct.equals(Constants.EMPTYSTRING)))
+ {
+ // the function is external, such as a java function
+ _isFunctionAvailable = hasMethods();
+ }
+ // the case of internal function is handled in getResult.
+ }
+ // case where _arg is not instanceof LiteralExpr can not be handled.
}
/**
- * Force the argument to this function to be a literal string.
+ * Argument of function-available call must be literal, typecheck
+ * returns the type of function-available to be boolean.
*/
public Type typeCheck(SymbolTable stable) throws TypeCheckError {
- if (argument() instanceof LiteralExpr) {
- return _type = Type.Boolean;
+ // may be already set
+ if ( _type != null ) {
+ return _type;
+ }
+ if (_arg instanceof LiteralExpr) {
+ _type = Type.Boolean;
+ return Type.Boolean;
}
ErrorMsg err = new ErrorMsg(ErrorMsg.NEED_LITERAL_ERR,
- "function-available", this);
+ "function-available", this);
throw new TypeCheckError(err);
}
/**
- * Returns the result that this function will return
+ * (For ext. java functions only)
+ * Parses the argument to function-available to extract the package
+ * qualified class name, for example, given the argument
+ * 'java:java.lang.Math.sin', getClassName would return
+ * 'java.lang.Math'. See also 'getMethodName'.
+ */
+ private String getClassName(String argValue){
+ int colonSep = argValue.indexOf(":");
+ if (colonSep != -1) {
+ argValue = argValue.substring(colonSep+1);
+ }
+ int lastDot = argValue.lastIndexOf(".");
+ if (lastDot != -1) {
+ argValue = argValue.substring(0, lastDot);
+ }
+ return argValue;
+ }
+
+ /**
+ * (For ext. java functions only)
+ * Parses the argument to function-available
+ * to extract the method name, for example, given the argument
+ * 'java.lang.Math.sin', getMethodName would return 'sin'.
+ */
+ private String getMethodName(String argValue){
+ int lastDot = argValue.lastIndexOf(".");
+ if (lastDot != -1) {
+ argValue = argValue.substring(lastDot+1);
+ }
+ return argValue;
+ }
+
+ /**
+ * (For java external functions only)
+ * Creates a full package qualified
+ * function name taking into account the namespace and the
+ * function name derived from the argument passed to function-available.
+ * For example, given a name of 'java:java.lang.Math.sin' and a
+ * namespace of 'http://xml.apache.org/xalan/xsltc/java' this routine
+ * constructs a uri and then derives the class name
+ * 'java.lang.Math.sin' from the uri. The uri in this example would
+ * be 'http://xml.apache.org/xalan/xsltc/java.java.lang.Math.sin'
+ */
+ private String getExternalFunctionName() {
+ int colonIndex = _nameOfFunct.indexOf(":");
+ String uri = _namespaceOfFunct +
+ "." + _nameOfFunct.substring(colonIndex+1);
+ try{
+ return getClassNameFromUri(uri);
+ } catch (TypeCheckError e) {
+ return null;
+ }
+ }
+
+ /**
+ * for external java functions only: reports on whether or not
+ * the specified method is found in the specifed class.
+ */
+ private boolean hasMethods() {
+
+ LiteralExpr arg = (LiteralExpr)_arg;
+ final String externalFunctName = getExternalFunctionName();
+ if (externalFunctName == null) {
+ return false;
+ }
+ final String className = getClassName(externalFunctName);
+
+ if (_namespaceOfFunct.startsWith(JAVA_EXT_XSLTC) ||
+ _namespaceOfFunct.startsWith(JAVA_EXT_XALAN)) {
+ try {
+ TransletLoader loader = new TransletLoader();
+ final Class clazz = loader.loadClass(className);
+
+ if (clazz == null) {
+ final ErrorMsg msg =
+ new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, className);
+ getParser().reportError(Constants.ERROR, msg);
+ }
+ else {
+ final String methodName = getMethodName(externalFunctName);
+ final Method[] methods = clazz.getDeclaredMethods();
+
+ for (int i = 0; i < methods.length; i++) {
+ final int mods = methods[i].getModifiers();
+
+ if (Modifier.isPublic(mods)
+ && Modifier.isStatic(mods)
+ && methods[i].getName().equals(methodName))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ catch (ClassNotFoundException e) {
+ final ErrorMsg msg =
+ new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, className);
+ getParser().reportError(Constants.ERROR, msg);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * reports on whether the function specified in the argument to
+ * xslt function 'function-available' was found.
*/
public boolean getResult() {
- final Parser parser = getParser();
- final LiteralExpr arg = (LiteralExpr)argument();
- return(parser.functionSupported(arg.getValue()));
+ if ((_namespaceOfFunct == null) ||
+ (_namespaceOfFunct.equals(Constants.EMPTYSTRING)))
+ {
+ // no namespace, so the function is an internal xslt function.
+ final Parser parser = getParser();
+ _isFunctionAvailable = parser.functionSupported(_nameOfFunct);
+ }
+ return _isFunctionAvailable;
}
+
/**
* Calls to 'function-available' are resolved at compile time since
* the namespaces declared in the stylsheet are not available at run
@@ -103,7 +253,7 @@
*/
public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
final ConstantPoolGen cpg = classGen.getConstantPool();
- final boolean result = getResult();
- methodGen.getInstructionList().append(new PUSH(cpg, result));
+ methodGen.getInstructionList().append(new PUSH(cpg, getResult()));
}
+
}
diff --git a/src/org/apache/xalan/xsltc/compiler/FunctionCall.java b/src/org/apache/xalan/xsltc/compiler/FunctionCall.java
index 4bf5be5..d609cd8 100644
--- a/src/org/apache/xalan/xsltc/compiler/FunctionCall.java
+++ b/src/org/apache/xalan/xsltc/compiler/FunctionCall.java
@@ -86,10 +86,19 @@
private final static Vector EMPTY_ARG_LIST = new Vector(0);
// Valid namespaces for Java function-call extension
- private final static String JAVA_EXT_PREFIX = TRANSLET_URI + "/java";
- private final static String JAVA_EXT_XALAN =
+ protected final static String EXT_XSLTC =
+ TRANSLET_URI;
+
+ protected final static String JAVA_EXT_XSLTC =
+ EXT_XSLTC + "/java";
+
+ protected final static String EXT_XALAN =
+ "http://xml.apache.org/xalan";
+
+ protected final static String JAVA_EXT_XALAN =
"http://xml.apache.org/xslt/java";
+
// External Java function's class/method/signature
private String _className;
private Method _chosenMethod;
@@ -170,6 +179,7 @@
public FunctionCall(QName fname, Vector arguments) {
_fname = fname;
_arguments = arguments;
+ _type = null;
}
public FunctionCall(QName fname) {
@@ -192,48 +202,71 @@
}
}
+ public String getClassNameFromUri(String uri)
+ throws TypeCheckError
+ {
+ final int length =
+ uri.startsWith(JAVA_EXT_XSLTC) ? JAVA_EXT_XSLTC.length() + 1 :
+ uri.startsWith(JAVA_EXT_XALAN) ? JAVA_EXT_XALAN.length() + 1 : 0;
+
+ if (length == 0) {
+ throw new TypeCheckError(this);
+ }
+ return (uri.length() > length) ? uri.substring(length) : EMPTYSTRING;
+ }
+
/**
* Type check a function call. Since different type conversions apply,
* type checking is different for standard and external (Java) functions.
*/
- public Type typeCheck(SymbolTable stable) throws TypeCheckError {
+ public Type typeCheck(SymbolTable stable)
+ throws TypeCheckError
+ {
+ if (_type != null) return _type;
final String namespace = _fname.getNamespace();
final String local = _fname.getLocalPart();
- // XPath functions have no namespace
- if (isStandard()) {
+ if (isExtension()) {
+ _fname = new QName(null, null, local);
+ return typeCheckStandard(stable);
+ }
+ else if (isStandard()) {
return typeCheckStandard(stable);
}
// Handle extension functions (they all have a namespace)
else {
- final int len = JAVA_EXT_PREFIX.length();
- if (namespace.equals(JAVA_EXT_PREFIX) ||
- namespace.equals(JAVA_EXT_XALAN)) {
- final int pos = local.indexOf('.');
- _className = local.substring(0, pos);
- _fname = new QName(namespace, null, local.substring(pos+1));
- }
- else if (namespace.length() >= len &&
- namespace.substring(0, len).equals(JAVA_EXT_PREFIX)) {
- _className = namespace.substring(len + 1);
- }
- else {
- /*
- * Warn user if external function could not be resolved.
- * Warning will _NOT_ be issued is the call is properly
- * wrapped in an <xsl:if> or <xsl:when> element. For details
- * see If.parserContents() and When.parserContents()
- */
- final Parser parser = getParser();
- if (parser != null) {
- reportWarning(this, parser, ErrorMsg.FUNCTION_RESOLVE_ERR,
- _fname.toString());
+ try {
+ _className = getClassNameFromUri(namespace);
+
+ final int pos = local.lastIndexOf('.');
+ if (pos > 0) {
+ _className = _className + local.substring(0, pos);
+ _fname = new QName(namespace, null, local.substring(pos + 1));
}
- unresolvedExternal = true;
- return _type = Type.Void;
+ else {
+ _fname = new QName(namespace, null, local);
+ }
+ if (_className.length() > 0) {
+ return typeCheckExternal(stable);
+ }
+ } catch (TypeCheckError e) {
+ // Falls through
}
- return typeCheckExternal(stable);
+
+ /*
+ * Warn user if external function could not be resolved.
+ * Warning will _NOT_ be issued is the call is properly
+ * wrapped in an <xsl:if> or <xsl:when> element. For details
+ * see If.parserContents() and When.parserContents()
+ */
+ final Parser parser = getParser();
+ if (parser != null) {
+ reportWarning(this, parser, ErrorMsg.FUNCTION_RESOLVE_ERR,
+ _fname.toString());
+ }
+ unresolvedExternal = true;
+ return _type = Type.Void;
}
}
@@ -243,8 +276,7 @@
* thrown, then catch it and re-throw it with a new "this".
*/
public Type typeCheckStandard(SymbolTable stable) throws TypeCheckError {
-
- _fname.clearNamespace(); // HACK!!!
+ _fname.clearNamespace(); // HACK!!!
final int n = _arguments.size();
final Vector argsType = typeCheckArgs(stable);
@@ -367,8 +399,8 @@
* Update true/false-lists.
*/
public void translateDesynthesized(ClassGenerator classGen,
- MethodGenerator methodGen) {
-
+ MethodGenerator methodGen)
+ {
Type type = Type.Boolean;
if (_chosenMethodType != null)
type = _chosenMethodType.resultType();
@@ -393,7 +425,7 @@
int index;
// Translate calls to methods in the BasisLibrary
- if (isStandard()) {
+ if (isStandard() || isExtension()) {
for (int i = 0; i < n; i++) {
final Expression exp = argument(i);
exp.translate(classGen, methodGen);
@@ -470,10 +502,13 @@
public boolean isStandard() {
final String namespace = _fname.getNamespace();
- if ((namespace == null) || (namespace.equals(Constants.EMPTYSTRING)))
- return true;
- else
- return false;
+ return (namespace == null) || (namespace.equals(Constants.EMPTYSTRING));
+ }
+
+ public boolean isExtension() {
+ final String namespace = _fname.getNamespace();
+ return (namespace != null) && (namespace.equals(EXT_XSLTC)
+ || namespace.equals(EXT_XALAN));
}
/**
@@ -485,7 +520,7 @@
Vector result = null;
final String namespace = _fname.getNamespace();
- if (namespace.startsWith(JAVA_EXT_PREFIX) ||
+ if (namespace.startsWith(JAVA_EXT_XSLTC) ||
namespace.startsWith(JAVA_EXT_XALAN)) {
final int nArgs = _arguments.size();
try {
diff --git a/src/org/apache/xalan/xsltc/compiler/Include.java b/src/org/apache/xalan/xsltc/compiler/Include.java
index 9f50727..1719cc8 100644
--- a/src/org/apache/xalan/xsltc/compiler/Include.java
+++ b/src/org/apache/xalan/xsltc/compiler/Include.java
@@ -65,6 +65,7 @@
package org.apache.xalan.xsltc.compiler;
import java.io.File;
+import java.io.FileNotFoundException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Enumeration;
@@ -88,8 +89,9 @@
public void parseContents(final Parser parser) {
final Stylesheet context = parser.getCurrentStylesheet();
+ String docToLoad = getAttribute("href");
+
try {
- String docToLoad = getAttribute("href");
if (context.checkForLoop(docToLoad)) {
final int errno = ErrorMsg.CIRCULAR_INCLUDE_ERR;
final ErrorMsg msg = new ErrorMsg(errno, docToLoad, this);
@@ -106,11 +108,28 @@
input = loader.loadSource(docToLoad, currLoadedDoc, xsltc);
}
else {
- File file = new File(currLoadedDoc);
- if (file.exists()) currLoadedDoc = "file:"+currLoadedDoc;
- final URL url = new URL(new URL(currLoadedDoc), docToLoad);
- docToLoad = url.toString();
- input = new InputSource(docToLoad);
+ // bug 7835, patch by Stefan Kost (s.kost@webmacher.de)
+ if ((currLoadedDoc != null) && (currLoadedDoc.length() > 0)) {
+ File file = new File(currLoadedDoc);
+ if (file.exists()) {
+ currLoadedDoc = "file:" + file.getCanonicalPath();
+ }
+ final URL url = new URL(new URL(currLoadedDoc), docToLoad);
+ docToLoad = url.toString();
+ input = new InputSource(docToLoad);
+ }
+ else {
+ File file = new File(System.getProperty("user.dir"),
+ docToLoad);
+ if (file.exists()) {
+ docToLoad = "file:" + file.getCanonicalPath();
+ }
+ else {
+ throw new FileNotFoundException(
+ "Could not load file " + docToLoad);
+ }
+ input = new InputSource(docToLoad);
+ }
}
final SyntaxTreeNode root = parser.parse(input);
@@ -145,6 +164,22 @@
}
}
}
+ catch (FileNotFoundException e) {
+ // Update systemId in parent stylesheet for error reporting
+ context.setSystemId(getAttribute("href"));
+
+ final ErrorMsg msg =
+ new ErrorMsg(ErrorMsg.FILE_NOT_FOUND_ERR, docToLoad, this);
+ parser.reportError(Constants.FATAL, msg);
+ }
+ catch (MalformedURLException e) {
+ // Update systemId in parent stylesheet for error reporting
+ context.setSystemId(getAttribute("href"));
+
+ final ErrorMsg msg =
+ new ErrorMsg(ErrorMsg.FILE_NOT_FOUND_ERR, docToLoad, this);
+ parser.reportError(Constants.FATAL, msg);
+ }
catch (Exception e) {
e.printStackTrace();
}
diff --git a/src/org/apache/xalan/xsltc/compiler/LiteralElement.java b/src/org/apache/xalan/xsltc/compiler/LiteralElement.java
index fffa79b..0308584 100644
--- a/src/org/apache/xalan/xsltc/compiler/LiteralElement.java
+++ b/src/org/apache/xalan/xsltc/compiler/LiteralElement.java
@@ -64,11 +64,13 @@
package org.apache.xalan.xsltc.compiler;
-import java.util.Hashtable;
-import java.util.Enumeration;
import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Enumeration;
import javax.xml.parsers.*;
+import javax.xml.transform.OutputKeys;
import org.xml.sax.*;
@@ -77,10 +79,11 @@
import org.apache.xalan.xsltc.compiler.util.*;
final class LiteralElement extends Instruction {
+
private String _name;
- private Hashtable _accessedPrefixes = null;
- private LiteralElement _parent;
+ private LiteralElement _literalElemParent;
private Vector _attributeElements = null;
+ private Hashtable _accessedPrefixes = null;
private final static String XMLNS_STRING = "xmlns";
@@ -119,10 +122,10 @@
SymbolTable stable, boolean declared) {
// Check if the parent has a declaration for this namespace
- if (_parent != null) {
- final String parentUri = _parent.accessedNamespace(prefix);
+ if (_literalElemParent != null) {
+ final String parentUri = _literalElemParent.accessedNamespace(prefix);
if (parentUri == null) {
- _parent.registerNamespace(prefix, uri, stable, declared);
+ _literalElemParent.registerNamespace(prefix, uri, stable, declared);
return;
}
if (parentUri.equals(uri)) return;
@@ -139,7 +142,7 @@
if (old != null) {
if (old.equals(uri))
return;
- else
+ else
prefix = stable.generateNamespacePrefix();
}
}
@@ -207,7 +210,6 @@
_attributeElements.insertElementAt(attribute,0);
}
-
/**
* Type-check the contents of this element. The element itself does not
* need any type checking as it leaves nothign on the JVM's stack.
@@ -255,23 +257,36 @@
* Registers all namespaces that are used by the element/attributes
*/
public void parseContents(Parser parser) {
-
final SymbolTable stable = parser.getSymbolTable();
stable.setCurrentNode(this);
// Find the closest literal element ancestor (if there is one)
- SyntaxTreeNode _parent = getParent();
- while ((_parent != null) && !(_parent instanceof LiteralElement))
- _parent = _parent.getParent();
- if (!(_parent instanceof LiteralElement))
- _parent = null;
+ SyntaxTreeNode _literalElemParent = getParent();
+ while (_literalElemParent != null && !(_literalElemParent instanceof LiteralElement)) {
+ _literalElemParent = _literalElemParent.getParent();
+ }
+
+ if (!(_literalElemParent instanceof LiteralElement)) {
+ _literalElemParent = null;
+ }
_name = translateQName(_qname, stable);
+ // Determine output type if first literal output is html
+ if (_name.toString().equalsIgnoreCase("html")) {
+ final SyntaxTreeNode parent = getParent();
+ if (parent instanceof Template) {
+ final Template tt = (Template) parent;
+ if (tt.isRootTemplate()) {
+ final Stylesheet stylesheet = parser.getCurrentStylesheet();
+ stylesheet.setOutputProperty(OutputKeys.METHOD, "html");
+ }
+ }
+ }
+
// Process all attributes and register all namespaces they use
final int count = _attributes.getLength();
for (int i = 0; i < count; i++) {
-
final QName qname = parser.getQName(_attributes.getQName(i));
final String uri = qname.getNamespace();
final String val = _attributes.getValue(i);
@@ -290,12 +305,15 @@
else if (qname == parser.getExcludeResultPrefixes()) {
stable.excludeNamespaces(val);
}
- // Ignore all other attributes in XSL namespace
- else if ((uri != null) && (uri.equals(XSLT_URI))) {
-
- }
- // Handle literal attributes (attributes not in XSL namespace)
else {
+ // Ignore special attributes
+ final String prefix = qname.getPrefix();
+ if (uri != null && uri.equals(XSLT_URI) ||
+ prefix != null && prefix.equals(XMLNS_STRING)) {
+ continue;
+ }
+
+ // Handle all other literal attributes
final String name = translateQName(qname, stable);
LiteralAttribute attr = new LiteralAttribute(name, val, parser);
addAttribute(attr);
@@ -311,9 +329,8 @@
final String prefix = (String)include.nextElement();
if (!prefix.equals("xml")) {
final String uri = lookupNamespace(prefix);
- if ((uri != null) && (!uri.equals(XSLT_URI))) {
- if (!stable.isExcludedNamespace(uri))
- registerNamespace(prefix,uri,stable,true);
+ if (uri != null && !stable.isExcludedNamespace(uri)) {
+ registerNamespace(prefix, uri, stable, true);
}
}
}
@@ -322,7 +339,6 @@
// Process all attributes and register all namespaces they use
for (int i = 0; i < count; i++) {
-
final QName qname = parser.getQName(_attributes.getQName(i));
final String val = _attributes.getValue(i);
@@ -344,7 +360,9 @@
/**
* Compiles code that emits the literal element to the output handler,
* first the start tag, then namespace declaration, then attributes,
- * then the element contents, and then the element end tag.
+ * then the element contents, and then the element end tag. Since the
+ * value of an attribute may depend on a variable, variables must be
+ * compiled first.
*/
public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
@@ -354,23 +372,52 @@
// Compile code to emit element start tag
il.append(methodGen.loadHandler());
il.append(new PUSH(cpg, _name));
- il.append(DUP2); // duplicate these 2 args for endElement
+ il.append(DUP2); // duplicate these 2 args for endElement
il.append(methodGen.startElement());
+ // The value of an attribute may depend on a (sibling) variable
+ for (int i = 0; i < elementCount(); i++) {
+ final SyntaxTreeNode item = (SyntaxTreeNode) elementAt(i);
+ if (item instanceof Variable) {
+ item.translate(classGen, methodGen);
+ removeElement(item); // avoid translating it twice
+ }
+ }
+
// Compile code to emit namespace attributes
if (_accessedPrefixes != null) {
+ boolean declaresDefaultNS = false;
Enumeration e = _accessedPrefixes.keys();
+
while (e.hasMoreElements()) {
final String prefix = (String)e.nextElement();
final String uri = (String)_accessedPrefixes.get(prefix);
- if ((uri != Constants.EMPTYSTRING) ||
- (prefix != Constants.EMPTYSTRING)) {
+
+ if (uri != Constants.EMPTYSTRING ||
+ prefix != Constants.EMPTYSTRING)
+ {
+ if (prefix == Constants.EMPTYSTRING) {
+ declaresDefaultNS = true;
+ }
il.append(methodGen.loadHandler());
il.append(new PUSH(cpg,prefix));
il.append(new PUSH(cpg,uri));
il.append(methodGen.namespace());
}
}
+
+ /*
+ * If our XslElement parent redeclares the default NS, and this
+ * element doesn't, it must be redeclared one more time.
+ */
+ if (!declaresDefaultNS && (_parent instanceof XslElement)
+ && ((XslElement) _parent).declaresDefaultNS())
+ {
+ il.append(methodGen.loadHandler());
+ il.append(new PUSH(cpg, Constants.EMPTYSTRING));
+ il.append(new PUSH(cpg, Constants.EMPTYSTRING));
+ il.append(methodGen.namespace());
+ }
}
// Output all attributes
diff --git a/src/org/apache/xalan/xsltc/compiler/LiteralExpr.java b/src/org/apache/xalan/xsltc/compiler/LiteralExpr.java
index 93623a9..b169eff 100644
--- a/src/org/apache/xalan/xsltc/compiler/LiteralExpr.java
+++ b/src/org/apache/xalan/xsltc/compiler/LiteralExpr.java
@@ -88,10 +88,7 @@
*/
public LiteralExpr(String value, String namespace) {
_value = value;
- if (namespace.equals(Constants.EMPTYSTRING))
- _namespace = null;
- else
- _namespace = namespace;
+ _namespace = namespace.equals(Constants.EMPTYSTRING) ? null : namespace;
}
public Type typeCheck(SymbolTable stable) throws TypeCheckError {
diff --git a/src/org/apache/xalan/xsltc/compiler/Number.java b/src/org/apache/xalan/xsltc/compiler/Number.java
index af99fe4..5435f27 100644
--- a/src/org/apache/xalan/xsltc/compiler/Number.java
+++ b/src/org/apache/xalan/xsltc/compiler/Number.java
@@ -129,23 +129,23 @@
}
}
else if (name.equals("format")) {
- _format = new AttributeValueTemplate(value, parser);
+ _format = new AttributeValueTemplate(value, parser, this);
_formatNeeded = true;
}
else if (name.equals("lang")) {
- _lang = new AttributeValueTemplate(value, parser);
+ _lang = new AttributeValueTemplate(value, parser, this);
_formatNeeded = true;
}
else if (name.equals("letter-value")) {
- _letterValue = new AttributeValueTemplate(value, parser);
+ _letterValue = new AttributeValueTemplate(value, parser, this);
_formatNeeded = true;
}
else if (name.equals("grouping-separator")) {
- _groupingSeparator = new AttributeValueTemplate(value, parser);
+ _groupingSeparator = new AttributeValueTemplate(value, parser, this);
_formatNeeded = true;
}
else if (name.equals("grouping-size")) {
- _groupingSize = new AttributeValueTemplate(value, parser);
+ _groupingSize = new AttributeValueTemplate(value, parser, this);
_formatNeeded = true;
}
}
diff --git a/src/org/apache/xalan/xsltc/compiler/Output.java b/src/org/apache/xalan/xsltc/compiler/Output.java
index 907b793..7127f28 100644
--- a/src/org/apache/xalan/xsltc/compiler/Output.java
+++ b/src/org/apache/xalan/xsltc/compiler/Output.java
@@ -65,9 +65,11 @@
package org.apache.xalan.xsltc.compiler;
import java.util.Vector;
+import java.util.Properties;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.io.OutputStreamWriter;
+import javax.xml.transform.OutputKeys;
import org.apache.bcel.generic.*;
import org.apache.bcel.classfile.JavaClass;
@@ -96,8 +98,9 @@
private boolean _disabled = false;
// Some global constants
- private final static String STRING_SIG = "Ljava/lang/String;";
- private final static String ONE_DOT_ZERO_STRING = "1.0";
+ private final static String STRING_SIG = "Ljava/lang/String;";
+ private final static String XML_VERSION = "1.0";
+ private final static String HTML_VERSION = "4.0";
/**
* Displays the contents of this element (for debugging)
@@ -124,6 +127,7 @@
* Scans the attribute list for the xsl:output instruction
*/
public void parseContents(Parser parser) {
+ final Properties outputProperties = new Properties();
// Ask the parser if it wants this <xsl:output> element
parser.setOutput(this);
@@ -133,25 +137,30 @@
String attrib = null;
- // Get the output XML version - only version "1.0" should be used
+ // Get the output version
_version = getAttribute("version");
- if ((_version == null) || (_version.equals(Constants.EMPTYSTRING))) {
- _version = ONE_DOT_ZERO_STRING;
+ if (_version == null || _version.equals(Constants.EMPTYSTRING)) {
+ _version = null;
}
- if (!_version.equals(ONE_DOT_ZERO_STRING)) {
- ErrorMsg msg = new ErrorMsg(ErrorMsg.OUTPUT_VERSION_ERR, this);
- parser.reportError(Constants.WARNING, msg);
+ else {
+ outputProperties.setProperty(OutputKeys.VERSION, _version);
}
// Get the output method - "xml", "html", "text" or <qname>
_method = getAttribute("method");
- if (_method.equals(Constants.EMPTYSTRING)) _method = null;
- if (_method != null) _method = _method.toLowerCase();
+ if (_method.equals(Constants.EMPTYSTRING)) {
+ _method = null;
+ }
+ if (_method != null) {
+ _method = _method.toLowerCase();
+ outputProperties.setProperty(OutputKeys.METHOD, _method);
+ }
// Get the output encoding - any value accepted here
_encoding = getAttribute("encoding");
- if (_encoding.equals(Constants.EMPTYSTRING))
+ if (_encoding.equals(Constants.EMPTYSTRING)) {
_encoding = null;
+ }
else {
try {
OutputStreamWriter writer =
@@ -162,42 +171,96 @@
_encoding, this);
parser.reportError(Constants.WARNING, msg);
}
+ outputProperties.setProperty(OutputKeys.ENCODING, _encoding);
}
// Should the XML header be omitted - translate to true/false
attrib = getAttribute("omit-xml-declaration");
- if ((attrib != null) && (attrib.equals("yes"))) _omitHeader = true;
+ if (attrib != null && !attrib.equals(Constants.EMPTYSTRING)) {
+ if (attrib.equals("yes")) {
+ _omitHeader = true;
+ }
+ outputProperties.setProperty(OutputKeys.OMIT_XML_DECLARATION, attrib);
+ }
// Add 'standalone' decaration to output - use text as is
_standalone = getAttribute("standalone");
- if (_standalone.equals(Constants.EMPTYSTRING)) _standalone = null;
+ if (_standalone.equals(Constants.EMPTYSTRING)) {
+ _standalone = null;
+ }
+ else {
+ outputProperties.setProperty(OutputKeys.STANDALONE, _standalone);
+ }
// Get system/public identifiers for output DOCTYPE declaration
_doctypeSystem = getAttribute("doctype-system");
- if (_doctypeSystem.equals(Constants.EMPTYSTRING)) _doctypeSystem = null;
+ if (_doctypeSystem.equals(Constants.EMPTYSTRING)) {
+ _doctypeSystem = null;
+ }
+ else {
+ outputProperties.setProperty(OutputKeys.DOCTYPE_SYSTEM, _doctypeSystem);
+ }
+
+
_doctypePublic = getAttribute("doctype-public");
- if (_doctypePublic.equals(Constants.EMPTYSTRING)) _doctypePublic = null;
+ if (_doctypePublic.equals(Constants.EMPTYSTRING)) {
+ _doctypePublic = null;
+ }
+ else {
+ outputProperties.setProperty(OutputKeys.DOCTYPE_PUBLIC, _doctypePublic);
+ }
// Names the elements of whose text contents should be output as CDATA
_cdata = getAttribute("cdata-section-elements");
- if ((_cdata != null) && (_cdata.equals(Constants.EMPTYSTRING)))
+ if (_cdata != null && _cdata.equals(Constants.EMPTYSTRING)) {
_cdata = null;
+ }
+ else {
+ outputProperties.setProperty(OutputKeys.CDATA_SECTION_ELEMENTS, _cdata);
+ }
// Get the indent setting - only has effect for xml and html output
attrib = getAttribute("indent");
- if ((attrib != null) && (!attrib.equals(EMPTYSTRING))) {
- if (attrib.equals("yes")) _indent = true;
+ if (attrib != null && !attrib.equals(EMPTYSTRING)) {
+ if (attrib.equals("yes")) {
+ _indent = true;
+ }
+ outputProperties.setProperty(OutputKeys.INDENT, attrib);
}
- else if ((_method != null) && (_method.equals("html"))) {
+
+ else if (_method != null && _method.equals("html")) {
_indent = true;
}
- // Get the MIME type for the output file - we don't do anythign with it,
- // but our client may use it to specify a data transport type, etc.
+ // Get the MIME type for the output file
_mediaType = getAttribute("media-type");
- if (_mediaType.equals(Constants.EMPTYSTRING)) _mediaType = null;
+ if (_mediaType.equals(Constants.EMPTYSTRING)) {
+ _mediaType = null;
+ }
+ else {
+ outputProperties.setProperty(OutputKeys.MEDIA_TYPE, _mediaType);
+ }
- // parseChildren(parser); - the element is always empty
+ // Implied properties
+ if (_method != null) {
+ if (_method.equals("html")) {
+ if (_version == null) {
+ _version = HTML_VERSION;
+ }
+ if (_mediaType == null) {
+ _mediaType = "text/html";
+ }
+ _indent = true;
+ }
+ else if (_method.equals("text")) {
+ if (_mediaType == null) {
+ _mediaType = "text/plain";
+ }
+ }
+ }
+
+ // Set output properties in current stylesheet
+ parser.getCurrentStylesheet().setOutputProperties(outputProperties);
}
/**
@@ -216,7 +279,7 @@
il.append(classGen.loadTranslet());
// Only update _version field if set and different from default
- if ((_version != null) && (!_version.equals(ONE_DOT_ZERO_STRING))) {
+ if (_version != null && !_version.equals(XML_VERSION)) {
field = cpg.addFieldref(TRANSLET_CLASS, "_version", STRING_SIG);
il.append(DUP);
il.append(new PUSH(cpg, _version));
diff --git a/src/org/apache/xalan/xsltc/compiler/ParentLocationPath.java b/src/org/apache/xalan/xsltc/compiler/ParentLocationPath.java
index adca196..8ffd159 100644
--- a/src/org/apache/xalan/xsltc/compiler/ParentLocationPath.java
+++ b/src/org/apache/xalan/xsltc/compiler/ParentLocationPath.java
@@ -216,10 +216,12 @@
Expression stp = _step;
if (stp instanceof ParentLocationPath)
stp = ((ParentLocationPath)stp).getStep();
+
if ((_path instanceof Step) && (stp instanceof Step)) {
final int path = ((Step)_path).getAxis();
final int step = ((Step)stp).getAxis();
if ((path == Axis.DESCENDANTORSELF && step == Axis.CHILD) ||
+ (path == Axis.DESCENDANTORSELF && step == Axis.ATTRIBUTE) ||
(path == Axis.PRECEDING && step == Axis.PARENT)) {
final int incl = cpg.addMethodref(STEP_ITERATOR_CLASS,
"includeSelf",
diff --git a/src/org/apache/xalan/xsltc/compiler/Parser.java b/src/org/apache/xalan/xsltc/compiler/Parser.java
index c4b2fd6..6e71ffe 100644
--- a/src/org/apache/xalan/xsltc/compiler/Parser.java
+++ b/src/org/apache/xalan/xsltc/compiler/Parser.java
@@ -71,6 +71,7 @@
import java.util.Vector;
import java.util.Hashtable;
import java.util.Dictionary;
+import java.util.Properties;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Stack;
@@ -99,6 +100,7 @@
private Vector _warnings; // Contains all compilation errors
private Hashtable _instructionClasses; // Maps instructions to classes
+ private Hashtable _instructionAttrs;; // reqd and opt attrs
private Hashtable _qNames;
private Hashtable _namespaces;
private QName _useAttributeSets;
@@ -126,6 +128,7 @@
_qNames = new Hashtable(512);
_namespaces = new Hashtable();
_instructionClasses = new Hashtable();
+ _instructionAttrs = new Hashtable();
_variableScope = new Hashtable();
_template = null;
_errors = new Vector();
@@ -136,6 +139,7 @@
_currentImportPrecedence = 1;
initStdClasses();
+ initInstructionAttrs();
initExtClasses();
initSymbolTable();
@@ -166,6 +170,10 @@
return _output;
}
+ public Properties getOutputProperties() {
+ return getTopLevelStylesheet().getOutputProperties();
+ }
+
public void addVariable(Variable var) {
addVariableOrParam(var);
}
@@ -265,6 +273,10 @@
}
public QName getQName(final String stringRep) {
+ return getQName(stringRep, true);
+ }
+
+ public QName getQName(final String stringRep, boolean reportError) {
// parse and retrieve namespace
final int colon = stringRep.lastIndexOf(':');
if (colon != -1) {
@@ -275,7 +287,7 @@
// Get the namespace uri from the symbol table
if (prefix.equals("xmlns") == false) {
namespace = _symbolTable.lookupNamespace(prefix);
- if (namespace == null) {
+ if (namespace == null && reportError) {
final int line = _locator.getLineNumber();
ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR,
line, prefix);
@@ -567,6 +579,71 @@
return(external);
}
+ private void initAttrTable(String elementName, String[] attrs) {
+ _instructionAttrs.put(getQName(XSLT_URI, XSL, elementName),
+ attrs);
+ }
+
+ private void initInstructionAttrs() {
+ initAttrTable("template",
+ new String[] {"match", "name", "priority", "mode"});
+ initAttrTable("stylesheet",
+ new String[] {"id", "version", "extension-element-prefixes",
+ "exclude-result-prefixes"});
+ initAttrTable("transform",
+ new String[] {"id", "version", "extension-element-prefixes",
+ "exclude-result-prefixes"});
+ initAttrTable("text", new String[] {"disable-output-escaping"});
+ initAttrTable("if", new String[] {"test"});
+ initAttrTable("choose", new String[] {});
+ initAttrTable("when", new String[] {"test"});
+ initAttrTable("otherwise", new String[] {});
+ initAttrTable("for-each", new String[] {"select"});
+ initAttrTable("message", new String[] {"terminate"});
+ initAttrTable("number",
+ new String[] {"level", "count", "from", "value", "format", "lang",
+ "letter-value", "grouping-separator", "grouping-size"});
+ initAttrTable("comment", new String[] {});
+ initAttrTable("copy", new String[] {"use-attribute-sets"});
+ initAttrTable("copy-of", new String[] {"select"});
+ initAttrTable("param", new String[] {"name", "select"});
+ initAttrTable("with-param", new String[] {"name", "select"});
+ initAttrTable("variable", new String[] {"name", "select"});
+ initAttrTable("output",
+ new String[] {"method", "version", "encoding",
+ "omit-xml-declaration", "standalone", "doctype-public",
+ "doctype-system", "cdata-section-elements", "indent",
+ "media-type"});
+ initAttrTable("sort",
+ new String[] {"select", "order", "case-order", "lang", "data-type"});
+ initAttrTable("key", new String[] {"name", "match", "use"});
+ initAttrTable("fallback", new String[] {});
+ initAttrTable("attribute", new String[] {"name", "namespace"});
+ initAttrTable("attribute-set",
+ new String[] {"name", "use-attribute-sets"});
+ initAttrTable("value-of",
+ new String[] {"select", "disable-output-escaping"});
+ initAttrTable("element",
+ new String[] {"name", "namespace", "use-attribute-sets"});
+ initAttrTable("call-template", new String[] {"name"});
+ initAttrTable("apply-templates", new String[] {"select", "mode"});
+ initAttrTable("apply-imports", new String[] {});
+ initAttrTable("decimal-format",
+ new String[] {"name", "decimal-separator", "grouping-separator",
+ "infinity", "minus-sign", "NaN", "percent", "per-mille",
+ "zero-digit", "digit", "pattern-separator"});
+ initAttrTable("import", new String[] {"href"});
+ initAttrTable("include", new String[] {"href"});
+ initAttrTable("strip-space", new String[] {"elements"});
+ initAttrTable("preserve-space", new String[] {"elements"});
+ initAttrTable("processing-instruction", new String[] {"name"});
+ initAttrTable("namespace-alias",
+ new String[] {"stylesheet-prefix", "result-prefix"});
+ }
+
+
+
+
/**
* Initialize the _instructionClasses Hashtable, which maps XSL element
* names to Java classes in this package.
@@ -614,8 +691,8 @@
COMPILER_PACKAGE + '.' + className);
}
- public boolean elementSupported(QName qname) {
- return(_instructionClasses.get(qname) != null);
+ public boolean elementSupported(String namespace, String localName) {
+ return(_instructionClasses.get(getQName(namespace, XSL, localName)) != null);
}
public boolean functionSupported(String fname) {
@@ -660,6 +737,7 @@
MethodType B_V = new MethodType(Type.Boolean, Type.Void);
MethodType B_B = new MethodType(Type.Boolean, Type.Boolean);
MethodType B_S = new MethodType(Type.Boolean, Type.String);
+ MethodType D_T = new MethodType(Type.NodeSet, Type.ResultTree);
MethodType R_RR = new MethodType(Type.Real, Type.Real, Type.Real);
MethodType I_II = new MethodType(Type.Int, Type.Int, Type.Int);
MethodType B_RR = new MethodType(Type.Boolean, Type.Real, Type.Real);
@@ -744,6 +822,9 @@
_symbolTable.addPrimop("normalize-space", S_S);
_symbolTable.addPrimop("system-property", S_S);
+ // Extensions
+ _symbolTable.addPrimop("nodeset", D_T);
+
// Operators +, -, *, /, % defined on real types.
_symbolTable.addPrimop("+", R_RR);
_symbolTable.addPrimop("-", R_RR);
@@ -814,10 +895,16 @@
* until we have received all child elements of an unsupported element to
* see if any <xsl:fallback> elements exist.
*/
- public SyntaxTreeNode makeInstance(String uri, String prefix, String local){
+
+ private boolean versionIsOne = true;
+
+ public SyntaxTreeNode makeInstance(String uri, String prefix,
+ String local, Attributes attributes)
+ {
+ boolean isStylesheet = false;
+ SyntaxTreeNode node = null;
QName qname = getQName(uri, prefix, local);
String className = (String)_instructionClasses.get(qname);
- SyntaxTreeNode node = null;
if (className != null) {
try {
@@ -825,10 +912,41 @@
node = (SyntaxTreeNode)clazz.newInstance();
node.setQName(qname);
node.setParser(this);
- if (_locator != null)
+ if (_locator != null){
node.setLineNumber(_locator.getLineNumber());
+ }
if (node instanceof Stylesheet) {
- _xsltc.setStylesheet((Stylesheet)node);
+ isStylesheet = true;
+ _xsltc.setStylesheet((Stylesheet) node);
+ }
+
+ // Check for illegal attributes
+ String[] legal = (String[]) _instructionAttrs.get(qname);
+ if (versionIsOne && legal != null) {
+ int j;
+ final int n = attributes.getLength();
+
+ for (int i = 0; i < n; i++) {
+ final String attrQName = attributes.getQName(i);
+
+ if (isStylesheet && attrQName.equals("version")) {
+ versionIsOne = attributes.getValue(i).equals("1.0");
+ }
+
+ if (attrQName.startsWith("xml")) continue;
+
+ for (j = 0; j < legal.length; j++) {
+ if (attrQName.equalsIgnoreCase(legal[j])) {
+ break;
+ }
+ }
+ if (j == legal.length) {
+ final ErrorMsg err =
+ new ErrorMsg(ErrorMsg.ILLEGAL_ATTRIBUTE_ERR,
+ attrQName, node);
+ reportError(WARNING, err);
+ }
+ }
}
}
catch (ClassNotFoundException e) {
@@ -1072,7 +1190,9 @@
* This has to be passed on to the symbol table!
*/
public void startPrefixMapping(String prefix, String uri) {
- if (_prefixMapping == null) _prefixMapping = new Hashtable();
+ if (_prefixMapping == null) {
+ _prefixMapping = new Hashtable();
+ }
_prefixMapping.put(prefix, uri);
}
@@ -1091,13 +1211,10 @@
String qname, Attributes attributes)
throws SAXException {
final int col = qname.lastIndexOf(':');
- final String prefix;
- if (col == -1)
- prefix = null;
- else
- prefix = qname.substring(0, col);
+ final String prefix = (col == -1) ? null : qname.substring(0, col);
- SyntaxTreeNode element = makeInstance(uri, prefix, localname);
+ SyntaxTreeNode element = makeInstance(uri, prefix,
+ localname, attributes);
if (element == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.ELEMENT_PARSE_ERR,
prefix+':'+localname);
diff --git a/src/org/apache/xalan/xsltc/compiler/ProcessingInstruction.java b/src/org/apache/xalan/xsltc/compiler/ProcessingInstruction.java
index 91e40a4..fa2a188 100644
--- a/src/org/apache/xalan/xsltc/compiler/ProcessingInstruction.java
+++ b/src/org/apache/xalan/xsltc/compiler/ProcessingInstruction.java
@@ -109,13 +109,13 @@
// get String out of the handler
il.append(new INVOKEVIRTUAL(cpg.addMethodref(STRING_VALUE_HANDLER,
- "getValue",
- "()"+STRING_SIG)));
+ "getValueOfPI",
+ "()" + STRING_SIG)));
// call "processingInstruction"
final int processingInstruction =
cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
"processingInstruction",
- "("+STRING_SIG+STRING_SIG+")V");
+ "(" + STRING_SIG + STRING_SIG + ")V");
il.append(new INVOKEINTERFACE(processingInstruction, 3));
// Restore old handler base from stack
il.append(methodGen.storeHandler());
diff --git a/src/org/apache/xalan/xsltc/compiler/Step.java b/src/org/apache/xalan/xsltc/compiler/Step.java
index 4d54224..9146616 100644
--- a/src/org/apache/xalan/xsltc/compiler/Step.java
+++ b/src/org/apache/xalan/xsltc/compiler/Step.java
@@ -508,22 +508,6 @@
il.append(new CHECKCAST(cpg.addClass(className)));
}
il.append(new INVOKESPECIAL(idx));
-
- // Determine if the node set should be generated using the
- // natural order of the node set or document order.
- // See CurrentNodeListIterator's constructor(s) for details.
- SyntaxTreeNode parent = getParent();
- while (!(parent instanceof Template)) {
- if (parent == null) break;
- if (parent instanceof ApplyTemplates) {
- idx = cpg.addMethodref(CURRENT_NODE_LIST_ITERATOR,
- "forceNaturalOrder",
- "()"+NODE_ITERATOR_SIG);
- il.append(new INVOKEVIRTUAL(idx));
- break;
- }
- parent = parent.getParent();
- }
}
}
}
diff --git a/src/org/apache/xalan/xsltc/compiler/StepPattern.java b/src/org/apache/xalan/xsltc/compiler/StepPattern.java
index 78243a3..c14b743 100644
--- a/src/org/apache/xalan/xsltc/compiler/StepPattern.java
+++ b/src/org/apache/xalan/xsltc/compiler/StepPattern.java
@@ -162,10 +162,12 @@
private int analyzeCases() {
boolean noContext = true;
final int n = _predicates.size();
+
for (int i = 0; i < n && noContext; i++) {
final Predicate pred = (Predicate)_predicates.elementAt(i);
- final Expression exp = pred.getExpr();
- if (exp.hasPositionCall()) noContext = false;
+ if (pred.getExpr().hasPositionCall()) {
+ noContext = false;
+ }
}
if (noContext) {
diff --git a/src/org/apache/xalan/xsltc/compiler/Stylesheet.java b/src/org/apache/xalan/xsltc/compiler/Stylesheet.java
index 15b0522..9b04d02 100644
--- a/src/org/apache/xalan/xsltc/compiler/Stylesheet.java
+++ b/src/org/apache/xalan/xsltc/compiler/Stylesheet.java
@@ -66,6 +66,7 @@
import java.util.Vector;
import java.util.Hashtable;
+import java.util.Properties;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Iterator;
@@ -117,6 +118,7 @@
private int _importPrecedence = 1;
private Mode _defaultMode;
private boolean _multiDocument = false;
+ private boolean _callsNodeset = false;
// All named key elements (needed by Key/IdPattern)
private Hashtable _keys = new Hashtable();
@@ -131,6 +133,8 @@
private boolean _forwardReference = false;
+ private Properties _outputProperties = null;
+
public void setForwardReference() {
_forwardReference = true;
}
@@ -147,6 +151,21 @@
_simplified = true;
}
+ public void setOutputProperty(String key, String value) {
+ if (_outputProperties == null) {
+ _outputProperties = new Properties();
+ }
+ _outputProperties.setProperty(key, value);
+ }
+
+ public void setOutputProperties(Properties props) {
+ _outputProperties = props;
+ }
+
+ public Properties getOutputProperties() {
+ return _outputProperties;
+ }
+
public void setMultiDocument(boolean flag) {
_multiDocument = flag;
}
@@ -155,6 +174,15 @@
return _multiDocument;
}
+ public void setCallsNodeset(boolean flag) {
+ if (flag) setMultiDocument(flag);
+ _callsNodeset = flag;
+ }
+
+ public boolean callsNodeset() {
+ return _callsNodeset;
+ }
+
public void numberFormattingUsed() {
_numberFormattingUsed = true;
}
@@ -284,7 +312,6 @@
super.addPrefixMapping(prefix, uri);
}
-
/**
* Store extension URIs
*/
@@ -308,8 +335,10 @@
public void excludeExtensionPrefixes(Parser parser) {
final SymbolTable stable = parser.getSymbolTable();
final String excludePrefixes = getAttribute("exclude-result-prefixes");
- final String extensionPrefixes =
- getAttribute("extension-element-prefixes");
+ final String extensionPrefixes = getAttribute("extension-element-prefixes");
+
+ // Exclude XSLT uri
+ stable.excludeURI(Constants.XSLT_URI);
stable.excludeNamespaces(excludePrefixes);
stable.excludeNamespaces(extensionPrefixes);
extensionURI(extensionPrefixes, stable);
@@ -692,12 +721,31 @@
return("("+DOM_INTF_SIG+NODE_ITERATOR_SIG+TRANSLET_OUTPUT_SIG+")V");
}
-
+ /**
+ * This method returns a vector with variables in the order in which
+ * they are to be compiled. The order is determined by the dependencies
+ * between them. The first step is to close the input vector under
+ * the dependence relation (this is usually needed when variables are
+ * defined inside other variables in a RTF).
+ */
private Vector resolveReferences(Vector input) {
+
+ // Make sure that the vector 'input' is closed
+ for (int i = 0; i < input.size(); i++) {
+ final VariableBase var = (VariableBase) input.elementAt(i);
+ final Vector dep = var.getDependencies();
+ final int depSize = (dep != null) ? dep.size() : 0;
+
+ for (int j = 0; j < depSize; j++) {
+ final VariableBase depVar = (VariableBase) dep.elementAt(j);
+ if (!input.contains(depVar)) {
+ input.addElement(depVar);
+ }
+ }
+ }
+
Vector result = new Vector();
-
int zeroDep = 0;
-
while (input.size() > 0) {
boolean changed = false;
for (int i = 0; i < input.size(); ) {
@@ -717,6 +765,8 @@
i++;
}
}
+
+
// If nothing was changed in this pass then we have a circular ref
if (!changed) {
ErrorMsg err = new ErrorMsg(ErrorMsg.CIRCULAR_VARIABLE_ERR,
@@ -878,6 +928,18 @@
"("+OUTPUT_HANDLER_SIG+")V");
il.append(new INVOKEVIRTUAL(index));
+ // Compile buildKeys -- TODO: omit if not needed
+ final String keySig = compileBuildKeys(classGen);
+ final int keyIdx = cpg.addMethodref(getClassName(),
+ "buildKeys", keySig);
+ il.append(classGen.loadTranslet()); // The 'this' pointer
+ il.append(classGen.loadTranslet());
+ il.append(new GETFIELD(domField)); // The DOM reference
+ il.append(transf.loadIterator()); // Not really used, but...
+ il.append(transf.loadHandler()); // The output handler
+ il.append(new PUSH(cpg, DOM.ROOTNODE)); // Start with the root node
+ il.append(new INVOKEVIRTUAL(keyIdx));
+
// Look for top-level elements that need handling
final Enumeration toplevel = elements();
if ((_globals.size() > 0) || (toplevel.hasMoreElements())) {
@@ -896,17 +958,6 @@
il.append(new INVOKEVIRTUAL(topLevelIdx));
}
- final String keySig = compileBuildKeys(classGen);
- final int keyIdx = cpg.addMethodref(getClassName(),
- "buildKeys", keySig);
- il.append(classGen.loadTranslet()); // The 'this' pointer
- il.append(classGen.loadTranslet());
- il.append(new GETFIELD(domField)); // The DOM reference
- il.append(transf.loadIterator()); // Not really used, but...
- il.append(transf.loadHandler()); // The output handler
- il.append(new PUSH(cpg, DOM.ROOTNODE)); // Start with the root node
- il.append(new INVOKEVIRTUAL(keyIdx));
-
// start document
il.append(transf.loadHandler());
il.append(transf.startDocument());
diff --git a/src/org/apache/xalan/xsltc/compiler/SymbolTable.java b/src/org/apache/xalan/xsltc/compiler/SymbolTable.java
index cf5c576..4b190d1 100644
--- a/src/org/apache/xalan/xsltc/compiler/SymbolTable.java
+++ b/src/org/apache/xalan/xsltc/compiler/SymbolTable.java
@@ -262,11 +262,10 @@
* Check if a namespace should not be declared in the output (unless used)
*/
public boolean isExcludedNamespace(String uri) {
- if (uri == null) return false;
- if (_excludedURI == null) return false;
- final Integer refcnt = (Integer)_excludedURI.get(uri);
- if (refcnt == null) return false;
- if (refcnt.intValue() > 0) return true;
+ if (uri != null && _excludedURI != null) {
+ final Integer refcnt = (Integer)_excludedURI.get(uri);
+ return (refcnt != null && refcnt.intValue() > 0);
+ }
return false;
}
diff --git a/src/org/apache/xalan/xsltc/compiler/SyntaxTreeNode.java b/src/org/apache/xalan/xsltc/compiler/SyntaxTreeNode.java
index 26f6c13..322781c 100644
--- a/src/org/apache/xalan/xsltc/compiler/SyntaxTreeNode.java
+++ b/src/org/apache/xalan/xsltc/compiler/SyntaxTreeNode.java
@@ -193,18 +193,16 @@
* @return The value of the attribute of name 'qname'.
*/
protected String getAttribute(String qname) {
- if (_attributes == null)
- return(Constants.EMPTYSTRING);
+ if (_attributes == null) {
+ return EMPTYSTRING;
+ }
final String value = _attributes.getValue(qname);
- if (value == null)
- return(Constants.EMPTYSTRING);
- else
- return(value);
+ return (value == null || value.equals(EMPTYSTRING)) ?
+ EMPTYSTRING : value;
}
protected boolean hasAttribute(String qname) {
- if (_attributes == null) return false;
- return (_attributes.getValue(qname) != null);
+ return (_attributes != null && _attributes.getValue(qname) != null);
}
/**
@@ -554,10 +552,11 @@
* @param methodGen BCEL Java method generator
*/
protected void compileResultTree(ClassGenerator classGen,
- MethodGenerator methodGen) {
-
+ MethodGenerator methodGen)
+ {
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
+ final Stylesheet stylesheet = classGen.getStylesheet();
// Save the current handler base on the stack
il.append(methodGen.loadHandler());
@@ -602,13 +601,42 @@
il.append(new NEW(cpg.addClass(DOM_ADAPTER_CLASS)));
il.append(new DUP_X1());
il.append(SWAP);
- // Give the DOM adapter an empty type mapping to start with.
- // Type mapping is expensive and will only be done when casting
- // a result tree fragment to a node-set.
- il.append(new ICONST(0));
- il.append(new ANEWARRAY(cpg.addClass(STRING)));
- il.append(DUP);
- il.append(new INVOKESPECIAL(index)); // leave DOMAdapter on stack
+
+ /*
+ * Give the DOM adapter an empty type mapping if the nodeset
+ * extension function is never called.
+ */
+ if (!stylesheet.callsNodeset()) {
+ il.append(new ICONST(0));
+ il.append(new ANEWARRAY(cpg.addClass(STRING)));
+ il.append(DUP);
+ il.append(new INVOKESPECIAL(index));
+ }
+ else {
+ // Push name arrays on the stack
+ il.append(ALOAD_0);
+ il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
+ NAMES_INDEX,
+ NAMES_INDEX_SIG)));
+ il.append(ALOAD_0);
+ il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
+ NAMESPACE_INDEX,
+ NAMESPACE_INDEX_SIG)));
+
+ // Initialized DOM adapter
+ il.append(new INVOKESPECIAL(index));
+
+ // Add DOM adapter to MultiDOM class by calling addDOMAdapter()
+ il.append(DUP);
+ il.append(methodGen.loadDOM());
+ il.append(new CHECKCAST(cpg.addClass(classGen.getDOMClass())));
+ il.append(SWAP);
+ index = cpg.addMethodref(MULTI_DOM_CLASS,
+ "addDOMAdapter",
+ "(" + DOM_ADAPTER_SIG + ")I");
+ il.append(new INVOKEVIRTUAL(index));
+ il.append(POP); // ignore mask returned by addDOMAdapter
+ }
}
// Restore old handler base from stack
diff --git a/src/org/apache/xalan/xsltc/compiler/Template.java b/src/org/apache/xalan/xsltc/compiler/Template.java
index 6191e9e..a02faac 100644
--- a/src/org/apache/xalan/xsltc/compiler/Template.java
+++ b/src/org/apache/xalan/xsltc/compiler/Template.java
@@ -132,6 +132,11 @@
return _name != null;
}
+ public boolean isRootTemplate() {
+ final String match = getAttribute("match");
+ return (match != null && match.equals("/"));
+ }
+
public Pattern getPattern() {
return _pattern;
}
diff --git a/src/org/apache/xalan/xsltc/compiler/XSLTC.java b/src/org/apache/xalan/xsltc/compiler/XSLTC.java
index 4baf1ed..a5676d5 100644
--- a/src/org/apache/xalan/xsltc/compiler/XSLTC.java
+++ b/src/org/apache/xalan/xsltc/compiler/XSLTC.java
@@ -72,6 +72,7 @@
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Properties;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Date;
@@ -139,6 +140,7 @@
private int _outputType = FILE_OUTPUT; // by default
private Vector _classes;
+ private boolean _callsNodeset = false;
private boolean _multiDocument = false;
/**
@@ -163,6 +165,13 @@
}
/**
+ * Only for user by the internal TrAX implementation.
+ */
+ public Properties getOutputProperties() {
+ return _parser.getOutputProperties();
+ }
+
+ /**
* Initializes the compiler to compile a new stylesheet
*/
public void init() {
@@ -317,6 +326,7 @@
}
// Generate the bytecodes and output the translet class(es)
if ((!_parser.errorsFound()) && (_stylesheet != null)) {
+ _stylesheet.setCallsNodeset(_callsNodeset);
_stylesheet.setMultiDocument(_multiDocument);
_stylesheet.translate();
}
@@ -435,7 +445,6 @@
_parser.printWarnings();
}
-
/**
* This method is called by the XPathParser when it encounters a call
* to the document() function. Affects the DOM used by the translet.
@@ -449,6 +458,19 @@
}
/**
+ * This method is called by the XPathParser when it encounters a call
+ * to the nodeset() extension function. Implies multi document.
+ */
+ protected void setCallsNodeset(boolean flag) {
+ if (flag) setMultiDocument(flag);
+ _callsNodeset = flag;
+ }
+
+ public boolean callsNodeset() {
+ return _callsNodeset;
+ }
+
+ /**
* Set the class name for the generated translet. This class name is
* overridden if multiple stylesheets are compiled in one go using the
* compile(Vector urls) method.
diff --git a/src/org/apache/xalan/xsltc/compiler/XslAttribute.java b/src/org/apache/xalan/xsltc/compiler/XslAttribute.java
index f6e8b6e..96f64f9 100644
--- a/src/org/apache/xalan/xsltc/compiler/XslAttribute.java
+++ b/src/org/apache/xalan/xsltc/compiler/XslAttribute.java
@@ -73,10 +73,9 @@
final class XslAttribute extends Instruction {
- // Attribute contents
- private AttributeValue _name; // name treated as AVT (7.1.3)
- private AttributeValueTemplate _namespace = null;
private String _prefix;
+ private AttributeValue _name; // name treated as AVT (7.1.3)
+ private AttributeValueTemplate _namespace = null;
private boolean _ignore = false;
/**
@@ -99,15 +98,15 @@
* Parses the attribute's contents. Special care taken for namespaces.
*/
public void parseContents(Parser parser) {
-
- final SymbolTable stable = parser.getSymbolTable();
- String namespace = getAttribute("namespace");
- String name = getAttribute("name");
- QName qname = parser.getQName(name);
- final String prefix = qname.getPrefix();
boolean generated = false;
+ final SymbolTable stable = parser.getSymbolTable();
- if ((prefix != null) && (prefix.equals("xmlns"))) {
+ String name = getAttribute("name");
+ String namespace = getAttribute("namespace");
+ QName qname = parser.getQName(name, false);
+ final String prefix = qname.getPrefix();
+
+ if ((prefix != null) && (prefix.equals(XMLNS_PREFIX))) {
reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name);
return;
}
@@ -118,11 +117,13 @@
for (int i = 0; i < parent.elementCount(); i++) {
SyntaxTreeNode item = (SyntaxTreeNode)siblings.elementAt(i);
if (item == this) break;
+
// These three objects result in one or more attribute output
if (item instanceof XslAttribute) continue;
if (item instanceof UseAttributeSets) continue;
if (item instanceof LiteralAttribute) continue;
if (item instanceof Text) continue;
+
// These objects _can_ result in one or more attribute
// The output handler will generate an error if not (at runtime)
if (item instanceof If) continue;
@@ -134,24 +135,23 @@
}
// Get namespace from namespace attribute?
- if ((namespace != null) && (namespace != Constants.EMPTYSTRING)) {
- // Prefix could be in symbol table
+ if (namespace != null && namespace != Constants.EMPTYSTRING) {
_prefix = lookupPrefix(namespace);
- _namespace = new AttributeValueTemplate(namespace, parser);
+ _namespace = new AttributeValueTemplate(namespace, parser, this);
}
// Get namespace from prefix in name attribute?
- else if ((prefix != null) && (prefix != Constants.EMPTYSTRING)) {
+ else if (prefix != null && prefix != Constants.EMPTYSTRING) {
_prefix = prefix;
namespace = lookupNamespace(prefix);
- if (namespace != null)
- _namespace = new AttributeValueTemplate(namespace, parser);
+ if (namespace != null) {
+ _namespace = new AttributeValueTemplate(namespace, parser, this);
+ }
}
// Common handling for namespaces:
if (_namespace != null) {
-
// Generate prefix if we have none
- if (_prefix == null) {
+ if (_prefix == null || _prefix == Constants.EMPTYSTRING) {
if (prefix != null) {
_prefix = prefix;
}
@@ -160,25 +160,25 @@
generated = true;
}
}
-
- if (_prefix == Constants.EMPTYSTRING) {
- name = qname.getLocalPart();
+ else if (prefix != null && !prefix.equals(_prefix)) {
+ _prefix = prefix;
}
- else {
- name = _prefix+":"+qname.getLocalPart();
- // PROBLEM:
- // The namespace URI must be passed to the parent element,
- // but we don't yet know what the actual URI is (as we only
- // know it as an attribute value template). New design needed.
- if ((parent instanceof LiteralElement) && (!generated)) {
- ((LiteralElement)parent).registerNamespace(_prefix,
- namespace,
- stable,false);
- }
+
+ name = _prefix + ":" + qname.getLocalPart();
+
+ /*
+ * TODO: The namespace URI must be passed to the parent
+ * element but we don't yet know what the actual URI is
+ * (as we only know it as an attribute value template).
+ */
+ if ((parent instanceof LiteralElement) && (!generated)) {
+ ((LiteralElement)parent).registerNamespace(_prefix,
+ namespace,
+ stable, false);
}
}
- if (name.equals("xmlns")) {
+ if (name.equals(XMLNS_PREFIX)) {
reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name);
return;
}
@@ -191,15 +191,14 @@
parseChildren(parser);
}
- /**
- *
- */
public Type typeCheck(SymbolTable stable) throws TypeCheckError {
- if (_ignore) return(Type.Void);
- _name.typeCheck(stable);
- if (_namespace != null)
- _namespace.typeCheck(stable);
- typeCheckContents(stable);
+ if (!_ignore) {
+ _name.typeCheck(stable);
+ if (_namespace != null) {
+ _namespace.typeCheck(stable);
+ }
+ typeCheckContents(stable);
+ }
return Type.Void;
}
diff --git a/src/org/apache/xalan/xsltc/compiler/XslElement.java b/src/org/apache/xalan/xsltc/compiler/XslElement.java
index 84d7d38..6f3df54 100644
--- a/src/org/apache/xalan/xsltc/compiler/XslElement.java
+++ b/src/org/apache/xalan/xsltc/compiler/XslElement.java
@@ -74,10 +74,11 @@
final class XslElement extends Instruction {
- private AttributeValue _name; // name treated as AVT (7.1.3)
- private AttributeValueTemplate _namespace = null;
private String _prefix;
private boolean _ignore = false;
+ private boolean _isLiteralName = true;
+ private AttributeValueTemplate _name;
+ private AttributeValueTemplate _namespace;
/**
* Displays the contents of the element
@@ -89,87 +90,124 @@
}
/**
- * Parses the element's contents. Special care taken for namespaces.
- * TODO: The namespace attribute that specifies the namespace to use
- * for the element is an attribute value template and not a string
- * constant. This means that we do not know what namespace is used
- * before runtime. This causes a problem with the way output namespaces
- * are handled at compile-time. We use a shortcut in this method to get
- * around the problem by treating the namaspace attribute as a constant.
- * (Yes, I know this is a hack, bad, bad, bad.)
+ * This method is now deprecated. The new implemation of this class
+ * never declares the default NS.
*/
- public void parseContents(Parser parser) {
+ public boolean declaresDefaultNS() {
+ return false;
+ }
+ /**
+ * Checks if <param>str</param> is a literal (i.e. not an AVT) or not.
+ */
+ private boolean isLiteral(String str) {
+ final int length = str.length();
+ for (int i = 0; i < length; i++) {
+ if (str.charAt(i) == '{' && str.charAt(i + 1) != '{') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Simple check to determine if qname is legal. If it returns false
+ * then <param>str</param> is illegal; if it returns true then
+ * <param>str</param> may or may not be legal.
+ */
+ private boolean isLegalName(String str) {
+ if (str.indexOf(' ') > -1) {
+ return false;
+ }
+ final int colon = str.indexOf(':');
+ if (colon == 0 || colon == str.length() - 1) {
+ return false;
+ }
+ final char first = str.charAt(0);
+ if (!Character.isLetter(first) && first != '_') {
+ return false;
+ }
+ return true;
+ }
+
+ public void parseContents(Parser parser) {
final SymbolTable stable = parser.getSymbolTable();
- // Get the "name" attribute of the <xsl:element> element
+ // Handle the 'name' attribute
String name = getAttribute("name");
- if ((name == null) || (name.equals(EMPTYSTRING))) {
+ if (name == EMPTYSTRING) {
ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
name, this);
parser.reportError(WARNING, msg);
- _ignore = true; // Ignore the element if the QName is invalid
+ parseChildren(parser);
+ _ignore = true; // Ignore the element if the QName is invalid
return;
}
- // Try to construct a QName and then get the prefix and local part
- QName qname = parser.getQNameSafe(name);
- String prefix = qname.getPrefix();
- String local = qname.getLocalPart();
-
- // First try to get the namespace URI from the "namespace" attribute
+ // Get namespace attribute
String namespace = getAttribute("namespace");
- // Then try to get it from the "name" attribute QName prefix
- if (!hasAttribute("namespace")) {
- // We are supposed to use the default namespace URI if the QName
- // from the "name" attribute is not prefixed, so check that first
- if (prefix == null) prefix = EMPTYSTRING;
- // Then look up the URI that is in scope for the prefix
- namespace = lookupNamespace(prefix);
- // Signal error if the prefix does not map to any namespace URI
- if (namespace == null) {
- ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR,
- prefix, this);
- parser.reportError(WARNING, err);
+ // Optimize compilation when name is known at compile time
+ _isLiteralName = isLiteral(name);
+ if (_isLiteralName) {
+ if (!isLegalName(name)) {
+ ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
+ name, this);
+ parser.reportError(WARNING, msg);
parseChildren(parser);
- _ignore = true; // Ignore the element if prefix is undeclared
+ _ignore = true; // Ignore the element if the QName is invalid
return;
}
- _namespace = new AttributeValueTemplate(namespace, parser);
- _prefix = prefix;
+
+ final QName qname = parser.getQNameSafe(name);
+ String prefix = qname.getPrefix();
+ String local = qname.getLocalPart();
+
+ if (prefix == null) {
+ prefix = EMPTYSTRING;
+ }
+
+ if (!hasAttribute("namespace")) {
+ namespace = lookupNamespace(prefix);
+ if (namespace == null) {
+ ErrorMsg err = new ErrorMsg(ErrorMsg.NAMESPACE_UNDEF_ERR,
+ prefix, this);
+ parser.reportError(WARNING, err);
+ parseChildren(parser);
+ _ignore = true; // Ignore the element if prefix is undeclared
+ return;
+ }
+ _prefix = prefix;
+ _namespace = (namespace == EMPTYSTRING) ? null :
+ new AttributeValueTemplate(namespace, parser, this);
+ }
+ else {
+ if (prefix == EMPTYSTRING) {
+ if (isLiteral(namespace)) {
+ prefix = lookupPrefix(namespace);
+ if (prefix == null) {
+ prefix = stable.generateNamespacePrefix();
+ }
+ }
+
+ // Prepend prefix to local name
+ final StringBuffer newName = new StringBuffer(prefix);
+ if (prefix != EMPTYSTRING) {
+ newName.append(':');
+ }
+ name = newName.append(local).toString();
+ }
+ _prefix = prefix;
+ _namespace = new AttributeValueTemplate(namespace, parser, this);
+ }
}
- // Check if this element belongs in a specific namespace
else {
- // Get the namespace requested by the xsl:element
- _namespace = new AttributeValueTemplate(namespace, parser);
- // Get the current prefix for that namespace (if any)
- _prefix = lookupPrefix(namespace);
- // Is it the default namespace?
- if ((_prefix = prefix) == null) _prefix = EMPTYSTRING;
-
- // Construct final element QName
- if (_prefix == EMPTYSTRING)
- name = qname.getLocalPart();
- else
- name = _prefix+":"+qname.getLocalPart();
+ _namespace = (namespace == EMPTYSTRING) ? null :
+ new AttributeValueTemplate(namespace, parser, this);
}
- _name = AttributeValue.create(this, name, parser);
+ _name = new AttributeValueTemplate(name, parser, this);
- // Next check that the local part of the QName is legal (no whitespace)
- if (_name instanceof SimpleAttributeValue) {
- if (local.equals(EMPTYSTRING) || (local.indexOf(' ') > -1)) {
- ErrorMsg err = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR,
- local, this);
- parser.reportError(WARNING, err);
- parseChildren(parser);
- _ignore = true; // Ignore the element if local part is invalid
- return;
- }
- }
-
- // Handle the 'use-attribute-sets' attribute
final String useSets = getAttribute("use-attribute-sets");
if (useSets.length() > 0) {
setFirstElement(new UseAttributeSets(useSets, parser));
@@ -184,34 +222,30 @@
public Type typeCheck(SymbolTable stable) throws TypeCheckError {
if (!_ignore) {
_name.typeCheck(stable);
- if (_namespace != null)
+ if (_namespace != null) {
_namespace.typeCheck(stable);
+ }
}
typeCheckContents(stable);
return Type.Void;
}
/**
- * Compiles code that emits the element with the necessary namespace
- * definitions. The element itself is ignored if the element definition
- * was in any way erronous, but the child nodes are still processed.
- * See the overriden translateContents() method as well.
+ * This method is called when the name of the element is known at compile time.
+ * In this case, there is no need to inspect the element name at runtime to
+ * determine if a prefix exists, needs to be generated, etc.
*/
- public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
+ public void translateLiteral(ClassGenerator classGen, MethodGenerator methodGen) {
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
- // Ignore this element if not correctly declared
if (!_ignore) {
- // Compile code that emits the element start tag
il.append(methodGen.loadHandler());
_name.translate(classGen, methodGen);
- il.append(DUP2); // duplicate these 2 args for endElement
+ il.append(DUP2);
il.append(methodGen.startElement());
- // Compile code that emits any needed namespace declaration
if (_namespace != null) {
- // public void attribute(final String name, final String value)
il.append(methodGen.loadHandler());
il.append(new PUSH(cpg, _prefix));
_namespace.translate(classGen,methodGen);
@@ -219,12 +253,94 @@
}
}
- // Compile code that emits the element attributes and contents
translateContents(classGen, methodGen);
- // Ignore this element if not correctly declared
if (!_ignore) {
- // Compile code that emits the element end tag
+ il.append(methodGen.endElement());
+ }
+ }
+
+ /**
+ * At runtime the compilation of xsl:element results in code that: (i)
+ * evaluates the avt for the name, (ii) checks for a prefix in the name
+ * (iii) generates a new prefix and create a new qname when necessary
+ * (iv) calls startElement() on the handler (v) looks up a uri in the XML
+ * when the prefix is not known at compile time (vi) calls namespace()
+ * on the handler (vii) evaluates the contents (viii) calls endElement().
+ */
+ public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
+ LocalVariableGen local = null;
+ final ConstantPoolGen cpg = classGen.getConstantPool();
+ final InstructionList il = methodGen.getInstructionList();
+
+ // Optimize translation if element name is a literal
+ if (_isLiteralName) {
+ translateLiteral(classGen, methodGen);
+ return;
+ }
+
+ if (!_ignore) {
+ il.append(methodGen.loadHandler());
+ _name.translate(classGen, methodGen);
+
+ // Call BasisLibrary.getPrefix() and store result in local variable
+ il.append(DUP);
+ final int getPrefix = cpg.addMethodref(BASIS_LIBRARY_CLASS, "getPrefix",
+ "(" + STRING_SIG + ")" + STRING_SIG);
+ il.append(new INVOKESTATIC(getPrefix));
+ il.append(DUP);
+ local = methodGen.addLocalVariable("prefix",
+ org.apache.bcel.generic.Type.STRING,
+ il.getEnd(), null);
+ il.append(new ASTORE(local.getIndex()));
+
+ // If prefix is null then generate a prefix at runtime
+ final BranchHandle ifNotNull = il.append(new IFNONNULL(null));
+ if (_namespace != null) {
+ final int generatePrefix = cpg.addMethodref(BASIS_LIBRARY_CLASS,
+ "generatePrefix",
+ "()" + STRING_SIG);
+ il.append(new INVOKESTATIC(generatePrefix));
+ il.append(DUP);
+ il.append(new ASTORE(local.getIndex()));
+
+ // Prepend newly generated prefix to the name
+ final int makeQName = cpg.addMethodref(BASIS_LIBRARY_CLASS, "makeQName",
+ "(" + STRING_SIG + STRING_SIG + ")" + STRING_SIG);
+ il.append(new INVOKESTATIC(makeQName));
+ }
+ ifNotNull.setTarget(il.append(DUP2));
+ il.append(methodGen.startElement());
+
+ if (_namespace != null) {
+ il.append(methodGen.loadHandler());
+ il.append(new ALOAD(local.getIndex()));
+ _namespace.translate(classGen, methodGen);
+ il.append(methodGen.namespace());
+ }
+ else {
+ // If prefix not known at compile time, call DOM.lookupNamespace()
+ il.append(new ALOAD(local.getIndex()));
+ final BranchHandle ifNull = il.append(new IFNULL(null));
+ il.append(methodGen.loadHandler());
+ il.append(new ALOAD(local.getIndex()));
+
+ il.append(methodGen.loadDOM());
+ il.append(methodGen.loadCurrentNode());
+ il.append(new ALOAD(local.getIndex()));
+
+ final int lookupNamespace = cpg.addInterfaceMethodref(DOM_INTF,
+ "lookupNamespace",
+ "(I" + STRING_SIG + ")" + STRING_SIG);
+ il.append(new INVOKEINTERFACE(lookupNamespace, 3));
+ il.append(methodGen.namespace());
+ ifNull.setTarget(il.append(NOP));
+ }
+ }
+
+ translateContents(classGen, methodGen);
+
+ if (!_ignore) {
il.append(methodGen.endElement());
}
}
@@ -239,7 +355,7 @@
for (int i = 0; i < n; i++) {
final SyntaxTreeNode item =
(SyntaxTreeNode)getContents().elementAt(i);
- if ((_ignore) && (item instanceof XslAttribute)) continue;
+ if (_ignore && item instanceof XslAttribute) continue;
item.translate(classGen, methodGen);
}
}
diff --git a/src/org/apache/xalan/xsltc/compiler/util/ErrorMessages.java b/src/org/apache/xalan/xsltc/compiler/util/ErrorMessages.java
index 5c5b243..1ff59cd 100644
--- a/src/org/apache/xalan/xsltc/compiler/util/ErrorMessages.java
+++ b/src/org/apache/xalan/xsltc/compiler/util/ErrorMessages.java
@@ -220,8 +220,8 @@
"The -i option must be used with the -o option.",
// COMPILE_USAGE_STR
"Usage:\n" +
- " xsltc [-o <output>] [-d <directory>] [-j <jarfile>]\n"+
- " [-p <package name>] [-x] [-s] [-u] <stylesheet>|-i\n\n"+
+ " java org.apache.xalan.xsltc.cmdline.Compile [-o <output>] [-d <directory>] [-j <jarfile>]\n"+
+ " [-p <package name>] [-x] [-s] [-u] { <stylesheet> | -i }\n\n"+
" Where <output> is the name to give the the generated translet.\n"+
" <stylesheet> is one or more stylesheet file names, or if\n"+
" the -u options is specified, one or more stylesheet URLs.\n"+
@@ -235,8 +235,8 @@
" The -s option disables calling System.exit.",
// TRANSFORM_USAGE_STR
"Usage: \n" +
- " xslt [-j <jarfile>] {-u <document_url> | <document>} <class>\n"+
- " [<name1>=<value1> ...]\n\n" +
+ " java org.apache.xalan.xsltc.cmdline.Transform [-j <jarfile>] [-x] [-s]\n" +
+ " {-u <document_url> | <document>} <class> [<param1>=<value1> ...]\n" +
" Where <document> is the xml document to be transformed, or\n" +
" <document_url> is a url for the xml document,\n" +
" <class> is the translet class which is either in\n" +
diff --git a/src/org/apache/xalan/xsltc/compiler/util/MethodGenerator.java b/src/org/apache/xalan/xsltc/compiler/util/MethodGenerator.java
index a0e5fc8..45b42a0 100644
--- a/src/org/apache/xalan/xsltc/compiler/util/MethodGenerator.java
+++ b/src/org/apache/xalan/xsltc/compiler/util/MethodGenerator.java
@@ -103,6 +103,7 @@
private final Instruction _nextNode;
private SlotAllocator _slotAllocator;
+ private boolean _allocatorInit = false;
public MethodGenerator(int access_flags, Type return_type,
Type[] arg_types, String[] arg_names,
@@ -173,17 +174,26 @@
_slotAllocator = new SlotAllocator();
_slotAllocator.initialize(getLocalVariables());
+ _allocatorInit = true;
}
+ /**
+ * Allocates a local variable. If the slot allocator has already been
+ * initialized, then call addLocalVariable2() so that the new variable
+ * is known to the allocator. Failing to do this may cause the allocator
+ * to return a slot that is already in use.
+ */
public LocalVariableGen addLocalVariable(String name, Type type,
InstructionHandle start,
- InstructionHandle end) {
-
- return super.addLocalVariable(name, type, start, end);
+ InstructionHandle end)
+ {
+ return (_allocatorInit) ? addLocalVariable2(name, type, start)
+ : super.addLocalVariable(name, type, start, end);
}
public LocalVariableGen addLocalVariable2(String name, Type type,
- InstructionHandle start) {
+ InstructionHandle start)
+ {
return super.addLocalVariable(name, type,
_slotAllocator.allocateSlot(type),
start, null);
diff --git a/src/org/apache/xalan/xsltc/compiler/util/ReferenceType.java b/src/org/apache/xalan/xsltc/compiler/util/ReferenceType.java
index 8d56714..b04a7f8 100644
--- a/src/org/apache/xalan/xsltc/compiler/util/ReferenceType.java
+++ b/src/org/apache/xalan/xsltc/compiler/util/ReferenceType.java
@@ -113,6 +113,9 @@
else if (type == Type.Node) {
translateTo(classGen, methodGen, (NodeType) type);
}
+ else if (type == Type.ResultTree) {
+ translateTo(classGen, methodGen, (ResultTreeType) type);
+ }
else {
ErrorMsg err = new ErrorMsg(ErrorMsg.INTERNAL_ERR, type.toString());
classGen.getParser().reportError(Constants.FATAL, err);
@@ -211,6 +214,20 @@
}
/**
+ * Casts a reference into a ResultTree.
+ *
+ * @see org.apache.xalan.xsltc.compiler.util.Type#translateTo
+ */
+ public void translateTo(ClassGenerator classGen, MethodGenerator methodGen,
+ ResultTreeType type) {
+ final ConstantPoolGen cpg = classGen.getConstantPool();
+ final InstructionList il = methodGen.getInstructionList();
+ int index = cpg.addMethodref(BASIS_LIBRARY_CLASS, "referenceToResultTree",
+ "(" + OBJECT_SIG + ")" + DOM_INTF_SIG);
+ il.append(new INVOKESTATIC(index));
+ }
+
+ /**
* Expects a reference on the stack and translates it to a non-synthesized
* boolean. It does not push a 0 or a 1 but instead returns branchhandle
* list to be appended to the false list.
diff --git a/src/org/apache/xalan/xsltc/compiler/util/Util.java b/src/org/apache/xalan/xsltc/compiler/util/Util.java
index 2082fe1..0fc872b 100644
--- a/src/org/apache/xalan/xsltc/compiler/util/Util.java
+++ b/src/org/apache/xalan/xsltc/compiler/util/Util.java
@@ -108,17 +108,20 @@
* Replace all illegal Java chars by '_'.
*/
public static String toJavaName(String name) {
- final StringBuffer result = new StringBuffer();
+ if (name.length() > 0) {
+ final StringBuffer result = new StringBuffer();
- char ch = name.charAt(0);
- result.append(Character.isJavaIdentifierStart(ch) ? ch : '_');
+ char ch = name.charAt(0);
+ result.append(Character.isJavaIdentifierStart(ch) ? ch : '_');
- final int n = name.length();
- for (int i = 1; i < n; i++) {
- ch = name.charAt(i);
- result.append(Character.isJavaIdentifierPart(ch) ? ch : '_');
+ final int n = name.length();
+ for (int i = 1; i < n; i++) {
+ ch = name.charAt(i);
+ result.append(Character.isJavaIdentifierPart(ch) ? ch : '_');
+ }
+ return result.toString();
}
- return result.toString();
+ return name;
}
public static Type getJCRefType(String signature) {
@@ -130,15 +133,11 @@
}
public static void println(String s) {
- if (false) {
- System.out.println(s);
- }
+ System.out.println(s);
}
public static void println(char ch) {
- if (false) {
- System.out.println(ch);
- }
+ System.out.println(ch);
}
public static void TRACE1() {
diff --git a/src/org/apache/xalan/xsltc/compiler/xpath.cup b/src/org/apache/xalan/xsltc/compiler/xpath.cup
index 31a8a68..a4f1e85 100644
--- a/src/org/apache/xalan/xsltc/compiler/xpath.cup
+++ b/src/org/apache/xalan/xsltc/compiler/xpath.cup
@@ -119,6 +119,10 @@
_xsltc.setMultiDocument(flag);
}
+ public void setCallsNodeset(boolean flag) {
+ _xsltc.setCallsNodeset(flag);
+ }
+
public int findNodeType(int axis, Object test) {
if (test == null) { // *
@@ -895,6 +899,11 @@
else if (fname == parser.getQName("namespace-uri")) {
RESULT = new NamespaceUriCall(fname, argl);
}
+ // Special case for extension function nodeset()
+ else if (fname.getLocalPart().equals("nodeset")) {
+ parser.setCallsNodeset(true); // implies MultiDOM
+ RESULT = new FunctionCall(fname, argl);
+ }
else {
RESULT = new FunctionCall(fname, argl);
}
diff --git a/src/org/apache/xalan/xsltc/dom/CurrentNodeListIterator.java b/src/org/apache/xalan/xsltc/dom/CurrentNodeListIterator.java
index dfb9f3d..79df194 100644
--- a/src/org/apache/xalan/xsltc/dom/CurrentNodeListIterator.java
+++ b/src/org/apache/xalan/xsltc/dom/CurrentNodeListIterator.java
@@ -99,11 +99,6 @@
_currentNode = currentNode;
}
- public NodeIterator forceNaturalOrder() {
- _docOrder = true;
- return this;
- }
-
public void setRestartable(boolean isRestartable) {
_isRestartable = isRestartable;
_source.setRestartable(isRestartable);
@@ -139,9 +134,8 @@
final int currentNode = _currentNode;
for (int index = _current; index < last; ) {
- final int node = _nodes.at(index++); // note increment
- final int position = docOrder ? index : last - index + 1;
- if (_filter.test(node, position, last, currentNode, _translet, this)) {
+ final int node = _nodes.at(index++); // note increment
+ if (_filter.test(node, index, last, currentNode, _translet, this)) {
_current = index;
return returnNode(node);
}
@@ -156,9 +150,8 @@
final int currNode = _currentNode;
for (int index = _current; index < last; ) {
- int nodeIndex = _nodes.at(index++); // note increment
- final int pos = docOrder ? index : last - index + 1;
- if (_filter.test(nodeIndex, pos, last, currNode, _translet, this)) {
+ int nodeIndex = _nodes.at(index++); // note increment
+ if (_filter.test(nodeIndex, index, last, currNode, _translet, this)) {
lastPosition++;
}
}
@@ -185,7 +178,7 @@
}
public int getLast() {
- return ( _last == -1 ) ? computePositionOfLast() : _last;
+ return (_last == -1) ? computePositionOfLast() : _last;
}
public void setMark() {
diff --git a/src/org/apache/xalan/xsltc/dom/DOMAdapter.java b/src/org/apache/xalan/xsltc/dom/DOMAdapter.java
index 4d75669..e0cc9bf 100644
--- a/src/org/apache/xalan/xsltc/dom/DOMAdapter.java
+++ b/src/org/apache/xalan/xsltc/dom/DOMAdapter.java
@@ -80,6 +80,8 @@
private short[] _NSreverse;
private StripFilter _filter = null;
+
+ private int _multiDOMMask;
public DOMAdapter(DOMImpl dom,
String[] namesArray,
@@ -111,6 +113,14 @@
return _domImpl.getTreeString();
}
+ public int getMultiDOMMask() {
+ return _multiDOMMask;
+ }
+
+ public void setMultiDOMMask(int mask) {
+ _multiDOMMask = mask;
+ }
+
public NodeIterator getChildren(final int node) {
NodeIterator iterator = _domImpl.getChildren(node);
if (_filter == null) {
@@ -139,8 +149,9 @@
public NodeIterator getAxisIterator(final int axis) {
NodeIterator iterator = _domImpl.getAxisIterator(axis);
- if (_filter != null)
+ if (_filter != null) {
iterator = _domImpl.strippingIterator(iterator,_mapping,_filter);
+ }
return(iterator);
}
@@ -278,4 +289,9 @@
return(_domImpl.isAttribute(node));
}
+ public String lookupNamespace(int node, String prefix)
+ throws TransletException
+ {
+ return _domImpl.lookupNamespace(node, prefix);
+ }
}
diff --git a/src/org/apache/xalan/xsltc/dom/DOMBuilder.java b/src/org/apache/xalan/xsltc/dom/DOMBuilder.java
index cc67a53..dabd49e 100644
--- a/src/org/apache/xalan/xsltc/dom/DOMBuilder.java
+++ b/src/org/apache/xalan/xsltc/dom/DOMBuilder.java
@@ -64,4 +64,4 @@
import org.xml.sax.ContentHandler;
import org.xml.sax.ext.LexicalHandler;
-public interface DOMBuilder extends ContentHandler, LexicalHandler { }
+public interface DOMBuilder extends ExtendedSAX { }
diff --git a/src/org/apache/xalan/xsltc/dom/DOMImpl.java b/src/org/apache/xalan/xsltc/dom/DOMImpl.java
index df1a7ea..8721aab 100644
--- a/src/org/apache/xalan/xsltc/dom/DOMImpl.java
+++ b/src/org/apache/xalan/xsltc/dom/DOMImpl.java
@@ -81,6 +81,7 @@
import org.xml.sax.*;
import org.xml.sax.ext.*;
+import org.xml.sax.helpers.AttributesImpl;
import org.apache.xalan.xsltc.*;
import org.apache.xalan.xsltc.util.IntegerArray;
import org.apache.xalan.xsltc.runtime.BasisLibrary;
@@ -136,8 +137,12 @@
// Tracks which textnodes are whitespaces and which are not
private BitArray _whitespace; // takes xml:space into acc.
+ // Tracks which textnodes are not escaped
+ private BitArray _dontEscape = null;
+
// The URI to this document
- private String _documentURI;
+ private String _documentURI = null;
+ static private int _documentURIIndex = 0;
// Support for access/navigation through org.w3c.dom API
private Node[] _nodes;
@@ -159,11 +164,11 @@
* Returns the origin of the document from which the tree was built
*/
public String getDocumentURI() {
- return(_documentURI);
+ return (_documentURI != null) ? _documentURI : "rtf" + _documentURIIndex++;
}
public String getDocumentURI(int node) {
- return(_documentURI);
+ return getDocumentURI();
}
public void setupMapping(String[] names, String[] namespaces) {
@@ -171,6 +176,37 @@
}
/**
+ * Lookup a namespace URI from a prefix starting at node. This method
+ * is used in the execution of xsl:element when the prefix is not known
+ * at compile time.
+ */
+ public String lookupNamespace(int node, String prefix)
+ throws TransletException
+ {
+ int anode, nsnode;
+ final AncestorIterator ancestors = new AncestorIterator();
+
+ if (isElement(node)) {
+ ancestors.includeSelf();
+ }
+
+ ancestors.setStartNode(node);
+ while ((anode = ancestors.next()) != NULL) {
+ final NodeIterator namespaces =
+ new NamespaceIterator().setStartNode(anode);
+
+ while ((nsnode = namespaces.next()) != NULL) {
+ if (_prefixArray[_prefix[nsnode]].equals(prefix)) {
+ return getNodeValue(nsnode);
+ }
+ }
+ }
+
+ // TODO: Internationalization?
+ throw new TransletException("Namespace prefix '" + prefix + "' is undeclared.");
+ }
+
+ /**
* Returns 'true' if a specific node is an element (of any type)
*/
public boolean isElement(final int node) {
@@ -1053,6 +1089,7 @@
while ((_ns == DOM.NULL) && (_node != DOM.NULL)) {
_node = _parent[_node];
_ns = _lengthOrAttr[_node];
+
while ((_ns != DOM.NULL) && (_type[_ns] != NAMESPACE)) {
_ns = _nextSibling[_ns];
}
@@ -1482,7 +1519,9 @@
public int next() {
while (++_node < _limit) {
- if (_type[_node] > TEXT) return(returnNode(_node));
+ if (_type[_node] > TEXT) {
+ return(returnNode(_node));
+ }
}
return(NULL);
}
@@ -1980,6 +2019,7 @@
case ROOT:
return getNodeValue(_offsetOrChild[node]);
case TEXT:
+ // GTM - add escapign code here too.
case COMMENT:
return makeStringValue(node);
case PROCESSING_INSTRUCTION:
@@ -2162,6 +2202,13 @@
out.writeObject(_whitespace);
+ if (_dontEscape != null) {
+ out.writeObject(_dontEscape);
+ }
+ else {
+ out.writeObject(new BitArray(0));
+ }
+
out.flush();
}
@@ -2190,6 +2237,11 @@
_whitespace = (BitArray)in.readObject();
+ _dontEscape = (BitArray)in.readObject();
+ if (_dontEscape.size() == 0) {
+ _dontEscape = null;
+ }
+
_types = setupMapping(_namesArray);
}
@@ -2614,9 +2666,23 @@
_lengthOrAttr[node]));
break;
case TEXT:
+ boolean last = false;
+ boolean escapeBit = false;
+
+ if (_dontEscape != null) {
+ escapeBit = _dontEscape.getBit(node);
+ if (escapeBit) {
+ last = handler.setEscaping(false);
+ }
+ }
+
handler.characters(_text,
_offsetOrChild[node],
_lengthOrAttr[node]);
+
+ if (_dontEscape != null && escapeBit) {
+ handler.setEscaping(last);
+ }
break;
case ATTRIBUTE:
shallowCopy(node, handler);
@@ -2628,6 +2694,7 @@
if (isElement(node)) {
// Start element definition
final String name = copyElement(node, type, handler);
+
// Copy element attribute
for (int a=_lengthOrAttr[node]; a!=NULL; a=_nextSibling[a]) {
if (_type[a] != NAMESPACE) {
@@ -2694,9 +2761,11 @@
case ROOT: // do nothing
return EMPTYSTRING;
case TEXT:
+
handler.characters(_text,
_offsetOrChild[node],
_lengthOrAttr[node]);
+
return null;
case PROCESSING_INSTRUCTION:
copyPI(node, handler);
@@ -2899,8 +2968,7 @@
* DOM builder's interface is pure SAX2 (must investigate)
*/
public TransletOutputHandler getOutputDomBuilder() {
- DOMBuilder builder = getBuilder();
- return new SAXAdapter(builder, builder);
+ return new SAXAdapter(new DOMBuilderImpl());
}
/**
@@ -2959,6 +3027,11 @@
private static final String XML_STRING = "xml:";
private static final String XMLSPACE_STRING = "xml:space";
private static final String PRESERVE_STRING = "preserve";
+ private static final String XML_PREFIX = "xml";
+ private static final String XMLNS_PREFIX = "xmlns";
+
+ private boolean _escaping = true;
+ private boolean _disableEscaping = false;
/**
* Default constructor for the DOMBuiler class
@@ -3076,7 +3149,6 @@
*/
private short makeElementNode(String uri, String localname)
throws SAXException {
-
final String name;
if (uri != EMPTYSTRING)
name = uri + ':' + localname;
@@ -3170,6 +3242,7 @@
*/
private int makeTextNode(boolean isWhitespace) {
if (_currentOffset > _baseOffset) {
+
final int node = nextNode();
final int limit = _currentOffset;
// Tag as whitespace node if the parser tells us that it is...
@@ -3188,6 +3261,14 @@
_type[node] = TEXT;
linkChildren(node);
storeTextRef(node);
+
+ if (_disableEscaping) {
+ if (_dontEscape == null) {
+ _dontEscape = new BitArray(_whitespace.size());
+ }
+ _dontEscape.setBit(node);
+ _disableEscaping = false;
+ }
return node;
}
return -1;
@@ -3217,33 +3298,34 @@
* Creates an attribute node
*/
private int makeAttributeNode(int parent, Attributes attList, int i)
- throws SAXException {
-
+ throws SAXException
+ {
final int node = nextAttributeNode();
-
final String qname = attList.getQName(i);
- final String localname = attList.getLocalName(i);
+ String localName = attList.getLocalName(i);
final String value = attList.getValue(i);
StringBuffer namebuf = new StringBuffer(EMPTYSTRING);
- // Create the internal attribute node name (uri+@+localname)
- if (qname.startsWith(XML_STRING)) {
- if (qname.startsWith(XMLSPACE_STRING))
- xmlSpaceDefine(attList.getValue(i), parent);
+ if (qname.startsWith(XMLSPACE_STRING)) {
+ xmlSpaceDefine(attList.getValue(i), parent);
}
+
+ // If local name is null set it to the empty string
+ if (localName == null) {
+ localName = EMPTYSTRING;
+ }
+
+ // Create the internal attribute node name (uri+@+localname)
final String uri = attList.getURI(i);
- if ((uri != null) && (!uri.equals(EMPTYSTRING))) {
+ if (uri != null && !uri.equals(EMPTYSTRING)) {
namebuf.append(uri);
namebuf.append(':');
}
namebuf.append('@');
- if (localname != null )
- namebuf.append(localname);
- else
- namebuf.append(qname);
+ namebuf.append(localName.length() > 0 ? localName : qname);
String name = namebuf.toString();
-
+
// Get the index of the attribute node name (create new if non-ex).
Integer obj = (Integer)_names.get(name);
if (obj == null) {
@@ -3280,6 +3362,9 @@
}
System.arraycopy(ch, start, _text, _currentOffset, length);
_currentOffset += length;
+
+ _disableEscaping = !_escaping;
+
}
/**
@@ -3295,7 +3380,7 @@
_type2[0] = NAMESPACE;
startPrefixMapping(EMPTYSTRING, EMPTYSTRING);
- startPrefixMapping("xml", "http://www.w3.org/XML/1998/namespace");
+ startPrefixMapping(XML_PREFIX, "http://www.w3.org/XML/1998/namespace");
_lengthOrAttr[ROOTNODE] = _nextNamespace;
_parent2[_nextNamespace] = ROOTNODE;
_nextNamespace = DOM.NULL;
@@ -3374,41 +3459,61 @@
_lengthOrAttr[node] = DOM.NULL;
+ int last = -1;
final int count = attributes.getLength();
// Append any namespace nodes
if (_nextNamespace != DOM.NULL) {
_lengthOrAttr[node] = _nextNamespace;
+
while (_nextNamespace != DOM.NULL) {
_parent2[_nextNamespace] = node;
- int tail = _nextNamespace;
- _nextNamespace = _nextSibling2[_nextNamespace];
+ _nextNamespace = _nextSibling2[last = _nextNamespace];
// Chain last namespace node to following attribute node(s)
- if ((_nextNamespace == DOM.NULL) && (count > 0))
- _nextSibling2[tail] = _currentAttributeNode;
+ if (_nextNamespace == DOM.NULL && count > 0) {
+ _nextSibling2[last] = _currentAttributeNode;
+ }
}
}
+ // If local name is null set it to the empty string
+ if (localName == null) {
+ localName = EMPTYSTRING;
+ }
+
// Append any attribute nodes
+ boolean attrsAdded = false;
if (count > 0) {
int attr = _currentAttributeNode;
- if (_lengthOrAttr[node] == DOM.NULL)
+ if (_lengthOrAttr[node] == DOM.NULL) {
_lengthOrAttr[node] = attr;
- for (int i = 0; i<count; i++) {
- attr = makeAttributeNode(node, attributes, i);
- _parent2[attr] = node;
- _nextSibling2[attr] = attr + 1;
}
- _nextSibling2[attr] = DOM.NULL;
+ for (int i = 0; i < count; i++) {
+ if (!attributes.getQName(i).startsWith(XMLNS_PREFIX)) {
+ attr = makeAttributeNode(node, attributes, i);
+ _parent2[attr] = node;
+ _nextSibling2[attr] = attr + 1;
+ attrsAdded = true;
+ }
+ }
+ // Did we append namespace nodes only?
+ if (!attrsAdded && last != -1) {
+ _nextSibling2[last] = DOM.NULL;
+ }
+ else {
+ _nextSibling2[attr] = DOM.NULL;
+ }
}
final int col = qname.lastIndexOf(':');
// Assign an internal type to this element (may exist)
- if ((uri != null) && (localName.length() > 0))
+ if (uri != null && localName.length() > 0) {
_type[node] = makeElementNode(uri, localName);
- else
+ }
+ else {
_type[node] = makeElementNode(qname, col);
+ }
// Assign an internal type to the element's prefix (may exist)
if (col > -1) {
@@ -3502,7 +3607,8 @@
else
_nextSibling2[attr-1] = attr;
_nextSibling2[attr] = DOM.NULL;
- _prefix2[attr] = idx.shortValue();
+ // _prefix2[attr] = idx.shortValue();
+ _prefix2[attr] = ((Integer) stack.elementAt(0)).shortValue();
}
}
@@ -3587,6 +3693,11 @@
// Resize the '_whitespace' array (a BitArray instance)
_whitespace.resize(newSize);
+ // Resize the '_dontEscape' array (a BitArray instance)
+ if (_dontEscape != null) {
+ _dontEscape.resize(newSize);
+ }
+
// Resize the '_prefix' array
final short[] newPrefix = new short[newSize];
System.arraycopy(_prefix, 0, newPrefix, 0, length);
@@ -3656,5 +3767,11 @@
}
}
+ public boolean setEscaping(boolean value) {
+ final boolean temp = _escaping;
+ _escaping = value;
+ return temp;
+ }
+
} // end of DOMBuilder
}
diff --git a/src/org/apache/xalan/xsltc/dom/ExtendedSAX.java b/src/org/apache/xalan/xsltc/dom/ExtendedSAX.java
new file mode 100644
index 0000000..8e87428
--- /dev/null
+++ b/src/org/apache/xalan/xsltc/dom/ExtendedSAX.java
@@ -0,0 +1,69 @@
+/*
+ * @(#)$Id$
+ *
+ * The Apache Software License, Version 1.1
+ *
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Xalan" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation and was
+ * originally based on software copyright (c) 2001, Sun
+ * Microsystems., http://www.sun.com. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ * @author Morten Jorgensen
+ *
+ */
+package org.apache.xalan.xsltc.dom;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.ext.LexicalHandler;
+
+public interface ExtendedSAX extends ContentHandler, LexicalHandler {
+ public boolean setEscaping(boolean escape);
+}
diff --git a/src/org/apache/xalan/xsltc/dom/MultiDOM.java b/src/org/apache/xalan/xsltc/dom/MultiDOM.java
index 95b7b87..42f9f19 100644
--- a/src/org/apache/xalan/xsltc/dom/MultiDOM.java
+++ b/src/org/apache/xalan/xsltc/dom/MultiDOM.java
@@ -114,6 +114,7 @@
public NodeIterator setStartNode(final int node) {
_mask = node & SET;
int dom = node >>> 24;
+
// consider caching these
if ((_type == NO_TYPE) || (_type == DOM.ELEMENT)) {
_source = _adapters[dom].getAxisIterator(_axis);
@@ -262,8 +263,7 @@
// This method only has a function in DOM adapters
}
- public int addDOMAdapter(DOM dom) {
-
+ public int addDOMAdapter(DOMAdapter dom) {
// Add the DOM adapter to the array of DOMs
final int domNo = _free++;
if (domNo == _size) {
@@ -275,9 +275,11 @@
// Store reference to document (URI) in hashtable
String uri = dom.getDocumentURI(0);
- _documents.put(uri,new Integer(domNo));
+ _documents.put(uri, new Integer(domNo));
- return domNo << 24;
+ // Store mask in DOMAdapter
+ dom.setMultiDOMMask(domNo << 24);
+ return (domNo << 24);
}
public int getDocumentMask(String uri) {
@@ -453,4 +455,9 @@
return(_adapters[node>>>24].isAttribute(node & CLR));
}
+ public String lookupNamespace(int node, String prefix)
+ throws TransletException
+ {
+ return _adapters[node>>>24].lookupNamespace(node, prefix);
+ }
}
diff --git a/src/org/apache/xalan/xsltc/dom/SortingIterator.java b/src/org/apache/xalan/xsltc/dom/SortingIterator.java
index 9d16c2b..e2ffd9e 100644
--- a/src/org/apache/xalan/xsltc/dom/SortingIterator.java
+++ b/src/org/apache/xalan/xsltc/dom/SortingIterator.java
@@ -66,11 +66,12 @@
import org.apache.xalan.xsltc.NodeIterator;
import org.apache.xalan.xsltc.TransletException;
+import org.apache.xalan.xsltc.runtime.BasisLibrary;
public final class SortingIterator extends NodeIteratorBase {
private final static int INIT_DATA_SIZE = 16;
- private final NodeIterator _source;
- private final NodeSortRecordFactory _factory;
+ private NodeIterator _source;
+ private NodeSortRecordFactory _factory;
private NodeSortRecord[] _data;
private int _free = 0;
private int _current; // index in _nodes of the next node to try
@@ -124,6 +125,29 @@
_current = _markedNode;
}
+ /**
+ * Clone a <code>SortingIterator</code> by cloning its source
+ * iterator and then sharing the factory and the array of
+ * <code>NodeSortRecords</code>.
+ */
+ public NodeIterator cloneIterator() {
+ try {
+ final SortingIterator clone = (SortingIterator) super.clone();
+ clone._source = _source.cloneIterator();
+ clone._factory = _factory; // shared between clones
+ clone._data = _data; // shared between clones
+ clone._free = _free;
+ clone._current = _current;
+ clone.setRestartable(false);
+ return clone.reset();
+ }
+ catch (CloneNotSupportedException e) {
+ BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
+ e.toString());
+ return null;
+ }
+ }
+
private void addRecord(NodeSortRecord record) {
if (_free == _data.length) {
NodeSortRecord[] newArray = new NodeSortRecord[_data.length * 2];
diff --git a/src/org/apache/xalan/xsltc/dom/UnionIterator.java b/src/org/apache/xalan/xsltc/dom/UnionIterator.java
index bfd1452..be22cd1 100644
--- a/src/org/apache/xalan/xsltc/dom/UnionIterator.java
+++ b/src/org/apache/xalan/xsltc/dom/UnionIterator.java
@@ -152,8 +152,10 @@
final int smallest = _heap[0].node;
if (smallest == END) { // iterator _heap[0] is done
if (_heapSize > 1) {
- // replace it with last
+ // Swap first and last (iterator must be restartable)
+ final LookAheadIterator temp = _heap[0];
_heap[0] = _heap[--_heapSize];
+ _heap[_heapSize] = temp;
}
else {
return END;
diff --git a/src/org/apache/xalan/xsltc/runtime/AbstractTranslet.java b/src/org/apache/xalan/xsltc/runtime/AbstractTranslet.java
index e363410..9b4c431 100644
--- a/src/org/apache/xalan/xsltc/runtime/AbstractTranslet.java
+++ b/src/org/apache/xalan/xsltc/runtime/AbstractTranslet.java
@@ -143,44 +143,15 @@
*/
public final void popParamFrame() {
if (pbase > 0) {
- int bot = pbase - 1;
- int top = pframe - 1;
- pframe = pbase - 1;
- pbase = ((Integer) paramsStack.elementAt(pframe)).intValue();
- // bug fix #3424, John Howard.
- // remove objects that are in the stack since objects are
- // added with insertElementAt(int) and will cause memory retention
- for (int i=top; i>=bot; i--) {
- paramsStack.removeElementAt(i);
+ final int oldpbase = ((Integer)paramsStack.elementAt(--pbase)).intValue();
+ for (int i = pbase; i < pframe; i++) {
+ paramsStack.setElementAt(null, i); // for the GC
}
+ pframe = pbase; pbase = oldpbase;
}
}
/**
- * Replace a certain character in a string with a new substring.
- */
- private static String replace(String base, char c, String str) {
- final int len = base.length() - 1;
- int pos;
- while ((pos = base.indexOf(c)) > -1) {
- if (pos == 0) {
- final String after = base.substring(1);
- base = str + after;
- }
- else if (pos == len) {
- final String before = base.substring(0, pos);
- base = before + str;
- }
- else {
- final String before = base.substring(0, pos);
- final String after = base.substring(pos+1);
- base = before + str + after;
- }
- }
- return base;
- }
-
- /**
* Add a new global parameter if not already in the current frame.
*/
public final Object addParameter(String name, Object value) {
@@ -224,6 +195,7 @@
* Clears the parameter stack.
*/
public void clearParameters() {
+ pbase = pframe = 0;
paramsStack.clear();
}
@@ -246,6 +218,7 @@
varsStack.insertElementAt(new Integer(vbase), vframe);
vbase = ++vframe;
vframe += frameSize;
+ varsStack.setSize(vframe + 1); // clear stack frame
}
/**
@@ -253,16 +226,11 @@
*/
public final void popVarFrame() {
if (vbase > 0) {
- int bot = vbase - 1;
- int top = vframe - 1;
- vframe = vbase - 1;
- vbase = ((Integer)varsStack.elementAt(vframe)).intValue();
- // bug fix 3424, John Howard
- // remove objects that are in the stack since objects are
- // added with insertElementAt(int) and will cause memory retention
- for (int i=top; i>=bot; i--) {
- varsStack.removeElementAt(i);
+ final int oldvbase = ((Integer)varsStack.elementAt(--vbase)).intValue();
+ for (int i = vbase; i < vframe; i++) {
+ varsStack.setElementAt(null, i); // for the GC
}
+ vframe = vbase; vbase = oldvbase;
}
}
@@ -270,18 +238,38 @@
* Get the value of a variable given its index.
*/
public final Object getVariable(int vindex) {
- // bug fix 3424, John Howard
return varsStack.elementAt(vbase + vindex);
}
-
/**
* Set the value of a variable in the current frame.
*/
public final void addVariable(int vindex, Object value) {
- final int index = vbase + vindex;
- if (index > varsStack.size()) varsStack.setSize(index);
- varsStack.insertElementAt(value, index);
+ varsStack.setElementAt(value, vbase + vindex);
+ }
+
+ /**
+ * Replace a certain character in a string with a new substring.
+ */
+ private static String replace(String base, char c, String str) {
+ final int len = base.length() - 1;
+ int pos;
+ while ((pos = base.indexOf(c)) > -1) {
+ if (pos == 0) {
+ final String after = base.substring(1);
+ base = str + after;
+ }
+ else if (pos == len) {
+ final String before = base.substring(0, pos);
+ base = before + str;
+ }
+ else {
+ final String before = base.substring(0, pos);
+ final String after = base.substring(pos+1);
+ base = before + str + after;
+ }
+ }
+ return base;
}
/************************************************************************
@@ -582,10 +570,10 @@
// Transfer all settings relevant to XML output
if (_method.equals("xml")) {
if (_standalone != null) handler.setStandalone(_standalone);
+ if (_omitHeader) handler.omitHeader(true);
handler.setType(TextOutput.XML);
handler.setCdataElements(_cdata);
if (_version != null) handler.setVersion(_version);
- if (_omitHeader) handler.omitHeader(true);
handler.setIndent(_indent);
if (_doctypeSystem != null)
handler.setDoctype(_doctypeSystem, _doctypePublic);
diff --git a/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java b/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java
index 18f223b..a623187 100644
--- a/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java
+++ b/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java
@@ -79,6 +79,10 @@
import org.apache.xalan.xsltc.*;
import org.apache.xalan.xsltc.DOM;
import org.apache.xalan.xsltc.NodeIterator;
+import org.apache.xalan.xsltc.dom.Axis;
+import org.apache.xalan.xsltc.dom.DOMAdapter;
+import org.apache.xalan.xsltc.dom.MultiDOM;
+import org.apache.xalan.xsltc.dom.AbsoluteIterator;
import org.apache.xalan.xsltc.dom.SingletonIterator;
/**
@@ -427,7 +431,7 @@
if (name.equals("xsl:version"))
return("1.0");
if (name.equals("xsl:vendor"))
- return("Apache Xalan XSLTC");
+ return("Apache Software Foundation");
if (name.equals("xsl:vendor-url"))
return("http://xml.apache.org/xalan-j");
@@ -447,6 +451,15 @@
return EMPTYSTRING;
}
+ /**
+ * Implements the nodeset() extension function.
+ */
+ public static NodeIterator nodesetF(DOM rtf) {
+ final DOMAdapter adapter = (DOMAdapter) rtf;
+ return new SingletonIterator(
+ DOM.ROOTNODE | adapter.getMultiDOMMask(), true);
+ }
+
//-- Begin utility functions
private static boolean isWhiteSpace(char ch) {
@@ -891,28 +904,31 @@
* obj is an instanceof Node then create a singleton iterator.
*/
public static NodeIterator referenceToNodeSet(Object obj) {
- try {
- // Convert var/param -> node
- if (obj instanceof Node) {
- return(new SingletonIterator(((Node)obj).node));
- }
- // Convert var/param -> node-set
- else if (obj instanceof NodeIterator) {
- return(((NodeIterator)obj).cloneIterator());
- }
- // Convert var/param -> result-tree fragment
- else if (obj instanceof DOM) {
- DOM dom = (DOM)obj;
- return(dom.getIterator());
- }
- else {
- final String className = obj.getClass().getName();
- runTimeError(DATA_CONVERSION_ERR, "reference", className);
- return null;
- }
+ // Convert var/param -> node
+ if (obj instanceof Node) {
+ return(new SingletonIterator(((Node)obj).node));
}
- catch (ClassCastException e) {
- runTimeError(DATA_CONVERSION_ERR, "reference", "node-set");
+ // Convert var/param -> node-set
+ else if (obj instanceof NodeIterator) {
+ return(((NodeIterator)obj).cloneIterator());
+ }
+ else {
+ final String className = obj.getClass().getName();
+ runTimeError(DATA_CONVERSION_ERR, "reference", className);
+ return null;
+ }
+ }
+
+ /**
+ * Utility function used to convert references to DOMs.
+ */
+ public static DOM referenceToResultTree(Object obj) {
+ try {
+ return ((DOM) obj);
+ }
+ catch (IllegalArgumentException e) {
+ final String className = obj.getClass().getName();
+ runTimeError(DATA_CONVERSION_ERR, "reference", className);
return null;
}
}
@@ -960,6 +976,29 @@
}
}
+ /**
+ * This function is used in the execution of xsl:element
+ */
+ public static String getPrefix(String qname) {
+ final int index = qname.indexOf(':');
+ return (index > 0) ? qname.substring(0, index) : null;
+ }
+
+ /**
+ * This function is used in the execution of xsl:element
+ */
+ private static int prefixIndex = 0;
+ public static String generatePrefix() {
+ return ("ns" + prefixIndex++);
+ }
+
+ /**
+ * This function is used in the execution of xsl:element
+ */
+ public static String makeQName(String localName, String prefix) {
+ return (new StringBuffer(prefix).append(':').append(localName).toString());
+ }
+
public static final int RUN_TIME_INTERNAL_ERR = 0;
public static final int RUN_TIME_COPY_ERR = 1;
public static final int DATA_CONVERSION_ERR = 2;
diff --git a/src/org/apache/xalan/xsltc/runtime/Constants.java b/src/org/apache/xalan/xsltc/runtime/Constants.java
index 4f72e23..da2bdb3 100644
--- a/src/org/apache/xalan/xsltc/runtime/Constants.java
+++ b/src/org/apache/xalan/xsltc/runtime/Constants.java
@@ -83,4 +83,9 @@
public static final String NAMESPACE_FEATURE =
"http://xml.org/sax/features/namespaces";
+ public static final String EMPTYSTRING = "";
+ public static final String XML_PREFIX = "xml";
+ public static final String XMLNS_PREFIX = "xmlns";
+ public static final String XMLNS_STRING = "xmlns:";
+ public static final String XMLNS_URI = "http://www.w3.org/2000/xmlns/";
}
diff --git a/src/org/apache/xalan/xsltc/runtime/DefaultSAXOutputHandler.java b/src/org/apache/xalan/xsltc/runtime/DefaultSAXOutputHandler.java
index 37e3c3e..b3d356f 100644
--- a/src/org/apache/xalan/xsltc/runtime/DefaultSAXOutputHandler.java
+++ b/src/org/apache/xalan/xsltc/runtime/DefaultSAXOutputHandler.java
@@ -63,6 +63,8 @@
package org.apache.xalan.xsltc.runtime;
+import java.util.Vector;
+
import java.io.IOException;
import java.io.Writer;
import java.io.FileOutputStream;
@@ -122,7 +124,7 @@
private int _indentLevel = 0;
// This is used for aggregating namespace declarations
- private AttributeList _namespaceDeclarations = new AttributeList();
+ private Vector _namespaceDecls = null;
/**
* Constructor - set Writer to send output to and output encoding
@@ -239,7 +241,8 @@
/**
* SAX2: Receive notification of the beginning of a document.
*/
- public void startDocument() throws SAXException { }
+ public void startDocument() throws SAXException {
+ }
/**
* SAX2: Receive notification of the end of an element.
@@ -266,7 +269,7 @@
if (_startTagOpen) closeStartTag(true); // Close any open element.
_element = elementName; // Save element name
- // Handle indentation (not a requirement)
+ // Handle inden3dcb50483dcb504tation (not a requirement)
if (_indent) {
if (!_emptyElements.containsKey(elementName.toLowerCase())) {
indent(_lineFeedNextStartTag);
@@ -283,24 +286,26 @@
_indentNextEndTag = false;
// Output namespace declarations first...
- int declCount = _namespaceDeclarations.getLength();
- for (int i=0; i<declCount; i++) {
- final String prefix = _namespaceDeclarations.getQName(i);
- _writer.write(XMLNS);
- if ((prefix != null) && (prefix != EMPTYSTRING)) {
- _writer.write(':');
- _writer.write(prefix);
+ if (_namespaceDecls != null) {
+ int nDecls = _namespaceDecls.size();
+ for (int i = 0; i < nDecls; i++) {
+ final String prefix = (String) _namespaceDecls.elementAt(i++);
+ _writer.write(XMLNS);
+ if ((prefix != null) && (prefix != EMPTYSTRING)) {
+ _writer.write(':');
+ _writer.write(prefix);
+ }
+ _writer.write('=');
+ _writer.write('\"');
+ _writer.write((String) _namespaceDecls.elementAt(i));
+ _writer.write('\"');
}
- _writer.write('=');
- _writer.write('\"');
- _writer.write(_namespaceDeclarations.getValue(i));
- _writer.write('\"');
- }
- _namespaceDeclarations.clear();
+ _namespaceDecls.clear();
+ }
// ...then output all attributes
int attrCount = attrs.getLength();
- for (int i=0; i<attrCount; i++) {
+ for (int i = 0; i < attrCount; i++) {
_writer.write(' ');
_writer.write(attrs.getQName(i));
_writer.write('=');
@@ -440,7 +445,11 @@
* Namespace declarations are output in startElement()
*/
public void startPrefixMapping(String prefix, String uri) {
- _namespaceDeclarations.add(prefix,uri);
+ if (_namespaceDecls == null) {
+ _namespaceDecls = new Vector(2);
+ }
+ _namespaceDecls.addElement(prefix);
+ _namespaceDecls.addElement(uri);
}
/**
diff --git a/src/org/apache/xalan/xsltc/runtime/SAXAdapter.java b/src/org/apache/xalan/xsltc/runtime/SAXAdapter.java
index ae0e891..8642def 100644
--- a/src/org/apache/xalan/xsltc/runtime/SAXAdapter.java
+++ b/src/org/apache/xalan/xsltc/runtime/SAXAdapter.java
@@ -65,37 +65,32 @@
package org.apache.xalan.xsltc.runtime;
import org.xml.sax.*;
-import org.xml.sax.ext.LexicalHandler;
-import org.apache.xalan.xsltc.*;
+import org.xml.sax.ext.*;
+import org.apache.xalan.xsltc.TransletException;
+import org.apache.xalan.xsltc.TransletOutputHandler;
+import org.apache.xalan.xsltc.dom.DOMBuilder;
public final class SAXAdapter implements TransletOutputHandler {
- private final ContentHandler _saxHandler;
- private final LexicalHandler _lexHandler;
+ private final DOMBuilder _domBuilder;
private final AttributeList _attributes = new AttributeList();
private String _openElementName;
- public SAXAdapter(ContentHandler saxHandler) {
- _saxHandler = saxHandler;
- _lexHandler = null;
- }
-
- public SAXAdapter(ContentHandler saxHandler, LexicalHandler lexHandler) {
- _saxHandler = saxHandler;
- _lexHandler = lexHandler;
+ public SAXAdapter(DOMBuilder domBuilder) {
+ _domBuilder = domBuilder;
}
private void maybeEmitStartElement() throws SAXException {
if (_openElementName != null) {
- _saxHandler.startElement(null, null, _openElementName, _attributes);
+ _domBuilder.startElement(null, null, _openElementName, _attributes);
_openElementName = null;
}
}
public void startDocument() throws TransletException {
try {
- _saxHandler.startDocument();
+ _domBuilder.startDocument();
}
catch (SAXException e) {
throw new TransletException(e);
@@ -104,7 +99,7 @@
public void endDocument() throws TransletException {
try {
- _saxHandler.endDocument();
+ _domBuilder.endDocument();
}
catch (SAXException e) {
throw new TransletException(e);
@@ -115,7 +110,7 @@
throws TransletException {
try {
maybeEmitStartElement();
- _saxHandler.characters(characters, offset, length);
+ _domBuilder.characters(characters, offset, length);
}
catch (SAXException e) {
throw new TransletException(e);
@@ -136,7 +131,7 @@
public void endElement(String elementName) throws TransletException {
try {
maybeEmitStartElement();
- _saxHandler.endElement(null, null, elementName);
+ _domBuilder.endElement(null, null, elementName);
}
catch (SAXException e) {
throw new TransletException(e);
@@ -161,9 +156,9 @@
public void comment(String comment) throws TransletException {
try {
maybeEmitStartElement();
- if (_lexHandler != null) {
+ if (_domBuilder != null) {
char[] chars = comment.toCharArray();
- _lexHandler.comment(chars, 0, chars.length);
+ _domBuilder.comment(chars, 0, chars.length);
}
}
catch (SAXException e) {
@@ -175,13 +170,17 @@
throws TransletException {
try {
maybeEmitStartElement();
- _saxHandler.processingInstruction(target, data);
+ _domBuilder.processingInstruction(target, data);
}
catch (SAXException e) {
throw new TransletException(e);
}
}
+ public boolean setEscaping(boolean escape) throws TransletException {
+ return _domBuilder.setEscaping(escape);
+ }
+
// The SAX handler does not handle these:
public void setType(int type) {}
public void setHeader(String header) {}
@@ -189,8 +188,5 @@
public void omitHeader(boolean value) {}
public void setCdataElements(Hashtable elements) { }
public void close() {}
- public boolean setEscaping(boolean escape) throws TransletException {
- return(true);
- }
public String getPrefix(String uri) { return(""); }
}
diff --git a/src/org/apache/xalan/xsltc/runtime/StringValueHandler.java b/src/org/apache/xalan/xsltc/runtime/StringValueHandler.java
index 2e47eb4..cd0f76e 100644
--- a/src/org/apache/xalan/xsltc/runtime/StringValueHandler.java
+++ b/src/org/apache/xalan/xsltc/runtime/StringValueHandler.java
@@ -86,4 +86,29 @@
_free = 0; // getValue resets
return new String(_buffer, 0, length);
}
+
+ /**
+ * The value of a PI must not contain the substring "?>". Should
+ * that substring be present, replace it by "? >".
+ */
+ public String getValueOfPI() {
+ final String value = getValue();
+
+ if (value.indexOf("?>") > 0) {
+ final int n = value.length();
+ final StringBuffer valueOfPI = new StringBuffer();
+
+ for (int i = 0; i < n;) {
+ final char ch = value.charAt(i++);
+ if (ch == '?' && value.charAt(i) == '>') {
+ valueOfPI.append("? >"); i++;
+ }
+ else {
+ valueOfPI.append(ch);
+ }
+ }
+ return valueOfPI.toString();
+ }
+ return value;
+ }
}
diff --git a/src/org/apache/xalan/xsltc/runtime/TextOutput.java b/src/org/apache/xalan/xsltc/runtime/TextOutput.java
index fd29db7..22eb12c 100644
--- a/src/org/apache/xalan/xsltc/runtime/TextOutput.java
+++ b/src/org/apache/xalan/xsltc/runtime/TextOutput.java
@@ -72,10 +72,11 @@
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.AttributesImpl;
import org.apache.xalan.xsltc.*;
-public final class TextOutput implements TransletOutputHandler {
+public final class TextOutput implements TransletOutputHandler, Constants {
// These are the various output types we handle
public static final int UNKNOWN = 0; // determine type from output contents
@@ -96,12 +97,11 @@
private boolean _startTagOpen = false;
private boolean _headTagOpen = false;
private boolean _cdataTagOpen = false;
+ private boolean _is8859Encoded = false;
// Contains all elements that should be output as CDATA sections
private Hashtable _cdata = null;
- private static final String XML_PREFIX = "xml";
-
private static final char[] AMP = "&".toCharArray();
private static final char[] LT = "<".toCharArray();
private static final char[] GT = ">".toCharArray();
@@ -124,15 +124,15 @@
private static final int BEGCOMM_length = BEGCOMM.length;
private static final int ENDCOMM_length = ENDCOMM.length;
- private static final String EMPTYSTRING = "";
private static final String HREF_STR = "href";
- private static final String SRC_STR = "str";
+ private static final String CITE_STR = "cite";
+ private static final String SRC_STR = "src";
private static final String CHAR_ESC_START = "&#";
private static final String CDATA_ESC_START = "]]>&#";
private static final String CDATA_ESC_END = ";<![CDATA[";
- private AttributeList _attributes = new AttributeList();
- private String _elementName = null;
+ private AttributesImpl _attributes = new AttributesImpl();
+ private String _elementName = null;
// Each entry (prefix) in this hashtable points to a Stack of URIs
private Hashtable _namespaces;
@@ -179,6 +179,7 @@
_saxHandler = handler;
init();
_encoding = encoding;
+ _is8859Encoded = _encoding.equalsIgnoreCase("iso-8859-1");
}
/**
@@ -194,6 +195,7 @@
_lexHandler = lex;
init();
_encoding = encoding;
+ _is8859Encoded = _encoding.equalsIgnoreCase("iso-8859-1");
}
/**
@@ -252,8 +254,8 @@
AttributeList attrs = new AttributeList();
attrs.add("http-equiv", "Content-Type");
attrs.add("content", _mediaType+"; charset="+_encoding);
- _saxHandler.startElement(null, null, "meta", attrs);
- _saxHandler.endElement(null, null, "meta");
+ _saxHandler.startElement(EMPTYSTRING, EMPTYSTRING, "meta", attrs);
+ _saxHandler.endElement(EMPTYSTRING, EMPTYSTRING, "meta");
}
}
@@ -264,27 +266,10 @@
public void closeStartTag() throws TransletException {
try {
_startTagOpen = false;
-
- // Final check to assure that the element is within a namespace
- // that has been declared (all declarations for this element
- // should have been processed at this point).
- int col = _elementName.lastIndexOf(':');
- if (col > 0) {
- final String prefix = _elementName.substring(0,col);
- final String localname = _elementName.substring(col+1);
- final String uri = lookupNamespace(prefix);
- if (uri == null)
- BasisLibrary.runTimeError(BasisLibrary.NAMESPACE_PREFIX_ERR,
- prefix);
- if (uri.equals(EMPTYSTRING)) _elementName = localname;
- _saxHandler.startElement(uri, localname,
- _elementName, _attributes);
- }
- else {
- final String uri = lookupNamespace(EMPTYSTRING);
- _saxHandler.startElement(uri, _elementName,
- _elementName, _attributes);
- }
+
+ // Now is time to send the startElement event
+ _saxHandler.startElement(getNamespaceURI(_elementName, true),
+ getLocalName(_elementName), _elementName, _attributes);
// Insert <META> tag directly after <HEAD> element in HTML output
if (_headTagOpen) {
@@ -417,17 +402,19 @@
// the first CDATA and '>' at the beginning of the next. Other
// special characters/sequences are _NOT_ escaped within CDATA.
Integer I = (Integer)_cdataStack.peek();
- if ((I.intValue() == _depth) && (!_cdataTagOpen))
+ if ((I.intValue() == _depth) && (!_cdataTagOpen)) {
startCDATA(ch, off, len);
- // Output characters escaped if required.
- else if (_escapeChars)
- if (_cdataTagOpen)
+ }
+ else if (_escapeChars) {
+ if (_cdataTagOpen) {
escapeCDATA(ch, off, len);
- else
+ } else {
escapeCharacters(ch, off, len);
- // Output the chracters as the are if not.
- else
+ }
+ }
+ else {
_saxHandler.characters(ch, off, len);
+ }
return;
case HTML:
@@ -467,9 +454,7 @@
* Start an element in the output document. This might be an XML
* element (<elem>data</elem> type) or a CDATA section.
*/
- public void startElement(String elementName)
- throws TransletException {
-
+ public void startElement(String elementName) throws TransletException {
try {
switch(_outputType) {
@@ -552,9 +537,11 @@
if (limit > ch.length) limit = ch.length;;
+
// Step through characters and escape all special characters
for (int i = off; i < limit; i++) {
- switch (ch[i]) {
+ char current = ch[i];
+ switch (current) {
case '&':
_saxHandler.characters(ch, offset, i - offset);
_saxHandler.characters(AMP, 0, AMP_length);
@@ -576,9 +563,9 @@
offset = i + 1;
break;
default:
- // Escape all characters not in the basic ASCII character set
- // to simple (hexadecimal) character references
- if (ch[i] > '\u007F') {
+ if ( (current >= '\u007F' && current < '\u00A0') ||
+ (_is8859Encoded && (current > '\u00FF')) )
+ {
StringBuffer buf = new StringBuffer(CHAR_ESC_START);
buf.append(Integer.toString((int)ch[i]));
buf.append(';');
@@ -670,6 +657,14 @@
return(buf.toString());
}
+ private String makeHHString(int i) {
+ String s = Integer.toHexString(i).toUpperCase();
+ if (s.length() == 1) {
+ s = "0"+s;
+ }
+ return s;
+ }
+
/**
* This method escapes special characters used in HTML attribute values
*/
@@ -681,9 +676,17 @@
char[] ch = base.toCharArray();
StringBuffer buf = new StringBuffer();
for(int i=0; i<base.length(); i++){
- if (ch[i] > '\u007F') {
- buf.append('%');
- buf.append(Integer.toHexString((int)ch[i]));
+ if (ch[i] <= 0x20) {
+ buf.append('%');
+ buf.append(makeHHString(ch[i]));
+ }
+ else if (ch[i] > '\u007F') {
+ int high = (ch[i] >> 6) | 0xC0;
+ int low = (ch[i] & 0x3F) | 0x80; // First 6 bits + high bit
+ buf.append('%');
+ buf.append(makeHHString(high));
+ buf.append('%');
+ buf.append(makeHHString(low));
}
else {
// These chars are reserved or unsafe in URLs
@@ -705,7 +708,8 @@
buf.append(Integer.toHexString((int)ch[i]));
break;
case '\u0026' :
- buf.append("&");
+ //bug fix for customer/murphy3: buf.append("&");
+ buf.append("&");
break;
default:
buf.append(ch[i]); break;
@@ -758,27 +762,51 @@
return base;
}
- private String expandAttribute(String qname) throws TransletException {
- // If this attribute was created using an <xsl:attribute>
- // element with a 'namespace' attribute and a 'name' attribute
- // containing an AVT, then we might get an attribute name on
- // a strange format like 'prefix1:prefix2:localpart', where
- // prefix1 is from the AVT and prefix2 from the namespace.
- final int endcol = qname.lastIndexOf(':');
- if (endcol > 0) {
- final int startcol = qname.indexOf(':');
- final String localname = qname.substring(endcol+1);
- final String prefix = qname.substring(0,startcol);
- final String uri = lookupNamespace(prefix);
- if (uri == null)
+ /**
+ * Returns the URI of an element or attribute. Note that default namespaces
+ * do not apply directly to attributes.
+ */
+ private String getNamespaceURI(String qname, boolean isElement)
+ throws TransletException
+ {
+ String uri = EMPTYSTRING;
+ int col = qname.lastIndexOf(':');
+ final String prefix = (col > 0) ? qname.substring(0, col) : EMPTYSTRING;
+
+ if (prefix != EMPTYSTRING || isElement) {
+ uri = lookupNamespace(prefix);
+ if (uri == null && !prefix.equals(XMLNS_PREFIX)) {
BasisLibrary.runTimeError(BasisLibrary.NAMESPACE_PREFIX_ERR,
- prefix);
- // Omit prefix (use default) if the namespace URI is null
- if (uri.equals(EMPTYSTRING))
- return(localname);
- // Construct new QName if we've got two alt. prefixes
- else if (endcol != startcol)
- return(prefix+':'+localname);
+ qname.substring(0, col));
+ }
+ }
+ return uri;
+ }
+
+ /**
+ * Returns the local name of a qualified name. If the name has no prefix
+ * then return null.
+ */
+ private static String getLocalName(String qname) throws TransletException {
+ final int col = qname.lastIndexOf(':');
+ return (col > 0) ? qname.substring(col + 1) : null;
+ }
+
+ /**
+ * TODO: This method is a HACK! Since XSLTC does not have access to the
+ * XML file, it sometimes generates a NS prefix of the form "ns?" for
+ * an attribute. If at runtime, when the qname of the attribute is
+ * known, another prefix is specified for the attribute, then we can get
+ * a qname of the form "ns?:otherprefix:name". This function patches the
+ * qname by simply ignoring "otherprefix".
+ */
+ private static String patchQName(String qname) throws TransletException {
+ final int lastColon = qname.lastIndexOf(':');
+ if (lastColon > 0) {
+ final int firstColon = qname.indexOf(':');
+ if (firstColon != lastColon) {
+ return qname.substring(0, firstColon) + qname.substring(lastColon);
+ }
}
return qname;
}
@@ -790,59 +818,110 @@
public void attribute(String name, final String value)
throws TransletException {
- switch(_outputType) {
- case TEXT:
- // Do not output attributes if output mode is 'text'
- return;
- case XML:
- if (!_startTagOpen)
- BasisLibrary.runTimeError(BasisLibrary.STRAY_ATTRIBUTE_ERR,name);
- // Attributes whose names start with XML need special handling
- if (name.startsWith("xml")) {
- // Output as namespace declaration
- if (name.startsWith("xmlns")) {
- if (name.length() == 5)
- namespace(EMPTYSTRING, value);
- else
- namespace(name.substring(6),value);
- return;
- }
- // Output as xml:<blah> attribute
- _attributes.add(name, value);
- }
- else {
- // Output as regular attribute
- _attributes.add(expandAttribute(name), escapeString(value));
- }
- return;
- case HTML:
- if (!_startTagOpen)
- BasisLibrary.runTimeError(BasisLibrary.STRAY_ATTRIBUTE_ERR,name);
- // The following is an attempt to escape an URL stored in a href
- // attribute of HTML output. Normally URLs should be encoded at
- // the time they are created, since escaping or unescaping a
- // completed URI might change its semantics. We limit or escaping
- // to include space characters only - and nothing else. This is for
- // two reasons: (1) performance and (2) we want to make sure that
- // we do not change the meaning of the URL.
+ if (_outputType == TEXT) return;
- // URL-encode href attributes in HTML output
- final String tmp = name.toLowerCase();
- if (tmp.equals(HREF_STR) || tmp.equals(SRC_STR)) {
- _attributes.add(name,quickAndDirtyUrlEncode(escapeAttr(value)));
+ final String patchedName = patchQName(name);
+ final String localName = getLocalName(patchedName);
+ final String uri = getNamespaceURI(patchedName, false);
+ final int index = (localName == null) ?
+ _attributes.getIndex(name) : /* don't use patchedName */
+ _attributes.getIndex(uri, localName);
+
+ switch(_outputType) {
+ case XML:
+ if (!_startTagOpen) {
+ BasisLibrary.runTimeError(BasisLibrary.STRAY_ATTRIBUTE_ERR, patchedName);
+ }
+
+/*
+System.err.println("TextOutput.attribute() uri = " + uri
+ + " localname = " + localName
+ + " qname = " + name
+ + "\n value = " + value
+ + " escapeString(value) = " + escapeString(value));
+*/
+
+ // Output as namespace declaration
+ if (name.startsWith(XMLNS_PREFIX)) {
+ namespace(name.length() > 6 ? name.substring(6) : EMPTYSTRING, value);
}
else {
- _attributes.add(expandAttribute(name), escapeAttr(value));
+ if (index >= 0) { // Duplicate attribute?
+ _attributes.setAttribute(index, uri, localName, patchedName, "CDATA",
+ escapeString(value));
+ }
+ else {
+ _attributes.addAttribute(uri, localName, patchedName, "CDATA",
+ escapeString(value));
+ }
}
- return;
+ break;
+ case HTML:
+ if (!_startTagOpen) {
+ BasisLibrary.runTimeError(BasisLibrary.STRAY_ATTRIBUTE_ERR,name);
+ }
+
+ /*
+ * The following is an attempt to escape an URL stored in a href
+ * attribute of HTML output. Normally URLs should be encoded at
+ * the time they are created, since escaping or unescaping a
+ * completed URI might change its semantics. We limit or escaping
+ * to include space characters only - and nothing else. This is for
+ * two reasons: (1) performance and (2) we want to make sure that
+ * we do not change the meaning of the URL.
+ */
+ final String tmp = name.toLowerCase();
+ if (tmp.equals(HREF_STR) || tmp.equals(SRC_STR) || tmp.equals(CITE_STR)) {
+ if (index >= 0) {
+ _attributes.setAttribute(index, EMPTYSTRING, EMPTYSTRING, name,
+ "CDATA", quickAndDirtyUrlEncode(escapeAttr(value)));
+ }
+ else {
+ _attributes.addAttribute(EMPTYSTRING, EMPTYSTRING, name, "CDATA",
+ quickAndDirtyUrlEncode(escapeAttr(value)));
+ }
+ }
+ else {
+ if (index >= 0) {
+ _attributes.setAttribute(index, EMPTYSTRING, EMPTYSTRING,
+ name, "CDATA", escapeNonURLAttr(value));
+ }
+ else {
+ _attributes.addAttribute(EMPTYSTRING, EMPTYSTRING,
+ name, "CDATA", escapeNonURLAttr(value));
+ }
+ }
+ break;
}
}
/**
+ * Escape non ASCII characters (> u007F) as &#XXX; entities.
+ */
+ private String escapeNonURLAttr(String base) {
+ final int len = base.length() - 1;
+
+ char[] ch = base.toCharArray();
+ StringBuffer buf = new StringBuffer();
+ for(int i=0; i<base.length(); i++){
+ if (ch[i] > '\u007F') {
+ buf.append(CHAR_ESC_START);
+ buf.append(Integer.toString((int)ch[i]));
+ buf.append(';');
+ }
+ else {
+ buf.append(ch[i]);
+ }
+ }
+ base = buf.toString();
+ return base;
+ }
+
+
+ /**
* End an element or CDATA section in the output document
*/
public void endElement(String elementName) throws TransletException {
-
try {
switch(_outputType) {
case TEXT:
@@ -853,7 +932,10 @@
if (_startTagOpen) closeStartTag();
if (_cdataTagOpen) closeCDATA();
- _saxHandler.endElement(null, null, (String)(_qnameStack.pop()));
+ final String qname = (String) _qnameStack.pop();
+ _saxHandler.endElement(getNamespaceURI(qname, true),
+ getLocalName(qname), qname);
+
popNamespaces();
if (((Integer)_cdataStack.peek()).intValue() == _depth)
_cdataStack.pop();
@@ -862,7 +944,8 @@
case HTML:
// Close any open element
if (_startTagOpen) closeStartTag();
- _saxHandler.endElement(null, null, (String)(_qnameStack.pop()));
+ _saxHandler.endElement(EMPTYSTRING, EMPTYSTRING,
+ (String)(_qnameStack.pop()));
popNamespaces();
_depth--;
return;
@@ -929,10 +1012,15 @@
_prefixStack = new Stack();
// Define the default namespace (initially maps to "" uri)
- Stack stack = new Stack();
- _namespaces.put(EMPTYSTRING, stack);
+ Stack stack;
+ _namespaces.put(EMPTYSTRING, stack = new Stack());
stack.push(EMPTYSTRING);
_prefixStack.push(EMPTYSTRING);
+
+ _namespaces.put(XML_PREFIX, stack = new Stack());
+ stack.push("http://www.w3.org/XML/1998/namespace");
+ _prefixStack.push(XML_PREFIX);
+
_nodeStack.push(new Integer(-1));
_depth = 0;
}
@@ -941,9 +1029,9 @@
* Declare a prefix to point to a namespace URI
*/
private void pushNamespace(String prefix, String uri) throws SAXException {
-
+ // Prefixes "xml" and "xmlns" cannot be redefined
if (prefix.equals(XML_PREFIX)) return;
-
+
Stack stack;
// Get the stack that contains URIs for the specified prefix
if ((stack = (Stack)_namespaces.get(prefix)) == null) {
@@ -958,15 +1046,15 @@
_prefixStack.push(prefix);
_nodeStack.push(new Integer(_depth));
- if ((!prefix.equals(EMPTYSTRING)) && (uri.equals(EMPTYSTRING))) return;
- _saxHandler.startPrefixMapping(prefix, uri);
+ // Inform the SAX handler
+ _saxHandler.startPrefixMapping(prefix, escapeString(uri));
}
/**
* Undeclare the namespace that is currently pointed to by a given prefix
*/
private void popNamespace(String prefix) throws SAXException {
-
+ // Prefixes "xml" and "xmlns" cannot be redefined
if (prefix.equals(XML_PREFIX)) return;
Stack stack;
diff --git a/src/org/apache/xalan/xsltc/runtime/TransletLoader.java b/src/org/apache/xalan/xsltc/runtime/TransletLoader.java
index 1974afa..faa8861 100644
--- a/src/org/apache/xalan/xsltc/runtime/TransletLoader.java
+++ b/src/org/apache/xalan/xsltc/runtime/TransletLoader.java
@@ -57,6 +57,7 @@
* <http://www.apache.org/>.
*
* @author Morten Jorgensen
+ * @author Santiago Pericas-Geertsen
*
*/
@@ -64,6 +65,9 @@
import java.lang.Class;
import java.lang.ClassLoader;
+import java.lang.Thread;
+
+import java.net.*; // temporary
/**
* This class is intended used when the default Class.forName() method fails.
@@ -91,14 +95,14 @@
* Get a handle to the system class loader
*/
public TransletLoader() {
- // Get the default class loader
- ClassLoader loader = this.getClass().getClassLoader();
- // If this is the extensions class loader we need to get the
- // default system class loader instead. This is permitted if
- // this class was loaded by the extensions class loader.
- String loaderName = loader.getClass().getName();
- if (loaderName.equals("sun.misc.Launcher$ExtClassLoader"))
+ // Get the loader for the current thread (not the current class)
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+
+ // Avoid using the extensions class loader (see comment above)
+ final String loaderName = loader.getClass().getName();
+ if (loaderName.equals("sun.misc.Launcher$ExtClassLoader")) {
loader = ClassLoader.getSystemClassLoader();
+ }
_loader = loader;
}
@@ -108,6 +112,7 @@
public Class loadClass(String name) throws ClassNotFoundException {
return(Class.forName(name, false, _loader));
}
+
/**
* Loads a Class definition and runs static initializers.
*/
diff --git a/src/org/apache/xalan/xsltc/trax/DOM2SAX.java b/src/org/apache/xalan/xsltc/trax/DOM2SAX.java
index af2ddb7..4f3af59 100644
--- a/src/org/apache/xalan/xsltc/trax/DOM2SAX.java
+++ b/src/org/apache/xalan/xsltc/trax/DOM2SAX.java
@@ -63,6 +63,10 @@
package org.apache.xalan.xsltc.trax;
+import java.util.Stack;
+import java.util.Vector;
+import java.util.Hashtable;
+
import org.xml.sax.XMLReader;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
@@ -74,6 +78,9 @@
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.AttributeList;
+import org.xml.sax.helpers.AttributeListImpl;
+
import org.w3c.dom.Node;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
@@ -81,49 +88,110 @@
import org.w3c.dom.Entity;
import org.w3c.dom.Notation;
-import org.apache.xalan.xsltc.runtime.AttributeList;
+// import org.apache.xalan.xsltc.runtime.AttributeList;
-class DOM2SAX implements XMLReader , Locator {
+class DOM2SAX implements XMLReader, Locator {
- private Document _dom = null;
+ private final static String EMPTYSTRING = "";
+ private static final String XMLNS_PREFIX = "xmlns";
+
+ private Node _dom = null;
private ContentHandler _sax = null;
-
+ private Hashtable _nsPrefixes = new Hashtable();
+
public DOM2SAX(Node root) {
- _dom = (Document)root;
+ _dom = root;
}
public ContentHandler getContentHandler() {
return _sax;
}
- public DTDHandler getDTDHandler() {
- return null;
- }
-
- public ErrorHandler getErrorHandler() {
- return null;
- }
-
- public boolean getFeature(String name) throws SAXNotRecognizedException,
- SAXNotSupportedException
+ public void setContentHandler(ContentHandler handler) throws
+ NullPointerException
{
- return false;
+ if (handler == null) throw new NullPointerException();
+ _sax = handler;
}
- public void setFeature(String name, boolean value) throws
- SAXNotRecognizedException, SAXNotSupportedException
+ /**
+ * Begin the scope of namespace prefix. Forward the event to the
+ * SAX handler only if the prefix is unknown or it is mapped to a
+ * different URI.
+ */
+ private boolean startPrefixMapping(String prefix, String uri)
+ throws SAXException
{
-
+ boolean pushed = true;
+ Stack uriStack = (Stack) _nsPrefixes.get(prefix);
+
+ if (uriStack != null) {
+ if (uriStack.isEmpty()) {
+ _sax.startPrefixMapping(prefix, uri);
+ uriStack.push(uri);
+ }
+ else {
+ final String lastUri = (String) uriStack.peek();
+ if (!lastUri.equals(uri)) {
+ _sax.startPrefixMapping(prefix, uri);
+ uriStack.push(uri);
+ }
+ else {
+ pushed = false;
+ }
+ }
+ }
+ else {
+ _sax.startPrefixMapping(prefix, uri);
+ _nsPrefixes.put(prefix, uriStack = new Stack());
+ uriStack.push(uri);
+ }
+ return pushed;
+ }
+
+ /*
+ * End the scope of a name prefix by popping it from the stack and
+ * passing the event to the SAX Handler.
+ */
+ private void endPrefixMapping(String prefix)
+ throws SAXException
+ {
+ final Stack uriStack = (Stack) _nsPrefixes.get(prefix);
+
+ if (uriStack != null) {
+ _sax.endPrefixMapping(prefix);
+ uriStack.pop();
+ }
+ }
+
+ /**
+ * If the DOM was created using a DOM 1.0 API, the local name may be
+ * null. If so, get the local name from the qualified name before
+ * generating the SAX event.
+ */
+ private static String getLocalName(Node node) {
+ final String localName = node.getLocalName();
+
+ if (localName == null) {
+ final String qname = node.getNodeName();
+ final int col = qname.lastIndexOf(':');
+ return (col > 0) ? qname.substring(col + 1) : qname;
+ }
+ return localName;
}
public void parse(InputSource unused) throws IOException, SAXException {
- Node currNode = _dom;
- parse(currNode);
+ parse(_dom);
}
+ /**
+ * Traverse the DOM and generate SAX events for a handler. A
+ * startElement() event passes all attributes, including namespace
+ * declarations.
+ */
private void parse(Node node) throws IOException, SAXException {
Node first = null;
- if (node == null ) return;
+ if (node == null) return;
switch (node.getNodeType()) {
case Node.ATTRIBUTE_NODE: // handled by ELEMENT_NODE
@@ -139,6 +207,7 @@
case Node.DOCUMENT_NODE:
_sax.setDocumentLocator(this);
+
_sax.startDocument();
Node next = node.getFirstChild();
while (next != null) {
@@ -149,29 +218,85 @@
break;
case Node.ELEMENT_NODE:
- // Gather all attribute node of the element
- AttributeList attrs = new AttributeList();
- NamedNodeMap map = node.getAttributes();
- int length = map.getLength();
- for (int i=0; i<length; i++ ) {
- Node attr = map.item(i);
- attrs.add(attr.getNodeName(), attr.getNodeValue());
+ String prefix;
+ Vector pushedPrefixes = new Vector();
+ final AttributesImpl attrs = new AttributesImpl();
+ final NamedNodeMap map = node.getAttributes();
+ final int length = map.getLength();
+
+ // Process all namespace declarations
+ for (int i = 0; i < length; i++) {
+ final Node attr = map.item(i);
+ final String qnameAttr = attr.getNodeName();
+
+ // Ignore everything but NS declarations here
+ if (qnameAttr.startsWith(XMLNS_PREFIX)) {
+ final String uriAttr = attr.getNodeValue();
+ final int colon = qnameAttr.lastIndexOf(':');
+ prefix = (colon > 0) ? qnameAttr.substring(colon + 1) : EMPTYSTRING;
+ if (startPrefixMapping(prefix, uriAttr)) {
+ pushedPrefixes.addElement(prefix);
+ }
+ }
+ }
+
+ // Process all other attributes
+ for (int i = 0; i < length; i++) {
+ final Node attr = map.item(i);
+ final String qnameAttr = attr.getNodeName();
+
+ // Ignore NS declarations here
+ if (!qnameAttr.startsWith(XMLNS_PREFIX)) {
+ final String uriAttr = attr.getNamespaceURI();
+ final String localNameAttr = getLocalName(attr);
+
+ // Uri may be implicitly declared
+ if (uriAttr != null) {
+ final int colon = qnameAttr.lastIndexOf(':');
+ prefix = (colon > 0) ? qnameAttr.substring(0, colon) : EMPTYSTRING;
+ if (startPrefixMapping(prefix, uriAttr)) {
+ pushedPrefixes.addElement(prefix);
+ }
+ }
+
+ // Add attribute to list
+ attrs.addAttribute(attr.getNamespaceURI(), getLocalName(attr),
+ qnameAttr, "CDATA", attr.getNodeValue());
+ }
+ }
+
+ // Now process the element itself
+ final String qname = node.getNodeName();
+ final String uri = node.getNamespaceURI();
+ final String localName = getLocalName(node);
+
+ // Uri may be implicitly declared
+ if (uri != null) {
+ final int colon = qname.lastIndexOf(':');
+ prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
+ if (startPrefixMapping(prefix, uri)) {
+ pushedPrefixes.addElement(prefix);
+ }
}
// Generate SAX event to start element
- _sax.startElement(node.getNamespaceURI(), node.getLocalName(),
- node.getNodeName(), attrs);
+ _sax.startElement(uri, localName, qname, attrs);
// Traverse all child nodes of the element (if any)
next = node.getFirstChild();
- while ( next != null ) {
+ while (next != null) {
parse(next);
next = next.getNextSibling();
}
// Generate SAX event to close element
- _sax.endElement(node.getNamespaceURI(),
- node.getLocalName(), node.getNodeName());
+ _sax.endElement(uri, localName, qname);
+
+ // Generate endPrefixMapping() for all pushed prefixes
+ final int nPushedPrefixes = pushedPrefixes.size();
+ for (int i = 0; i < nPushedPrefixes; i++) {
+ endPrefixMapping((String) pushedPrefixes.elementAt(i));
+ }
break;
case Node.PROCESSING_INSTRUCTION_NODE:
@@ -186,48 +311,133 @@
}
}
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public DTDHandler getDTDHandler() {
+ return null;
+ }
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public ErrorHandler getErrorHandler() {
+ return null;
+ }
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public boolean getFeature(String name) throws SAXNotRecognizedException,
+ SAXNotSupportedException
+ {
+ return false;
+ }
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public void setFeature(String name, boolean value) throws
+ SAXNotRecognizedException, SAXNotSupportedException
+ {
+ }
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
public void parse(String sysId) throws IOException, SAXException {
throw new IOException("This method is not yet implemented.");
}
- public void setContentHandler(ContentHandler handler) throws
- NullPointerException
- {
- if (handler == null ) throw new NullPointerException();
- _sax = handler;
- }
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
public void setDTDHandler(DTDHandler handler) throws NullPointerException {
- if (handler == null ) throw new NullPointerException();
}
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
public void setEntityResolver(EntityResolver resolver) throws
NullPointerException
{
- if (resolver == null ) throw new NullPointerException();
}
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
public EntityResolver getEntityResolver() {
return null;
}
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
public void setErrorHandler(ErrorHandler handler) throws
NullPointerException
{
- if (handler == null ) throw new NullPointerException();
}
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
public void setProperty(String name, Object value) throws
SAXNotRecognizedException, SAXNotSupportedException {
}
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
public Object getProperty(String name) throws SAXNotRecognizedException,
SAXNotSupportedException
{
return null;
}
- // Locator methods
- public int getColumnNumber() { return 0; }
- public int getLineNumber() { return 0; }
- public String getPublicId() { return null; }
- public String getSystemId() { return null; }
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public int getColumnNumber() {
+ return 0;
+ }
+
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public int getLineNumber() {
+ return 0;
+ }
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public String getPublicId() {
+ return null;
+ }
- // private
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public String getSystemId() {
+ return null;
+ }
+
+ // Debugging
private String getNodeTypeFromCode(short code) {
String retval = null;
switch (code) {
diff --git a/src/org/apache/xalan/xsltc/trax/SAX2DOM.java b/src/org/apache/xalan/xsltc/trax/SAX2DOM.java
index 82177b7..de6da38 100644
--- a/src/org/apache/xalan/xsltc/trax/SAX2DOM.java
+++ b/src/org/apache/xalan/xsltc/trax/SAX2DOM.java
@@ -57,12 +57,14 @@
* <http://www.apache.org/>.
*
* @author G. Todd Miller
- *
*/
package org.apache.xalan.xsltc.trax;
+import java.util.Stack;
+import java.util.Vector;
+
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;
@@ -75,164 +77,135 @@
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.w3c.dom.Attr;
-import java.util.Stack;
+import org.apache.xalan.xsltc.runtime.Constants;
-class SAX2DOM implements ContentHandler {
+class SAX2DOM implements ContentHandler, Constants {
- private Document _document = null;
- private DocumentBuilder _builder = null;
- private Stack _nodeStk = null;
-
+ private Document _root = null;
+ private Stack _nodeStk = new Stack();
+ private Vector _namespaceDecls = null;
+
public SAX2DOM() throws ParserConfigurationException {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- _builder = factory.newDocumentBuilder();
- _nodeStk = new Stack();
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ _root = factory.newDocumentBuilder().newDocument();
+ }
+
+ public SAX2DOM(Node root) throws ParserConfigurationException {
+ if (root != null) {
+ _root = (Document) root; // TODO: add support for frags and elems
+ }
+ else {
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ _root = factory.newDocumentBuilder().newDocument();
+ }
}
public Node getDOM() {
- return _document;
+ return _root;
}
public void characters(char[] ch, int start, int length) {
- Text text = _document.createTextNode(new String(ch, start, length));
- Node last = (Node)_nodeStk.peek();
- last.appendChild(text);
+ final Node last = (Node)_nodeStk.peek();
+
+ // No text nodes can be children of root (DOM006 exception)
+ if (last != _root) {
+ final String text = new String(ch, start, length);
+ last.appendChild(_root.createTextNode(text));
+ }
}
public void startDocument() {
- _document = _builder.newDocument();
- Element root = (Element)_document.createElement("root");
- _document.appendChild(root);
- _nodeStk.push(root);
+ _nodeStk.push(_root);
}
public void endDocument() {
- //printDOM();
}
public void startElement(String namespace, String localName, String qName,
- Attributes attrs )
+ Attributes attrs)
{
- // create new element
- Element tmp = (Element)_document.createElementNS(namespace, qName);
- int nattrs = attrs.getLength();
- for (int i=0; i<nattrs; i++ ) {
- String namespaceuri = attrs.getURI(i);
- String value = attrs.getValue(i);
- String qname = attrs.getQName(i);
- if ((namespaceuri == null) || (namespaceuri.equals("")))
- tmp.setAttribute(qname, value);
- else
- tmp.setAttributeNS(namespaceuri, qname, value);
+ final Element tmp = (Element)_root.createElementNS(namespace, qName);
+
+ // Add namespace declarations first
+ if (_namespaceDecls != null) {
+ final int nDecls = _namespaceDecls.size();
+ for (int i = 0; i < nDecls; i++) {
+ final String prefix = (String) _namespaceDecls.elementAt(i++);
+
+ if (prefix == null || prefix.equals(EMPTYSTRING)) {
+ tmp.setAttributeNS(XMLNS_URI, XMLNS_PREFIX,
+ (String) _namespaceDecls.elementAt(i));
+ }
+ else {
+ tmp.setAttributeNS(XMLNS_URI, XMLNS_STRING + prefix,
+ (String) _namespaceDecls.elementAt(i));
+ }
+ }
+ _namespaceDecls.clear();
}
- // append this new node onto current stack node
+
+ // Add attributes to element
+ final int nattrs = attrs.getLength();
+ for (int i = 0; i < nattrs; i++) {
+ if (attrs.getLocalName(i) == null) {
+ tmp.setAttribute(attrs.getQName(i), attrs.getValue(i));
+ }
+ else {
+ tmp.setAttributeNS(attrs.getURI(i), attrs.getQName(i),
+ attrs.getValue(i));
+ }
+ }
+
+ // Append this new node onto current stack node
Node last = (Node)_nodeStk.peek();
last.appendChild(tmp);
- // push this node onto stack
+
+ // Push this node onto stack
_nodeStk.push(tmp);
}
public void endElement(String namespace, String localName, String qName) {
- Node lastActive = (Node)_nodeStk.pop();
- }
-
-
- public void ignorableWhitespace(char[] ch, int start, int length) {
- }
-
- public void processingInstruction(String target, String data) {
- }
-
- public void setDocumentLocator(Locator locator) {
- }
-
- public void skippedEntity(String name) {
+ _nodeStk.pop();
}
public void startPrefixMapping(String prefix, String uri) {
+ if (_namespaceDecls == null) {
+ _namespaceDecls = new Vector(2);
+ }
+ _namespaceDecls.addElement(prefix);
+ _namespaceDecls.addElement(uri);
}
public void endPrefixMapping(String prefix) {
+ // do nothing
}
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public void ignorableWhitespace(char[] ch, int start, int length) {
+ }
- // for debugging - will be removed
- private void printDOM() {
- System.out.println("SAX2DOM.java:Printing DOM...");
- Node currNode = _document;
- while (currNode != null) {
- // start of node processing
- switch (currNode.getNodeType()) {
- case Node.ATTRIBUTE_NODE :
- break;
- case Node.CDATA_SECTION_NODE :
- break;
- case Node.COMMENT_NODE :
- break;
- case Node.DOCUMENT_FRAGMENT_NODE :
- break;
- case Node.DOCUMENT_NODE :
- break;
- case Node.DOCUMENT_TYPE_NODE :
- break;
- case Node.ELEMENT_NODE :
- System.out.println("ELEMT NODE " + currNode.getLocalName() +":");
- org.w3c.dom.NamedNodeMap map = currNode.getAttributes();
- int length = map.getLength();
- for (int i=0; i<length; i++ ){
- Node attrNode = map.item(i);
- short code = attrNode.getNodeType();
- System.out.println("\tattr:"+attrNode.getNamespaceURI()+
- "," + attrNode.getLocalName() +
- "," + attrNode.getNodeName() +
- "=" + attrNode.getNodeValue());
- }
- break;
- case Node.ENTITY_NODE :
- org.w3c.dom.Entity edecl = (org.w3c.dom.Entity)currNode;
- String name = edecl.getNotationName();
- if ( name != null ) {
- System.out.println("ENT NODE: "+currNode.getNodeName()+
- ", "+ edecl.getSystemId()+ "," + name);
- }
- break;
- case Node.ENTITY_REFERENCE_NODE :
- break;
- case Node.NOTATION_NODE :
- break;
- case Node.PROCESSING_INSTRUCTION_NODE :
- break;
- case Node.TEXT_NODE :
- String data = currNode.getNodeValue();
- System.out.println("TEXT NODE:" + data);
- break;
- }
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public void processingInstruction(String target, String data) {
+ }
- // move to first child
- Node next = currNode.getFirstChild();
- if (next != null) {
- currNode = next;
- continue;
- }
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public void setDocumentLocator(Locator locator) {
+ }
- // no child nodes, walk the tree
- while (currNode != null) {
- switch (currNode.getNodeType()) {
- case Node.DOCUMENT_NODE:
- break;
- case Node.ELEMENT_NODE:
- break;
- }
- next = currNode.getNextSibling();
- if (next != null ) {
- currNode = next;
- break;
- }
- // move up a level
- currNode = currNode.getParentNode();
- }
- }
- }
-
+ /**
+ * This class is only used internally so this method should never
+ * be called.
+ */
+ public void skippedEntity(String name) {
+ }
}
diff --git a/src/org/apache/xalan/xsltc/trax/TemplatesHandlerImpl.java b/src/org/apache/xalan/xsltc/trax/TemplatesHandlerImpl.java
index 3c8f8f0..0fcddbb 100644
--- a/src/org/apache/xalan/xsltc/trax/TemplatesHandlerImpl.java
+++ b/src/org/apache/xalan/xsltc/trax/TemplatesHandlerImpl.java
@@ -91,8 +91,9 @@
// Create and initialize a stylesheet compiler
final XSLTC xsltc = new XSLTC();
super.setXSLTC(xsltc);
- xsltc.setParser(this);
xsltc.init();
+ super.init();
+ xsltc.setParser(this);
xsltc.setOutputType(XSLTC.BYTEARRAY_OUTPUT);
}
@@ -126,10 +127,7 @@
* process, or null if no Templates object has been created.
*/
public Templates getTemplates() {
-
try {
- // Create a placeholder for the translet bytecodes
- byte[][] bytecodes = null;
final XSLTC xsltc = getXSLTC();
@@ -137,6 +135,8 @@
String transletName = TransformerFactoryImpl._defaultTransletName;
if (_systemId != null) transletName = Util.baseName(_systemId);
xsltc.setClassName(transletName);
+ // get java-legal class name from XSLTC module
+ transletName = xsltc.getClassName();
Stylesheet stylesheet = null;
SyntaxTreeNode root = getDocumentRoot();
@@ -161,12 +161,13 @@
xsltc.printWarnings();
// Check that the transformation went well before returning
+ final byte[][] bytecodes = xsltc.getBytecodes();
if (bytecodes == null) {
xsltc.printErrors();
return null;
}
- return(new TemplatesImpl(bytecodes, transletName));
+ return new TemplatesImpl(bytecodes, transletName, getOutputProperties());
}
catch (CompilerException e) {
return null;
diff --git a/src/org/apache/xalan/xsltc/trax/TemplatesImpl.java b/src/org/apache/xalan/xsltc/trax/TemplatesImpl.java
index e1c099c..0686969 100644
--- a/src/org/apache/xalan/xsltc/trax/TemplatesImpl.java
+++ b/src/org/apache/xalan/xsltc/trax/TemplatesImpl.java
@@ -59,6 +59,7 @@
* @author Morten Jorgensen
* @author G. Todd Millerj
* @author Jochen Cordes <Jochen.Cordes@t-online.de>
+ * @author Santiago Pericas-Geertsen
*
*/
@@ -96,6 +97,8 @@
// and _bytecodes arrays (above).
private int _transletIndex = -1;
+ private Properties _outputProperties;
+
// Our own private class loader - builds Class definitions from bytecodes
private class TransletClassLoader extends ClassLoader {
@@ -126,9 +129,12 @@
* The bytecodes for the translet and auxiliary classes, plus the name of
* the main translet class, must be supplied
*/
- protected TemplatesImpl(byte[][] bytecodes, String transletName) {
+ protected TemplatesImpl(byte[][] bytecodes, String transletName,
+ Properties outputProperties)
+ {
_bytecodes = bytecodes;
_name = transletName;
+ _outputProperties = outputProperties;
}
/**
@@ -176,8 +182,13 @@
(TransletClassLoader) AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
- ClassLoader current = getClass().getClassLoader();
- return new TransletClassLoader(current);
+ /*
+ * Get the loader from the current thread instead of
+ * the class. This is important for translets that load
+ * external Java classes and run in multi-threaded envs.
+ */
+ return new TransletClassLoader(
+ Thread.currentThread().getContextClassLoader());
}
}
);
@@ -250,7 +261,7 @@
*/
public Transformer newTransformer()
throws TransformerConfigurationException {
- return(new TransformerImpl(getTransletInstance()));
+ return new TransformerImpl(getTransletInstance(), _outputProperties);
}
/**
diff --git a/src/org/apache/xalan/xsltc/trax/TransformerFactoryImpl.java b/src/org/apache/xalan/xsltc/trax/TransformerFactoryImpl.java
index 2f2cc9b..6ebfe90 100644
--- a/src/org/apache/xalan/xsltc/trax/TransformerFactoryImpl.java
+++ b/src/org/apache/xalan/xsltc/trax/TransformerFactoryImpl.java
@@ -336,7 +336,8 @@
}
// Create a Transformer object and store for other calls
- Templates templates = new TemplatesImpl(bytecodes,_defaultTransletName);
+ Templates templates = new TemplatesImpl(bytecodes,
+ _defaultTransletName, xsltc.getOutputProperties());
_copyTransformer = templates.newTransformer();
if (_uriResolver != null) _copyTransformer.setURIResolver(_uriResolver);
return(_copyTransformer);
@@ -363,22 +364,20 @@
/**
* Pass warning messages from the compiler to the error listener
*/
- private void passWarningsToListener(Vector messages) {
- try {
- // Nothing to do if there is no registered error listener
- if (_errorListener == null) return;
- // Nothing to do if there are not warning messages
- if (messages == null) return;
- // Pass messages to listener, one by one
- final int count = messages.size();
- for (int pos=0; pos<count; pos++) {
- String message = messages.elementAt(pos).toString();
- _errorListener.warning(new TransformerException(message));
- }
+ private void passWarningsToListener(Vector messages)
+ throws TransformerException
+ {
+ if (_errorListener == null || messages == null ) {
+ return;
}
- catch (TransformerException e) {
- // nada
+ // Pass messages to listener, one by one
+ final int count = messages.size();
+ for (int pos = 0; pos < count; pos++) {
+ String message = messages.elementAt(pos).toString();
+ _errorListener.error(
+ new TransformerConfigurationException(message));
}
+
}
/**
@@ -474,7 +473,6 @@
*/
public Templates newTemplates(Source source)
throws TransformerConfigurationException {
-
// Create and initialize a stylesheet compiler
final XSLTC xsltc = new XSLTC();
if (_debug) xsltc.setDebug(true);
@@ -499,10 +497,18 @@
final String transletName = xsltc.getClassName();
// Pass compiler warnings to the error listener
- if (_errorListener != null)
- passWarningsToListener(xsltc.getWarnings());
- else
+ if (_errorListener != this){
+ //passWarningsToListener(xsltc.getWarnings());
+ try {
+ passWarningsToListener(xsltc.getWarnings());
+ }
+ catch (TransformerException e) {
+ throw new TransformerConfigurationException(e);
+ }
+ }
+ else {
xsltc.printWarnings();
+ }
// Check that the transformation went well before returning
if (bytecodes == null) {
@@ -514,7 +520,8 @@
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR);
throw new TransformerConfigurationException(err.toString());
}
- return(new TemplatesImpl(bytecodes, transletName));
+ return new TemplatesImpl(bytecodes, transletName,
+ xsltc.getOutputProperties());
}
/**
@@ -527,7 +534,9 @@
*/
public TemplatesHandler newTemplatesHandler()
throws TransformerConfigurationException {
- return(new TemplatesHandlerImpl());
+ final TemplatesHandlerImpl handler = new TemplatesHandlerImpl();
+ handler.init();
+ return handler;
}
/**
diff --git a/src/org/apache/xalan/xsltc/trax/TransformerImpl.java b/src/org/apache/xalan/xsltc/trax/TransformerImpl.java
index 24e66cf..9ff16cd 100644
--- a/src/org/apache/xalan/xsltc/trax/TransformerImpl.java
+++ b/src/org/apache/xalan/xsltc/trax/TransformerImpl.java
@@ -118,7 +118,7 @@
private ErrorListener _errorListener = this;
private URIResolver _uriResolver = null;
- private Properties _properties = null;
+ private Properties _properties, _propertiesClone;
// Used for default output property settings
private final static String EMPTY_STRING = "";
@@ -138,9 +138,10 @@
* Implements JAXP's Transformer constructor
* Our Transformer objects always need a translet to do the actual work
*/
- protected TransformerImpl(Translet translet) {
+ protected TransformerImpl(Translet translet, Properties outputProperties) {
_translet = (AbstractTranslet)translet;
- _properties = createOutputProperties();
+ _properties = createOutputProperties(outputProperties);
+ _propertiesClone = (Properties) _properties.clone();
}
/**
@@ -211,7 +212,7 @@
if (handler != null) return handler;
}
else if (result instanceof DOMResult) {
- return (new SAX2DOM());
+ return new SAX2DOM(((DOMResult) result).getNode());
}
else if (result instanceof StreamResult) {
// Get StreamResult
@@ -409,8 +410,14 @@
// Handle DOMSource input
else if (source instanceof DOMSource) {
final DOMSource domsrc = (DOMSource)source;
- final Document tree = (Document)domsrc.getNode();
- final DOM2SAX dom2sax = new DOM2SAX(tree);
+ final org.w3c.dom.Node node = domsrc.getNode();
+
+ boolean isComplete = true;
+ if (node.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE) {
+ isComplete = false;
+ }
+
+ final DOM2SAX dom2sax = new DOM2SAX(node);
final InputSource input = null;
final String systemId = domsrc.getSystemId();
@@ -425,7 +432,13 @@
dom2sax.setContentHandler(builder);
// Parse the input and build the internal DOM
+ if (!isComplete) {
+ builder.startDocument();
+ }
dom2sax.parse(input); // need this parameter?
+ if (!isComplete) {
+ builder.endDocument();
+ }
dom.setDocumentURI(systemId);
}
// Handle StreamSource input
@@ -630,17 +643,12 @@
/**
* Implements JAXP's Transformer.getOutputProperties().
- * Returns a copy of the output properties for the transformation. This is
- * a set of layered properties. The first layer contains properties set by
- * calls to setOutputProperty() and setOutputProperties() on this class,
- * and the output settings defined in the stylesheet's <xsl:output>
- * element makes up the second level, while the default XSLT output
- * settings are returned on the third level.
+ * Returns a copy of the output properties for the transformation.
*
* @return Properties in effect for this Transformer
*/
public Properties getOutputProperties() {
- return(_properties);
+ return (Properties) _properties.clone();
}
/**
@@ -658,7 +666,7 @@
ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
throw new IllegalArgumentException(err.toString());
}
- return(_properties.getProperty(name));
+ return _properties.getProperty(name);
}
/**
@@ -671,8 +679,25 @@
* @throws IllegalArgumentException Never, errors are ignored
*/
public void setOutputProperties(Properties properties)
- throws IllegalArgumentException {
- _properties.putAll(properties);
+ throws IllegalArgumentException
+ {
+ if (properties != null) {
+ final Enumeration names = properties.propertyNames();
+
+ while (names.hasMoreElements()) {
+ final String name = (String) names.nextElement();
+ if (validOutputProperty(name)) {
+ _properties.setProperty(name, properties.getProperty(name));
+ }
+ else {
+ ErrorMsg err = new ErrorMsg(ErrorMsg.JAXP_UNKNOWN_PROP_ERR, name);
+ throw new IllegalArgumentException(err.toString());
+ }
+ }
+ }
+ else {
+ _properties = _propertiesClone;
+ }
}
/**
@@ -708,7 +733,8 @@
while (names.hasMoreElements()) {
// Get the next property name and value
String name = (String)names.nextElement();
- String value = (String)properties.get(name);
+ // bug fix # 6636- contributed by Tim Elcott
+ String value = (String)properties.getProperty(name);
// Pass property value to translet - override previous setting
if (name.equals(OutputKeys.ENCODING))
@@ -753,65 +779,40 @@
* Internal method to pass any properties to the translet prior to
* initiating the transformation
*/
- private Properties createOutputProperties() {
-
- // Level3: Return the default property value
- Properties third = new Properties();
- third.setProperty(OutputKeys.ENCODING, "UTF-8");
- third.setProperty(OutputKeys.METHOD, XML_STRING);
- third.setProperty(OutputKeys.INDENT, NO_STRING);
- third.setProperty(OutputKeys.DOCTYPE_PUBLIC, EMPTY_STRING);
- third.setProperty(OutputKeys.DOCTYPE_SYSTEM, EMPTY_STRING);
- third.setProperty(OutputKeys.CDATA_SECTION_ELEMENTS, EMPTY_STRING);
- third.setProperty(OutputKeys.MEDIA_TYPE, "text/xml");
- third.setProperty(OutputKeys.OMIT_XML_DECLARATION, NO_STRING);
- third.setProperty(OutputKeys.STANDALONE, NO_STRING);
- third.setProperty(OutputKeys.VERSION, "1.0");
+ private Properties createOutputProperties(Properties outputProperties) {
+ final Properties defaults = new Properties();
+ defaults.setProperty(OutputKeys.ENCODING, "UTF-8");
+ defaults.setProperty(OutputKeys.METHOD, XML_STRING);
+ defaults.setProperty(OutputKeys.INDENT, NO_STRING);
+ defaults.setProperty(OutputKeys.MEDIA_TYPE, "text/xml");
+ defaults.setProperty(OutputKeys.OMIT_XML_DECLARATION, NO_STRING);
+ defaults.setProperty(OutputKeys.STANDALONE, NO_STRING);
+ defaults.setProperty(OutputKeys.VERSION, "1.0");
- // Level2: Return the property value is set in the translet
- // Creating these properties with the third-level properties as default
- Properties second = new Properties(third);
- if (_translet != null) {
- String value = _translet._encoding;
- if (value != null) second.setProperty(OutputKeys.ENCODING, value);
-
- value = _translet._method;
- if (value != null) second.setProperty(OutputKeys.METHOD, value);
-
- if (_translet._indent)
- second.setProperty(OutputKeys.INDENT, "yes");
- else
- second.setProperty(OutputKeys.INDENT, "no");
-
- value = _translet._doctypePublic;
- if (value != null)
- second.setProperty(OutputKeys.DOCTYPE_PUBLIC, value);
-
- value = _translet._doctypeSystem;
- if (value != null)
- second.setProperty(OutputKeys.DOCTYPE_SYSTEM, value);
-
- value = makeCDATAString(_translet._cdata);
- if (value != null)
- second.setProperty(OutputKeys.CDATA_SECTION_ELEMENTS,value);
-
- value = _translet._mediaType;
- if (value != null) second.setProperty(OutputKeys.MEDIA_TYPE, value);
-
- if (_translet._omitHeader)
- second.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
- else
- second.setProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
-
- value = _translet._standalone;
- if (value != null) second.setProperty(OutputKeys.STANDALONE, value);
-
- value = _translet._version;
- if (value != null) second.setProperty(OutputKeys.VERSION, value);
+ // Copy propeties set in stylesheet to base
+ final Properties base = new Properties(defaults);
+ if (outputProperties != null) {
+ final Enumeration names = outputProperties.propertyNames();
+ while (names.hasMoreElements()) {
+ final String name = (String) names.nextElement();
+ base.setProperty(name, outputProperties.getProperty(name));
+ }
}
- // Creating the properties with the second-level properties as default
- return(new Properties(second));
+ // Update defaults based on output method
+ final String method = base.getProperty(OutputKeys.METHOD);
+ if (method != null) {
+ if (method.equals("html")) {
+ defaults.setProperty(OutputKeys.INDENT, "yes");
+ defaults.setProperty(OutputKeys.VERSION, "4.0");
+ defaults.setProperty(OutputKeys.MEDIA_TYPE, "text/html");
+ }
+ else if (method.equals("text")) {
+ defaults.setProperty(OutputKeys.MEDIA_TYPE, "text/plain");
+ }
+ }
+
+ return base;
}
/**
diff --git a/src/trax/trax.properties b/src/trax/trax.properties
deleted file mode 100644
index e593c39..0000000
--- a/src/trax/trax.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# $Revision$ $Date$
-#
-# Note: This properties file is provided for illustrative purposes
-# only and is not part of the interface definition.
-# This properties file is located in the implementation JAR
-# and different implementations will specify different
-# implementation classes and output methods.
-#
-
-# The TRaX Stylesheet processor
-trax.processor.xslt=org.apache.xalan.processor.StylesheetProcessor