0

I'm trying to compile an XSLT transformation using XSLTC, but the resulting class is not useable, because it contains methods with illegal names.

For illustration purposes, here is a (simplified) version of the stylesheet I use:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ns1="http://some.weird/Namespace#/Definition"
    xmlns="http://another.strange/Namespace#/Definition">

    <xsl:template match="ns1:*">
        <xsl:element name="{local-name()}">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="text()">
        <xsl:value-of select="." />
    </xsl:template>
</xsl:stylesheet>

What this is trying to accomplish is to change the namespace of all the elements of an XML file to a different one, so I'm basically using a modified identity transformation. However, XSLTC seems to have a problem with the '#' characters in the namespace URIs (even though as far as I understand, they should be legal). Because the class file that XSLTC creates when compiling the above looks like this (decompiled, of course):

import org.apache.xalan.xsltc.DOM;
import org.apache.xalan.xsltc.TransletException;
import org.apache.xalan.xsltc.dom.UnionIterator;
import org.apache.xalan.xsltc.runtime.AbstractTranslet;
import org.apache.xalan.xsltc.runtime.BasisLibrary;
import org.apache.xml.dtm.DTMAxisIterator;
import org.apache.xml.serializer.SerializationHandler;

public class ChangeNamespace extends AbstractTranslet {
    public DOM _dom;
    protected static String[] _sNamesArray = new String[0];
    protected static String[] _sUrisArray = new String[0];
    protected static int[] _sTypesArray = new int[0];
    protected static String[] _sNamespaceArray = new String[0];

    public void buildKeys(DOM var1, DTMAxisIterator var2, SerializationHandler var3, int var4) throws TransletException {
    }

    public void topLevel(DOM var1, DTMAxisIterator var2, SerializationHandler var3) throws TransletException {
        boolean var4 = false;
    }

    public void transform(DOM var1, DTMAxisIterator var2, SerializationHandler var3) throws TransletException {
        this._dom = this.makeDOMAdapter(var1);
        boolean var4 = false;
        this.transferOutputSettings(var3);
        this.buildKeys(this._dom, var2, var3, 0);
        this.topLevel(this._dom, var2, var3);
        var3.startDocument();
        this.applyTemplates(this._dom, var2, var3);
        var3.endDocument();
    }

    public void http$colon$$slash$$slash$another$dot$strange$slash$Namespace#$slash$Definition$colon$template$dot$0(DOM var1, DTMAxisIterator var2, SerializationHandler var3, int var4) {
        String var5 = BasisLibrary.getLocalName(var1.getNodeName(var4));
        BasisLibrary.checkQName(var5);
        String var10001 = BasisLibrary.startXslElement(var5, (String)null, var3, var1, var4);
        this.applyTemplates(var1, var1.getChildren(var4), var3);
        var3.endElement(var10001);
    }

    public void http$colon$$slash$$slash$another$dot$strange$slash$Namespace#$slash$Definition$colon$template$dot$1(DOM var1, DTMAxisIterator var2, SerializationHandler var3, int var4) {
        String var5;
        if((var5 = var1.shallowCopy(var4, var3)) != null) {
            int var6 = var5.length();
            this.applyTemplates(var1, (new UnionIterator(var1)).addIterator(var1.getAxisIterator(2)).addIterator(var1.getAxisIterator(3)).setStartNode(var4), var3);
            if(var6 != 0) {
                var3.endElement(var5);
            }
        }

    }

    public void http$colon$$slash$$slash$another$dot$strange$slash$Namespace#$slash$Definition$colon$template$dot$2(DOM var1, DTMAxisIterator var2, SerializationHandler var3, int var4) {
        var1.characters(var4, var3);
    }

    public final void applyTemplates(DOM var1, DTMAxisIterator var2, SerializationHandler var3) throws TransletException {
        int var4;
        while((var4 = var2.next()) >= 0) {
            switch(var1.getExpandedTypeID(var4)) {
            case 0:
            case 9:
                this.applyTemplates(var1, var1.getChildren(var4), var3);
                break;
            case 1:
                if(var1.getNamespaceName(var4).equals("http://some.weird/Namespace#/Definition")) {
                    this.http$colon$$slash$$slash$another$dot$strange$slash$Namespace#$slash$Definition$colon$template$dot$0(var1, var2, var3, var4);
                    break;
                } else {
                    var4 = var4;
                }
            case 2:
            case 7:
            case 8:
                this.http$colon$$slash$$slash$another$dot$strange$slash$Namespace#$slash$Definition$colon$template$dot$1(var1, var2, var3, var4);
                break;
            case 3:
                this.http$colon$$slash$$slash$another$dot$strange$slash$Namespace#$slash$Definition$colon$template$dot$2(var1, var2, var3, var4);
            case 4:
            case 5:
            case 6:
            case 10:
            case 11:
            case 12:
            case 13:
            }
        }

    }

    public ChangeNamespace() {
        super.namesArray = _sNamesArray;
        super.urisArray = _sUrisArray;
        super.typesArray = _sTypesArray;
        super.namespaceArray = _sNamespaceArray;
        super.transletVersion = 101;
    }
}

Note the '#' in some of the generated method names, which is illegal according to the Java spec, and no surprise that it results in a ClassFormatException courtesy of the classloader when I actually try to use the class.

Any ideas if I can somehow get XSLTC to compile the XSL into something valid? Can I perhaps modify my stylesheet somehow to accomplish the same thing that doesn't cause this problem?

And no, I can not change the namespaces themselves, they're fixed because they belong to an external system that I have to deal with but have no influence on.

4

1 回答 1

0

通过调试 XSLTC 的代码进一步调查问题,我设法将原因缩小到escape对类中命名的方法的调用org.apache.xalan.xsltc.compiler.util.Util

这是实际源代码(Xalan 2.7.2)中的样子:

public static String escape(String input) {
    return replace(input, ".-/:", new String[]{"$dot$", "$dash$", "$slash$", "$colon$"});
}

当尝试从字符串创建方法名称时(例如,在我的例子中,命名空间),它会从多个地方调用,尽管这种幼稚的实现不足以达到这个目的,正如我的案例所证明的那样。返回的字符串无需任何进一步的修改或检查即可使用,以确保生成的名称在 Java 中实际上是有效的。

我的解决方案(我不会在这里详细介绍,因为它不是一个好的解决方案 - 它不适合胆小的人,任何尝试做同样事情的人都应该有足够的专业知识来复制它而无需额外的指导,或远离这种诡计),实际上是使用字节码操作将上述方法的主体替换为也处理“#”字符的东西。这是一个绝望的举动,但至少现在对我有用。

于 2016-12-02T08:27:59.890 回答