diff --git a/xalan/src/main/java/org/apache/xalan/xsltc/compiler/Constants.java b/xalan/src/main/java/org/apache/xalan/xsltc/compiler/Constants.java index 865d542d9..0b6da35bc 100644 --- a/xalan/src/main/java/org/apache/xalan/xsltc/compiler/Constants.java +++ b/xalan/src/main/java/org/apache/xalan/xsltc/compiler/Constants.java @@ -174,6 +174,10 @@ public interface Constants extends InstructionConstants { = "org.apache.xalan.xsltc.dom.SingletonIterator"; public static final String MATCHING_ITERATOR = "org.apache.xalan.xsltc.dom.MatchingIterator"; + public static final String HASHMAP_CLASS = "java.util.HashMap"; + public static final String HASHMAP_SIG = "Ljava/util/HashMap;"; + public static final String MAP_SIG = "Ljava/util/Map;"; + public static final String NODE_SIG = "I"; public static final String GET_PARENT @@ -409,6 +413,13 @@ public interface Constants extends InstructionConstants { + PREFIX_URIS_IDX_SIG + PREFIX_URIS_ARRAY_SIG + "Z)" + STRING_SIG; + public static final String ADD_NAMESPACE_PREFIX_REF + = "addPrefix"; + public static final String ADD_NAMESPACE_PREFIX_SIG + = "(" + STRING_SIG + + STRING_SIG + + "Ljava/util/Map;"+ + ")" + STRING_SIG; public static final String DOM_FIELD = "_dom"; @@ -430,6 +441,7 @@ public interface Constants extends InstructionConstants { = "_scharData"; public static final String STATIC_CHAR_DATA_FIELD_SIG = "[C"; + public static final String STATIC_NS_MAP_FIELD = "_sNamespaceMap"; public static final String FORMAT_SYMBOLS_FIELD = "format_symbols"; diff --git a/xalan/src/main/java/org/apache/xalan/xsltc/compiler/Stylesheet.java b/xalan/src/main/java/org/apache/xalan/xsltc/compiler/Stylesheet.java index 7c7bda102..224f17e62 100644 --- a/xalan/src/main/java/org/apache/xalan/xsltc/compiler/Stylesheet.java +++ b/xalan/src/main/java/org/apache/xalan/xsltc/compiler/Stylesheet.java @@ -808,6 +808,7 @@ private void compileStaticInitializer(ClassGenerator classGen) { addStaticField(classGen, "[" + STRING_SIG, STATIC_URIS_ARRAY_FIELD); addStaticField(classGen, "[I", STATIC_TYPES_ARRAY_FIELD); addStaticField(classGen, "[" + STRING_SIG, STATIC_NAMESPACE_ARRAY_FIELD); + addStaticField(classGen, MAP_SIG, STATIC_NS_MAP_FIELD ); // Create fields of type char[] that will contain literal text from // the stylesheet. final int charDataFieldCount = getXSLTC().getCharacterDataCount(); @@ -927,6 +928,20 @@ private void compileStaticInitializer(ClassGenerator classGen) { staticConst.markChunkEnd(); } + // Create the namespace -> prefix map + staticConst.markChunkStart(); + il.append(new NEW(cpg.addClass(HASHMAP_CLASS))); + il.append(DUP); + final int init = cpg.addMethodref(HASHMAP_CLASS, "","()V"); + il.append(new INVOKESPECIAL(init)); + + int nsMapRef = cpg.addFieldref(_className, + STATIC_NS_MAP_FIELD, + MAP_SIG); + il.append(new PUTSTATIC(nsMapRef)); + staticConst.markChunkEnd(); + + // Put the tree of stylesheet namespace declarations into the translet final Vector namespaceAncestors = getXSLTC().getNSAncestorPointers(); if (namespaceAncestors != null && namespaceAncestors.size() != 0) { diff --git a/xalan/src/main/java/org/apache/xalan/xsltc/compiler/XslElement.java b/xalan/src/main/java/org/apache/xalan/xsltc/compiler/XslElement.java index ecf59ad15..2beaa06a5 100644 --- a/xalan/src/main/java/org/apache/xalan/xsltc/compiler/XslElement.java +++ b/xalan/src/main/java/org/apache/xalan/xsltc/compiler/XslElement.java @@ -47,6 +47,7 @@ final class XslElement extends Instruction { private String _prefix; private boolean _ignore = false; private boolean _isLiteralName = true; + private boolean _isLiteralNamespace = false; private AttributeValueTemplate _name; private AttributeValueTemplate _namespace; @@ -81,8 +82,7 @@ public void parseContents(Parser parser) { return; } - // Get namespace attribute - String namespace = getAttribute("namespace"); + String namespace = getAttribute("namespace"); // Optimize compilation when name is known at compile time _isLiteralName = Util.isLiteral(name); @@ -141,6 +141,11 @@ public void parseContents(Parser parser) { // name attribute contains variable parts. If there is no namespace // attribute, the generated code needs to be prepared to look up // any prefix in the stylesheet at run-time. + // if the namespace is literal, we can avoid generating a new + // prefix every time though + if (Util.isLiteral(namespace)) { + _isLiteralNamespace = true; + } _namespace = (namespace == EMPTYSTRING) ? null : new AttributeValueTemplate(namespace, parser, this); } @@ -245,13 +250,42 @@ public void translate(ClassGenerator classGen, MethodGenerator methodGen) { // Push handler for call to endElement() il.append(methodGen.loadHandler()); - // load name value again - nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex()))); - - if (_namespace != null) { - _namespace.translate(classGen, methodGen); - } - else { + if (_namespace != null) { + // If the namespace is a literal, we'll want to avoid creating a new + // prefix for it + if(_isLiteralNamespace){ + // we want to call to BasisLibrary.addPrefix(String qname, String namespace, Map prefixMap); + // So store the namespace in a var + LocalVariableGen namespaceValue = methodGen.addLocalVariable2( + "namespaceValue", Util.getJCRefType(STRING_SIG), null); + _namespace.translate(classGen, methodGen); + namespaceValue.setStart(il.append(new ASTORE(namespaceValue.getIndex()))); + + String transletClassName = getXSLTC().getClassName(); + // load name value again + nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex()))); + + il.append(new ALOAD(namespaceValue.getIndex())); + il.append(new GETSTATIC(cpg.addFieldref( + transletClassName, + STATIC_NS_MAP_FIELD, + MAP_SIG))); + il.append( + new INVOKESTATIC( + cpg.addMethodref(BASIS_LIBRARY_CLASS, + ADD_NAMESPACE_PREFIX_REF, + ADD_NAMESPACE_PREFIX_SIG))); + namespaceValue.setEnd(il.append(new ALOAD(namespaceValue.getIndex()))); + } + else{ + // load name value again + nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex()))); + _namespace.translate(classGen, methodGen); + } + } + else { + // load name value again + nameValue.setEnd(il.append(new ALOAD(nameValue.getIndex()))); // If name is an AVT and namespace is not specified, need to // look up any prefix in the stylesheet by calling // BasisLibrary.lookupStylesheetQNameNamespace( diff --git a/xalan/src/main/java/org/apache/xalan/xsltc/runtime/BasisLibrary.java b/xalan/src/main/java/org/apache/xalan/xsltc/runtime/BasisLibrary.java index d049df2c0..c0ff81884 100644 --- a/xalan/src/main/java/org/apache/xalan/xsltc/runtime/BasisLibrary.java +++ b/xalan/src/main/java/org/apache/xalan/xsltc/runtime/BasisLibrary.java @@ -27,6 +27,9 @@ import java.text.NumberFormat; import java.util.Locale; import java.util.ResourceBundle; +import java.util.HashMap; +import java.util.Map; +import java.util.Collections; import javax.xml.transform.dom.DOMSource; @@ -1599,8 +1602,42 @@ public static String getPrefix(String qname) { * This function is used in the execution of xsl:element */ private static int prefixIndex = 0; // not thread safe!! + private static Map nsMap = Collections.synchronizedMap(new HashMap()); public static String generatePrefix() { - return ("ns" + prefixIndex++); + return ("ns" + prefixIndex++); + } + // This will record a new prefix used globally by XSLTC + // it *shouldn't be used anymore, but there might still be an edge case + public static String generatePrefix(String namespace) { + if(nsMap.containsKey(namespace)){ + return nsMap.get(namespace); + } + else{ + // Create a prefix, add to map and return it + String result = generatePrefix(); + nsMap.put(namespace,result); + return result; + } + } + // This will record a new prefix (if there isn't one already) + // and add it to the qname if it doesn't already have a prefix. + // This stores the new prefix in the map belonging to the translet + // so we don't leak prefix between separate translets. + public static String addPrefix(String qname, String namespace, Map prefixMap){ + // if it already has a prefix we don't need to do anything + if(qname.indexOf(':') == -1){ + if(prefixMap.containsKey(namespace)){ + return prefixMap.get(namespace)+":"+qname; + } + else{ + String prefix = "ns"+prefixMap.size(); + prefixMap.put(namespace,prefix); + return prefix+":"+qname; + } + } + else{ + return qname; + } } public static final String RUN_TIME_INTERNAL_ERR =