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