Patch for Bugzilla bug report 24988 from Joanne Tong (joannet () ca ! ibm ! com) reviewed by myself. Changes required to test whether an attribute value that is required to be a QName, NCName or whitespace-separated list of QNames actually meets that requirement.
diff --git a/src/org/apache/xalan/xsltc/compiler/ApplyTemplates.java b/src/org/apache/xalan/xsltc/compiler/ApplyTemplates.java index 6ccfaeb..880caf1 100644 --- a/src/org/apache/xalan/xsltc/compiler/ApplyTemplates.java +++ b/src/org/apache/xalan/xsltc/compiler/ApplyTemplates.java
@@ -80,6 +80,7 @@ import org.apache.xalan.xsltc.compiler.util.Type; import org.apache.xalan.xsltc.compiler.util.TypeCheckError; import org.apache.xalan.xsltc.compiler.util.Util; +import org.apache.xml.utils.XMLChar; final class ApplyTemplates extends Instruction { private Expression _select; @@ -117,6 +118,10 @@ } if (mode.length() > 0) { + if (!XMLChar.isValidQName(mode)) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, mode, this); + parser.reportError(Constants.ERROR, err); + } _modeName = parser.getQNameIgnoreDefaultNs(mode); }
diff --git a/src/org/apache/xalan/xsltc/compiler/AttributeSet.java b/src/org/apache/xalan/xsltc/compiler/AttributeSet.java index 1d35c97..0a92bc5 100644 --- a/src/org/apache/xalan/xsltc/compiler/AttributeSet.java +++ b/src/org/apache/xalan/xsltc/compiler/AttributeSet.java
@@ -76,6 +76,8 @@ import org.apache.xalan.xsltc.compiler.util.MethodGenerator; import org.apache.xalan.xsltc.compiler.util.Type; import org.apache.xalan.xsltc.compiler.util.TypeCheckError; +import org.apache.xalan.xsltc.compiler.util.Util; +import org.apache.xml.utils.XMLChar; final class AttributeSet extends TopLevelElement { @@ -121,7 +123,13 @@ public void parseContents(Parser parser) { // Get this attribute set's name - _name = parser.getQNameIgnoreDefaultNs(getAttribute("name")); + final String name = getAttribute("name"); + + if (!XMLChar.isValidQName(name)) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this); + parser.reportError(Constants.ERROR, err); + } + _name = parser.getQNameIgnoreDefaultNs(name); if ((_name == null) || (_name.equals(EMPTYSTRING))) { ErrorMsg msg = new ErrorMsg(ErrorMsg.UNNAMED_ATTRIBSET_ERR, this); parser.reportError(Constants.ERROR, msg); @@ -130,6 +138,10 @@ // Get any included attribute sets (similar to inheritance...) final String useSets = getAttribute("use-attribute-sets"); if (useSets.length() > 0) { + if (!Util.isValidQNames(useSets)) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, useSets, this); + parser.reportError(Constants.ERROR, err); + } _useSets = new UseAttributeSets(useSets, parser); }
diff --git a/src/org/apache/xalan/xsltc/compiler/Copy.java b/src/org/apache/xalan/xsltc/compiler/Copy.java index c33927d..ede30ba 100644 --- a/src/org/apache/xalan/xsltc/compiler/Copy.java +++ b/src/org/apache/xalan/xsltc/compiler/Copy.java
@@ -77,6 +77,7 @@ import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.LocalVariableGen; import org.apache.xalan.xsltc.compiler.util.ClassGenerator; +import org.apache.xalan.xsltc.compiler.util.ErrorMsg; import org.apache.xalan.xsltc.compiler.util.MethodGenerator; import org.apache.xalan.xsltc.compiler.util.Type; import org.apache.xalan.xsltc.compiler.util.TypeCheckError; @@ -88,6 +89,10 @@ public void parseContents(Parser parser) { final String useSets = getAttribute("use-attribute-sets"); if (useSets.length() > 0) { + if (!Util.isValidQNames(useSets)) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, useSets, this); + parser.reportError(Constants.ERROR, err); + } _useSets = new UseAttributeSets(useSets, parser); } parseChildren(parser);
diff --git a/src/org/apache/xalan/xsltc/compiler/DecimalFormatting.java b/src/org/apache/xalan/xsltc/compiler/DecimalFormatting.java index ac5cbf6..23f1a2e 100644 --- a/src/org/apache/xalan/xsltc/compiler/DecimalFormatting.java +++ b/src/org/apache/xalan/xsltc/compiler/DecimalFormatting.java
@@ -76,6 +76,7 @@ import org.apache.xalan.xsltc.compiler.util.MethodGenerator; import org.apache.xalan.xsltc.compiler.util.Type; import org.apache.xalan.xsltc.compiler.util.TypeCheckError; +import org.apache.xml.utils.XMLChar; final class DecimalFormatting extends TopLevelElement { @@ -96,10 +97,17 @@ */ public void parseContents(Parser parser) { // Get the name of these decimal formatting symbols - _name = parser.getQNameIgnoreDefaultNs(getAttribute("name")); - if (_name == null) { - _name = parser.getQNameIgnoreDefaultNs(EMPTYSTRING); - } + final String name = getAttribute("name"); + if (name.length() > 0) { + if (!XMLChar.isValidQName(name)){ + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this); + parser.reportError(Constants.ERROR, err); + } + } + _name = parser.getQNameIgnoreDefaultNs(name); + if (_name == null) { + _name = parser.getQNameIgnoreDefaultNs(EMPTYSTRING); + } // Check if a set of symbols has already been registered under this name SymbolTable stable = parser.getSymbolTable();
diff --git a/src/org/apache/xalan/xsltc/compiler/Key.java b/src/org/apache/xalan/xsltc/compiler/Key.java index ccc426d..8d86842 100644 --- a/src/org/apache/xalan/xsltc/compiler/Key.java +++ b/src/org/apache/xalan/xsltc/compiler/Key.java
@@ -86,6 +86,7 @@ import org.apache.xalan.xsltc.compiler.util.TypeCheckError; import org.apache.xalan.xsltc.compiler.util.Util; import org.apache.xalan.xsltc.dom.Axis; +import org.apache.xml.utils.XMLChar; final class Key extends TopLevelElement { @@ -116,7 +117,12 @@ public void parseContents(Parser parser) { // Get the required attributes and parser XPath expressions - _name = parser.getQNameIgnoreDefaultNs(getAttribute("name")); + final String name = getAttribute("name"); + if (!XMLChar.isValidQName(name)){ + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this); + parser.reportError(Constants.ERROR, err); + } + _name = parser.getQNameIgnoreDefaultNs(name); _match = parser.parsePattern(this, "match", null); _use = parser.parseExpression(this, "use", null);
diff --git a/src/org/apache/xalan/xsltc/compiler/LiteralElement.java b/src/org/apache/xalan/xsltc/compiler/LiteralElement.java index c229ba6..0118b43 100644 --- a/src/org/apache/xalan/xsltc/compiler/LiteralElement.java +++ b/src/org/apache/xalan/xsltc/compiler/LiteralElement.java
@@ -72,6 +72,7 @@ import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.PUSH; import org.apache.xalan.xsltc.compiler.util.ClassGenerator; +import org.apache.xalan.xsltc.compiler.util.ErrorMsg; import org.apache.xalan.xsltc.compiler.util.MethodGenerator; import org.apache.xalan.xsltc.compiler.util.Type; import org.apache.xalan.xsltc.compiler.util.TypeCheckError; @@ -290,6 +291,10 @@ // in the vector or attributes to make sure that later local // attributes can override an attributes in the set. if (qname == parser.getUseAttributeSets()) { + if (!Util.isValidQNames(val)) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, val, this); + parser.reportError(Constants.ERROR, err); + } setFirstAttribute(new UseAttributeSets(val, parser)); } // Handle xsl:extension-element-prefixes
diff --git a/src/org/apache/xalan/xsltc/compiler/Output.java b/src/org/apache/xalan/xsltc/compiler/Output.java index a533639..81ed99b 100644 --- a/src/org/apache/xalan/xsltc/compiler/Output.java +++ b/src/org/apache/xalan/xsltc/compiler/Output.java
@@ -80,6 +80,7 @@ import org.apache.xalan.xsltc.compiler.util.MethodGenerator; import org.apache.xalan.xsltc.compiler.util.Util; import org.apache.xml.serializer.Encodings; +import org.apache.xml.utils.XMLChar; final class Output extends TopLevelElement { @@ -163,14 +164,21 @@ outputProperties.setProperty(OutputKeys.VERSION, _version); } - // Get the output method - "xml", "html", "text" or <qname> + // Get the output method - "xml", "html", "text" or <qname> (but not ncname) _method = getAttribute("method"); if (_method.equals(Constants.EMPTYSTRING)) { _method = null; } if (_method != null) { - _method = _method.toLowerCase(); - outputProperties.setProperty(OutputKeys.METHOD, _method); + _method = _method.toLowerCase(); + if ((_method.equals("xml"))|| + (_method.equals("html"))|| + (_method.equals("text"))|| + ((XMLChar.isValidQName(_method)&&(_method.indexOf(":") > 0)))) { + outputProperties.setProperty(OutputKeys.METHOD, _method); + } else { + reportError(this, parser, ErrorMsg.INVALID_METHOD_IN_OUTPUT, _method); + } } // Get the output encoding - any value accepted here @@ -241,8 +249,13 @@ // Make sure to store names in expanded form while (tokens.hasMoreTokens()) { + String qname = tokens.nextToken(); + if (!XMLChar.isValidQName(qname)) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, qname, this); + parser.reportError(Constants.ERROR, err); + } expandedNames.append( - parser.getQName(tokens.nextToken()).toString()).append(' '); + parser.getQName(qname).toString()).append(' '); } _cdata = expandedNames.toString(); if (_cdataToMerge != null) {
diff --git a/src/org/apache/xalan/xsltc/compiler/ProcessingInstruction.java b/src/org/apache/xalan/xsltc/compiler/ProcessingInstruction.java index bcc2d12..a3715e5 100644 --- a/src/org/apache/xalan/xsltc/compiler/ProcessingInstruction.java +++ b/src/org/apache/xalan/xsltc/compiler/ProcessingInstruction.java
@@ -63,24 +63,44 @@ package org.apache.xalan.xsltc.compiler; +import org.apache.bcel.generic.ALOAD; +import org.apache.bcel.generic.ASTORE; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.GETFIELD; import org.apache.bcel.generic.INVOKEINTERFACE; +import org.apache.bcel.generic.INVOKESTATIC; import org.apache.bcel.generic.INVOKEVIRTUAL; import org.apache.bcel.generic.InstructionList; +import org.apache.bcel.generic.LocalVariableGen; import org.apache.xalan.xsltc.compiler.util.ClassGenerator; import org.apache.xalan.xsltc.compiler.util.ErrorMsg; import org.apache.xalan.xsltc.compiler.util.MethodGenerator; import org.apache.xalan.xsltc.compiler.util.Type; import org.apache.xalan.xsltc.compiler.util.TypeCheckError; +import org.apache.xalan.xsltc.compiler.util.Util; +import org.apache.xml.utils.XMLChar; final class ProcessingInstruction extends Instruction { private AttributeValue _name; // name treated as AVT (7.1.3) + private boolean _isLiteral = false; // specified name is not AVT public void parseContents(Parser parser) { final String name = getAttribute("name"); - _name = AttributeValue.create(this, name, parser); + + if (name.length() > 0) { + _isLiteral = Util.isLiteral(name); + if (_isLiteral) { + if (!XMLChar.isValidNCName(name)) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_NCNAME_ERR, name, this); + parser.reportError(Constants.ERROR, err); + } + } + _name = AttributeValue.create(this, name, parser); + } + else + reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name"); + if (name.equals("xml")) { reportError(this, parser, ErrorMsg.ILLEGAL_PI_ERR, "xml"); } @@ -96,14 +116,41 @@ public void translate(ClassGenerator classGen, MethodGenerator methodGen) { final ConstantPoolGen cpg = classGen.getConstantPool(); final InstructionList il = methodGen.getInstructionList(); - - // Save the current handler base on the stack - il.append(methodGen.loadHandler()); - il.append(DUP); // first arg to "attributes" call - - // push attribute name - _name.translate(classGen, methodGen);// 2nd arg - + + if (!_isLiteral) { + // if the ncname is an AVT, then the ncname has to be checked at runtime if it is a valid ncname + LocalVariableGen nameValue = methodGen.addLocalVariable2("nameValue", + Util.getJCRefType(STRING_SIG), + il.getEnd()); + + // store the name into a variable first so _name.translate only needs to be called once + _name.translate(classGen, methodGen); + il.append(new ASTORE(nameValue.getIndex())); + il.append(new ALOAD(nameValue.getIndex())); + + // call checkNCName if the name is an AVT + final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkNCName", + "(" + +STRING_SIG + +")V"); + il.append(new INVOKESTATIC(check)); + + // Save the current handler base on the stack + il.append(methodGen.loadHandler()); + il.append(DUP); // first arg to "attributes" call + + // load name value again + il.append(new ALOAD(nameValue.getIndex())); + } else { + // Save the current handler base on the stack + il.append(methodGen.loadHandler()); + il.append(DUP); // first arg to "attributes" call + + // Push attribute name + _name.translate(classGen, methodGen);// 2nd arg + + } + il.append(classGen.loadTranslet()); il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS, "stringValueHandler",
diff --git a/src/org/apache/xalan/xsltc/compiler/Template.java b/src/org/apache/xalan/xsltc/compiler/Template.java index 4d2856c..3512e8b 100644 --- a/src/org/apache/xalan/xsltc/compiler/Template.java +++ b/src/org/apache/xalan/xsltc/compiler/Template.java
@@ -78,6 +78,8 @@ import org.apache.xalan.xsltc.compiler.util.Type; import org.apache.xalan.xsltc.compiler.util.TypeCheckError; import org.apache.xalan.xsltc.compiler.util.Util; +import org.apache.xml.utils.XMLChar; + public final class Template extends TopLevelElement { @@ -233,10 +235,18 @@ _stylesheet = super.getStylesheet(); if (name.length() > 0) { + if (!XMLChar.isValidQName(name)) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this); + parser.reportError(Constants.ERROR, err); + } _name = parser.getQNameIgnoreDefaultNs(name); } if (mode.length() > 0) { + if (!XMLChar.isValidQName(mode)) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, mode, this); + parser.reportError(Constants.ERROR, err); + } _mode = parser.getQNameIgnoreDefaultNs(mode); }
diff --git a/src/org/apache/xalan/xsltc/compiler/XslAttribute.java b/src/org/apache/xalan/xsltc/compiler/XslAttribute.java index eaff68d..d1cf936 100644 --- a/src/org/apache/xalan/xsltc/compiler/XslAttribute.java +++ b/src/org/apache/xalan/xsltc/compiler/XslAttribute.java
@@ -67,10 +67,14 @@ import java.util.Vector; +import org.apache.bcel.generic.ALOAD; +import org.apache.bcel.generic.ASTORE; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.GETFIELD; +import org.apache.bcel.generic.INVOKESTATIC; import org.apache.bcel.generic.INVOKEVIRTUAL; import org.apache.bcel.generic.InstructionList; +import org.apache.bcel.generic.LocalVariableGen; import org.apache.bcel.generic.PUSH; import org.apache.xalan.xsltc.compiler.util.ClassGenerator; import org.apache.xalan.xsltc.compiler.util.ErrorMsg; @@ -78,6 +82,7 @@ import org.apache.xalan.xsltc.compiler.util.Type; import org.apache.xalan.xsltc.compiler.util.TypeCheckError; import org.apache.xalan.xsltc.compiler.util.Util; +import org.apache.xml.utils.XMLChar; import org.apache.xml.serializer.ElemDesc; import org.apache.xml.serializer.SerializationHandler; @@ -88,6 +93,7 @@ private AttributeValue _name; // name treated as AVT (7.1.3) private AttributeValueTemplate _namespace = null; private boolean _ignore = false; + private boolean _isLiteral = false; // specified name is not AVT /** * Returns the name of the attribute @@ -117,10 +123,18 @@ QName qname = parser.getQName(name, false); final String prefix = qname.getPrefix(); - if ((prefix != null) && (prefix.equals(XMLNS_PREFIX))) { + if (((prefix != null) && (prefix.equals(XMLNS_PREFIX)))||(name.equals(XMLNS_PREFIX))) { reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name); return; } + + _isLiteral = Util.isLiteral(name); + if (_isLiteral) { + if (!XMLChar.isValidQName(name)) { + reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name); + return; + } + } // Ignore attribute if preceeded by some other type of element final SyntaxTreeNode parent = getParent(); @@ -190,11 +204,6 @@ } } - if (name.equals(XMLNS_PREFIX)) { - reportError(this, parser, ErrorMsg.ILLEGAL_ATTR_NAME_ERR, name); - return; - } - if (parent instanceof LiteralElement) { ((LiteralElement)parent).addAttribute(this); } @@ -222,7 +231,7 @@ final InstructionList il = methodGen.getInstructionList(); if (_ignore) return; - _ignore = true; + _ignore = true; // Compile code that emits any needed namespace declaration if (_namespace != null) { @@ -232,13 +241,40 @@ _namespace.translate(classGen,methodGen); il.append(methodGen.namespace()); } - - // Save the current handler base on the stack - il.append(methodGen.loadHandler()); - il.append(DUP); // first arg to "attributes" call - - // Push attribute name - _name.translate(classGen, methodGen);// 2nd arg + + if (!_isLiteral) { + // if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname + LocalVariableGen nameValue = methodGen.addLocalVariable2("nameValue", + Util.getJCRefType(STRING_SIG), + il.getEnd()); + + // store the name into a variable first so _name.translate only needs to be called once + _name.translate(classGen, methodGen); + il.append(new ASTORE(nameValue.getIndex())); + il.append(new ALOAD(nameValue.getIndex())); + + // call checkQName if the name is an AVT + final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkAttribQName", + "(" + +STRING_SIG + +")V"); + il.append(new INVOKESTATIC(check)); + + // Save the current handler base on the stack + il.append(methodGen.loadHandler()); + il.append(DUP); // first arg to "attributes" call + + // load name value again + il.append(new ALOAD(nameValue.getIndex())); + } else { + // Save the current handler base on the stack + il.append(methodGen.loadHandler()); + il.append(DUP); // first arg to "attributes" call + + // Push attribute name + _name.translate(classGen, methodGen);// 2nd arg + + } // Push attribute value - shortcut for literal strings if ((elementCount() == 1) && (elementAt(0) instanceof Text)) { @@ -282,8 +318,12 @@ // call "attribute" il.append(methodGen.attribute()); } + // Restore old handler base from stack il.append(methodGen.storeHandler()); + + + } }
diff --git a/src/org/apache/xalan/xsltc/compiler/XslElement.java b/src/org/apache/xalan/xsltc/compiler/XslElement.java index 2a1128f..5f987fd 100644 --- a/src/org/apache/xalan/xsltc/compiler/XslElement.java +++ b/src/org/apache/xalan/xsltc/compiler/XslElement.java
@@ -64,7 +64,10 @@ package org.apache.xalan.xsltc.compiler; +import org.apache.bcel.generic.ALOAD; +import org.apache.bcel.generic.ASTORE; import org.apache.bcel.generic.ConstantPoolGen; +import org.apache.bcel.generic.ICONST; import org.apache.bcel.generic.INVOKESTATIC; import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.LocalVariableGen; @@ -75,6 +78,7 @@ import org.apache.xalan.xsltc.compiler.util.Type; import org.apache.xalan.xsltc.compiler.util.TypeCheckError; import org.apache.xalan.xsltc.compiler.util.Util; +import org.apache.xml.utils.XMLChar; final class XslElement extends Instruction { @@ -101,39 +105,6 @@ 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(); @@ -152,9 +123,9 @@ String namespace = getAttribute("namespace"); // Optimize compilation when name is known at compile time - _isLiteralName = isLiteral(name); + _isLiteralName = Util.isLiteral(name); if (_isLiteralName) { - if (!isLegalName(name)) { + if (!XMLChar.isValidQName(name)) { ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_ELEM_NAME_ERR, name, this); parser.reportError(WARNING, msg); @@ -186,7 +157,7 @@ } else { if (prefix == EMPTYSTRING) { - if (isLiteral(namespace)) { + if (Util.isLiteral(namespace)) { prefix = lookupPrefix(namespace); if (prefix == null) { prefix = stable.generateNamespacePrefix(); @@ -213,6 +184,10 @@ final String useSets = getAttribute("use-attribute-sets"); if (useSets.length() > 0) { + if (!Util.isValidQNames(useSets)) { + ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, useSets, this); + parser.reportError(Constants.ERROR, err); + } setFirstElement(new UseAttributeSets(useSets, parser)); } @@ -283,11 +258,30 @@ } if (!_ignore) { - // Push handler for call to endElement() - il.append(methodGen.loadHandler()); - - // Push name and namespace URI - _name.translate(classGen, methodGen); + + // if the qname is an AVT, then the qname has to be checked at runtime if it is a valid qname + LocalVariableGen nameValue = methodGen.addLocalVariable2("nameValue", + Util.getJCRefType(STRING_SIG), + il.getEnd()); + + // store the name into a variable first so _name.translate only needs to be called once + _name.translate(classGen, methodGen); + il.append(new ASTORE(nameValue.getIndex())); + il.append(new ALOAD(nameValue.getIndex())); + + // call checkQName if the name is an AVT + final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "checkQName", + "(" + +STRING_SIG + +")V"); + il.append(new INVOKESTATIC(check)); + + // Push handler for call to endElement() + il.append(methodGen.loadHandler()); + + // load name value again + il.append(new ALOAD(nameValue.getIndex())); + if (_namespace != null) { _namespace.translate(classGen, methodGen); } @@ -299,14 +293,16 @@ il.append(methodGen.loadHandler()); il.append(methodGen.loadDOM()); il.append(methodGen.loadCurrentNode()); + + // Invoke BasisLibrary.startXslElemCheckQName() + il.append(new INVOKESTATIC( + cpg.addMethodref(BASIS_LIBRARY_CLASS, "startXslElement", + "(" + STRING_SIG + + STRING_SIG + + TRANSLET_OUTPUT_SIG + + DOM_INTF_SIG + "I)" + STRING_SIG))); - // Invoke BasisLibrary.startXslElement() - il.append(new INVOKESTATIC( - cpg.addMethodref(BASIS_LIBRARY_CLASS, "startXslElement", - "(" + STRING_SIG - + STRING_SIG - + TRANSLET_OUTPUT_SIG - + DOM_INTF_SIG + "I)" + STRING_SIG))); + } translateContents(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 65f0ca0..191fe93 100644 --- a/src/org/apache/xalan/xsltc/compiler/util/ErrorMessages.java +++ b/src/org/apache/xalan/xsltc/compiler/util/ErrorMessages.java
@@ -929,8 +929,38 @@ * stylesheet (see above). */ {ErrorMsg.RUNTIME_ERROR_KEY, - "Translet errors:"} + "Translet errors:"}, + + /* + * Note to translators: An attribute whose value is constrained to + * be a "QName" or a list of "QNames" had a value that was incorrect. + * 'QName' is an XML syntactic term that must not be translated. The + * substitution text contains the actual value of the attribute. + */ + {ErrorMsg.INVALID_QNAME_ERR, + "An attribute whose value must be a QName or whitespace-separated list of QNames had the value ''{0}''"}, + /* + * Note to translators: An attribute whose value is required to + * be an "NCName". + * 'NCName' is an XML syntactic term that must not be translated. The + * substitution text contains the actual value of the attribute. + */ + {ErrorMsg.INVALID_NCNAME_ERR, + "An attribute whose value must be an NCName had the value ''{0}''"}, + + /* + * Note to translators: An attribute with an incorrect value was + * encountered. The permitted value is one of the literal values + * "xml", "html" or "text"; it is also permitted to have the form of + * a QName that is not also an NCName. The terms "method", + * "xsl:output", "xml", "html" and "text" are keywords that must not + * be translated. The term "qname-but-not-ncname" is an XML syntactic + * term. The substitution text contains the actual value of the + * attribute. + */ + {ErrorMsg.INVALID_METHOD_IN_OUTPUT, + "The method attribute of an <xsl:output> element had the value ''{0}''. The value must be one of 'xml', 'html', 'text', or qname-but-not-ncname"} };
diff --git a/src/org/apache/xalan/xsltc/compiler/util/ErrorMsg.java b/src/org/apache/xalan/xsltc/compiler/util/ErrorMsg.java index 286005f..1954d03 100644 --- a/src/org/apache/xalan/xsltc/compiler/util/ErrorMsg.java +++ b/src/org/apache/xalan/xsltc/compiler/util/ErrorMsg.java
@@ -183,7 +183,10 @@ public static final String COULD_NOT_CREATE_TRANS_FACT = "COULD_NOT_CREATE_TRANS_FACT"; public static final String TRANSLET_NAME_JAVA_CONFLICT = "TRANSLET_NAME_JAVA_CONFLICT"; - + public static final String INVALID_QNAME_ERR = "INVALID_QNAME_ERR"; + public static final String INVALID_NCNAME_ERR = "INVALID_NCNAME_ERR"; + public static final String INVALID_METHOD_IN_OUTPUT = "INVALID_METHOD_IN_OUTPUT"; + // All error messages are localized and are stored in resource bundles. // This array and the following 4 strings are read from that bundle. private static ResourceBundle _bundle;
diff --git a/src/org/apache/xalan/xsltc/compiler/util/Util.java b/src/org/apache/xalan/xsltc/compiler/util/Util.java index 4fbdce2..6bc28fa 100644 --- a/src/org/apache/xalan/xsltc/compiler/util/Util.java +++ b/src/org/apache/xalan/xsltc/compiler/util/Util.java
@@ -63,8 +63,11 @@ package org.apache.xalan.xsltc.compiler.util; +import java.util.StringTokenizer; + import org.apache.bcel.generic.Type; import org.apache.xalan.xsltc.compiler.Constants; +import org.apache.xml.utils.XMLChar; public final class Util { static public char filesep; @@ -203,6 +206,35 @@ final int index = qname.lastIndexOf(":"); return (index > 0) ? qname.substring(0, index) : Constants.EMPTYSTRING; + } + + /** + * Checks if the string is a literal (i.e. not an AVT) or not. + */ + public static boolean isLiteral(String str) { + final int length = str.length(); + for (int i = 0; i < length - 1; i++) { + if (str.charAt(i) == '{' && str.charAt(i + 1) != '{') { + return false; + } + } + return true; } + + /** + * Checks if the string is valid list of qnames + */ + public static boolean isValidQNames(String str) { + if ((str != null) && (!str.equals(Constants.EMPTYSTRING))) { + final StringTokenizer tokens = new StringTokenizer(str); + while (tokens.hasMoreTokens()) { + if (!XMLChar.isValidQName(tokens.nextToken())) { + return false; + } + } + } + return true; + } + }
diff --git a/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java b/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java index 41d1cc2..f7a01d6 100644 --- a/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java +++ b/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java
@@ -93,7 +93,9 @@ import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; +import org.apache.xml.serializer.NamespaceMappings; import org.apache.xml.serializer.SerializationHandler; +import org.apache.xml.utils.XMLChar; /** * Standard XSLT functions. All standard functions expect the current node @@ -1282,49 +1284,112 @@ runTimeError(RUN_TIME_COPY_ERR); } } + + /** + * Utility function to check if xsl:attribute has a valid qname + * This method should only be invoked if the name attribute is an AVT + */ + public static void checkAttribQName(String name) { + final int firstOccur = name.indexOf(":"); + final int lastOccur = name.lastIndexOf(":"); + final String localName = name.substring(lastOccur + 1); + + if (firstOccur > 0) { + final String newPrefix = name.substring(0, firstOccur); + + if (firstOccur != lastOccur) { + final String oriPrefix = name.substring(firstOccur+1, lastOccur); + if (!XMLChar.isValidNCName(oriPrefix)) { + // even though the orignal prefix is ignored, it should still get checked for valid NCName + runTimeError(INVALID_QNAME_ERR,oriPrefix+":"+localName); + } + } + + // prefix must be a valid NCName + if (!XMLChar.isValidNCName(newPrefix)) { + runTimeError(INVALID_QNAME_ERR,newPrefix+":"+localName); + } + } + + // local name must be a valid NCName and must not be XMLNS + if ((!XMLChar.isValidNCName(localName))||(localName.equals(Constants.XMLNS_PREFIX))) { + runTimeError(INVALID_QNAME_ERR,localName); + } + } + + /** + * Utility function to check if a name is a valid ncname + * This method should only be invoked if the attribute value is an AVT + */ + public static void checkNCName(String name) { + if (!XMLChar.isValidNCName(name)) { + runTimeError(INVALID_NCNAME_ERR,name); + } + } /** + * Utility function to check if a name is a valid qname + * This method should only be invoked if the attribute value is an AVT + */ + public static void checkQName(String name) { + if (!XMLChar.isValidQName(name)) { + runTimeError(INVALID_QNAME_ERR,name); + } + } + + /** * Utility function for the implementation of xsl:element. */ public static String startXslElement(String qname, String namespace, SerializationHandler handler, DOM dom, int node) { - try { - // Get prefix from qname - String prefix; - final int index = qname.indexOf(':'); - - if (index > 0) { - prefix = qname.substring(0, index); - - // Handle case when prefix is not known at compile time - if (namespace == null || namespace.length() == 0) { - namespace = dom.lookupNamespace(node, prefix); - } - - handler.startElement(namespace, qname.substring(index+1), - qname); - handler.namespaceAfterStartElement(prefix, namespace); - } - else { - // Need to generate a prefix? - if (namespace != null && namespace.length() > 0) { - prefix = generatePrefix(); - qname = prefix + ':' + qname; - handler.startElement(namespace, qname, qname); - handler.namespaceAfterStartElement(prefix, namespace); - } - else { - handler.startElement(null, null, qname); - } - } - } - catch (SAXException e) { - throw new RuntimeException(e.getMessage()); - } - - return qname; - } + try { + // Get prefix from qname + String prefix; + final int index = qname.indexOf(':'); + + if (index > 0) { + prefix = qname.substring(0, index); + + // Handle case when prefix is not known at compile time + if (namespace == null || namespace.length() == 0) { + try { + // not sure if this line of code ever works + namespace = dom.lookupNamespace(node, prefix); + } + catch(RuntimeException e) { + handler.flushPending(); // need to flush or else can't get namespacemappings + NamespaceMappings nm = handler.getNamespaceMappings(); + namespace = nm.lookupNamespace(prefix); + if (namespace == null) { + runTimeError(NAMESPACE_PREFIX_ERR,prefix); + } + } + } + + handler.startElement(namespace, qname.substring(index+1), + qname); + handler.namespaceAfterStartElement(prefix, namespace); + } + else { + // Need to generate a prefix? + if (namespace != null && namespace.length() > 0) { + prefix = generatePrefix(); + qname = prefix + ':' + qname; + handler.startElement(namespace, qname, qname); + handler.namespaceAfterStartElement(prefix, namespace); + } + else { + handler.startElement(null, null, qname); + } + } + } + catch (SAXException e) { + throw new RuntimeException(e.getMessage()); + } + + return qname; + } /** * This function is used in the execution of xsl:element @@ -1382,6 +1447,8 @@ "UNSUPPORTED_EXT_ERR"; public static final String UNKNOWN_TRANSLET_VERSION_ERR = "UNKNOWN_TRANSLET_VERSION_ERR"; + public static final String INVALID_QNAME_ERR = "INVALID_QNAME_ERR"; + public static final String INVALID_NCNAME_ERR = "INVALID_NCNAME_ERR"; // All error messages are localized and are stored in resource bundles. protected static ResourceBundle m_bundle;
diff --git a/src/org/apache/xalan/xsltc/runtime/ErrorMessages.java b/src/org/apache/xalan/xsltc/runtime/ErrorMessages.java index 57b9818..2732743 100644 --- a/src/org/apache/xalan/xsltc/runtime/ErrorMessages.java +++ b/src/org/apache/xalan/xsltc/runtime/ErrorMessages.java
@@ -278,14 +278,33 @@ "Unrecognized XSLTC extension ''{0}''"}, - // - // Note to translators: This error message is produced if the translet - // class was compiled using a newer version of XSLTC and deployed for - // execution with an older version of XSLTC. The substitution text is - // the name of the translet class. - // + /* + * Note to translators: This error message is produced if the translet + * class was compiled using a newer version of XSLTC and deployed for + * execution with an older version of XSLTC. The substitution text is + * the name of the translet class. + */ {BasisLibrary.UNKNOWN_TRANSLET_VERSION_ERR, - "The specified translet, ''{0}'', was created using a version of XSLTC more recent than the version of the XSLTC run-time that is in use. You must recompile the stylesheet or use a more recent version of XSLTC to run this translet."} + "The specified translet, ''{0}'', was created using a version of XSLTC more recent than the version of the XSLTC run-time that is in use. You must recompile the stylesheet or use a more recent version of XSLTC to run this translet."}, + + /* + * Note to translators: An attribute whose effective value is required + * to be a "QName" had a value that was incorrect. + * 'QName' is an XML syntactic term that must not be translated. The + * substitution text contains the actual value of the attribute. + */ + {BasisLibrary.INVALID_QNAME_ERR, + "An attribute whose value must be a QName had the value ''{0}''"}, + + + /* + * Note to translators: An attribute whose effective value is required + * to be a "NCName" had a value that was incorrect. + * 'NCName' is an XML syntactic term that must not be translated. The + * substitution text contains the actual value of the attribute. + */ + {BasisLibrary.INVALID_NCNAME_ERR, + "An attribute whose value must be an NCName had the value ''{0}''"}, }; public Object[][] getContents() {
diff --git a/src/org/apache/xml/utils/XMLChar.java b/src/org/apache/xml/utils/XMLChar.java index cf3a1c2..bda89de 100644 --- a/src/org/apache/xml/utils/XMLChar.java +++ b/src/org/apache/xml/utils/XMLChar.java
@@ -677,5 +677,28 @@ } return false; } // isValidIANAEncoding(String):boolean + + /** + * 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> is legal. + */ + public static boolean isValidQName(String str) { + + final int colon = str.indexOf(':'); + + if (colon == 0 || colon == str.length() - 1) { + return false; + } + + if (colon > 0) { + final String prefix = str.substring(0,colon); + final String localPart = str.substring(colon+1); + return isValidNCName(prefix) && isValidNCName(localPart); + } + else { + return isValidNCName(str); + } + } } // class XMLChar