jkesselm 00/09/20 16:33:13
Modified: java/src/org/apache/xalan/templates AVT.java
ElemLiteralResult.java ElemTemplateElement.java
Stylesheet.java StylesheetComposed.java
java/src/org/apache/xalan/transformer TransformerImpl.java
Added: java/src/org/apache/xalan/processor CompiledTemplate.java
CompilingStylesheetHandler.java
CompilingStylesheetProcessor.java
Log:
Just the start of an experiment in compiling stylesheets to Java.
Only handles ElemTemplate and ElemLiteralElement at this time;
other nodes continue to run interpreted. Generated code is
not yet efficient, nor serializable, nor (sigh) reeentrant; working
on all those issues. Currently relies on a routine in BSF to invoke
the Java compiler; we should probably adopt that into
synthetic.Class.
To try it out, change the trax.processor.xslt to
org.apache.xalan.processor.CompiledStylesheetProcessor
It does pass our regression tests; the only known differences are
in attribute order.
Revision Changes Path
1.1
xml-xalan/java/src/org/apache/xalan/processor/CompiledTemplate.java
Index: CompiledTemplate.java
===================================================================
/** Interface for generated template classes */
package org.apache.xalan.processor;
public interface CompiledTemplate
{
public void execute(org.apache.xalan.transformer.TransformerImpl
transformer,org.w3c.dom.Node sourceNode,org.apache.xalan.utils.QName mode)
throws org.xml.sax.SAXException;
}
1.1
xml-xalan/java/src/org/apache/xalan/processor/CompilingStylesheetHandler.java
Index: CompilingStylesheetHandler.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 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 [EMAIL PROTECTED]
*
* 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) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xalan.processor;
import java.net.URL;
import java.util.Stack;
import java.util.Vector;
import java.io.File;
import java.util.StringTokenizer;
import org.apache.xalan.templates.Constants;
import org.apache.xalan.templates.ElemTemplateElement;
import org.apache.xalan.templates.ElemTemplate;
import org.apache.xalan.templates.ElemLiteralResult;
import org.apache.xalan.templates.ElemAttributeSet;
import org.apache.xalan.templates.ElemAttribute;
import org.apache.xalan.templates.StylesheetRoot;
import org.apache.xalan.templates.Stylesheet;
import org.apache.xalan.templates.XMLNSDecl;
import trax.ProcessorException;
import trax.TemplatesBuilder;
import trax.Templates;
import trax.TransformException;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathFactory;
import org.apache.xpath.compiler.XPathParser;
import org.apache.xpath.compiler.FunctionTable;
import org.apache.xpath.functions.Function;
import org.apache.xalan.res.XSLMessages;
import org.apache.xalan.res.XSLTErrorResources;
import org.apache.xalan.utils.PrefixResolver;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.helpers.NamespaceSupport;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* <meta name="usage" content="advanced"/>
* Initializes and processes a stylesheet via SAX events.
* This differs from StylesheetHandler in adding a post-
* processing stage which attempts to replace part or all
* of the recursively-interpreted Templates collection with
* Java code, which is generated, compiled, and patched
* back into the tempate trees.
* @see StylesheetHandler
*/
public class CompilingStylesheetHandler
extends StylesheetHandler
{
/**
* Create a StylesheetHandler object, creating a root stylesheet
* as the target.
* @exception May throw ProcessorException if a StylesheetRoot
* can not be constructed for some reason.
*/
public CompilingStylesheetHandler(StylesheetProcessor processor)
throws ProcessorException
{
super(processor);
}
/**
* Receive notification of the end of the document.
* Run standard cleanup of the internal representation,
* then start trying to replace that rep with custom code.
*
* @exception org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#endDocument
*/
public void endDocument ()
throws SAXException
{
super.endDocument();
Stylesheet current=getStylesheet();
if(current==getStylesheetRoot())
{
// Begin compiling. Loop modeled on StylesheetRoot.recompose()
// calling recomposeTemplates().
StylesheetRoot root=getStylesheetRoot();
// loop from recompose()
int nImports = root.getGlobalImportCount();
for(int imp = 0; imp < nImports; imp++)
{
org.apache.xalan.templates.StylesheetComposed
sheet = root.getGlobalImport(imp);
// loop from sheet.recomposeTemplates
// Scan both main and included stylesheets
int nIncludes = sheet.getIncludeCountComposed();
for(int k = nIncludes-1; k >= -1; k--)
{
Stylesheet included = (-1 == k) ? sheet :
sheet.getIncludeComposed(k);
int n = included.getTemplateCount();
for(int i = 0; i < n; i++)
{
ElemTemplate t=included.getTemplate(i);
if(!(t instanceof CompiledTemplate))
{
ElemTemplate newT=compileTemplate(t);
if(newT!=t)
included.replaceTemplate(newT,i);
}
}
}
// Need to rebuild each sheet's cache.
sheet.recomposeTemplates(true);
}
// TODO: ***** Do we need to reconsider the StylesheetRoot?
// (The old Recompose option does so. I don't _think_ it's
needed here.)
// After compiling I think we have to reconstruct the cached
// "composed templates" set.
// NOTE: RECOMPOSE WAS A ONE-TIME OP
// I've set up an alternate entry that allows me to flush
// before composing. That could take over from the standard
// entry point... Or flush might be made the new default
// behavior; I don't know whether that would be appropriate.
root.recomposeTemplates(true);
}
}
/**
Analyse an <xsl:template> tree, converting some (most?)
of its recursive evaluate() behavior into straight-line
Java code. That code is then compiled and instantiated
to produce a new "equivalent" object, which can be used
to replace the original Template.
Note that the compiled Template may have to reference
children that we don't yet know how to compile. This
is done by copying references to those kids into a vector,
and having the generated code invoke them via offsets
in that vector.
At this time, the compiler's rather simpleminded. No
common subexpression extraction is performed, and no
attempt is made to optimize across stylesheets. We're
just flattening some of the code and reorganizing it
into direct SAX invocations.
Literal result elements become SAX begin/endElement
Context-insensitive attributes become literal assignment
to the attribute trees.
xsl:choose and xsl:for-each may be flattened
Namespace declarations become SAX prefix operations.
***** NS aliasing really should have occured before we get here,
as should most NS resolution. The annoying exception is when
xsl:attribute has a prefixed name but no explicit namespace,
a feature which I Really Wish had not been supported.
Other nodes simply have their .evaluate() invoked
TODO: Their children really should be walked for further compilation
opportunities.
TODO: ***** OPTIMIZATION: We should preload/cache the synthetic.Class
objects rather than doing forName/forClass lookups every time.
*/
ElemTemplate compileTemplate(ElemTemplate source)
{
ElemTemplate instance=source;
String className=generateUniqueClassName();
try
{
// public class ACompiledTemplate000... extends ElemTemplate
// implements CompiledTemplate
synthetic.Class tClass=
synthetic.Class.declareClass(className);
tClass.setModifiers(java.lang.reflect.Modifier.PUBLIC);
tClass.setSuperClass(ElemTemplate.class);
tClass.addImplements(tClass.forName("org.apache.xalan.processor.CompiledTemplate"));
// Object[] interpretArray is used to
// bind to nodes we don't yet know how to compile.
// Set at construction. ElemTemplateElements and AVTs...
// Synthesis needs a more elegant way to declare array classes
// given a base class...
synthetic.reflection.Field interpretArray=
tClass.declareField("interpretArray");
// org.apache.xalan.templates.ElemTemplateElement
interpretArray.setType(tClass.forName("java.lang.Object[]"));
// Namespace context tracking. Note that this is dynamic state
// during execution, _NOT_ the static state tied to a single
// ElemTemplateElement during parsing
//GONK//
// TODO: ***** PROBLEM: THIS ISN'T THREADSAFE. It needs to be
// a class field rather than an automatic in order to support
// callback via getNamespaceForPrefix. But it also needs to be
// thread-specific. Unfortunately, that callback doesn't pass
// the xctxt as a parameter, so we can't stash it there.
// Best thought I've got is a Thread-indexed hashtable... ugh.
synthetic.reflection.Field m_nsSupport=
tClass.declareField("m_nsSupport");
m_nsSupport.setType(tClass.forClass(org.xml.sax.helpers.NamespaceSupport.class));
m_nsSupport.setInitializer("new
org.xml.sax.helpers.NamespaceSupport()");
// And accessor, to let kids query current state
synthetic.reflection.Method getNSURI =
tClass.declareMethod("getNamespaceForPrefix");
getNSURI.addParameter(tClass.forClass(java.lang.String.class),"nsprefix");
getNSURI.setReturnType(tClass.forClass(java.lang.String.class));
getNSURI.setModifiers(java.lang.reflect.Modifier.PUBLIC);
getNSURI.getBody().append(
"String nsuri=m_nsSupport.getURI(nsprefix);\n"
+"if(null==nsuri || nsuri.length()==0)\n"
+"nsuri=m_parentNode.getNamespaceForPrefix(nsprefix);\n"
+"return nsuri;\n"
);
// public constructor: Copy values from original
// template object, pick up "uncompiled children"
// array from compilation/instantiation process.
synthetic.reflection.Constructor ctor=
tClass.declareConstructor();
ctor.setModifiers(java.lang.reflect.Modifier.PUBLIC);
ctor.addParameter(tClass.getSuperclass(),"original");
ctor.addParameter(interpretArray.getType(),"interpretArray");
// It'd be easiest in most cases to copy values direct from the
// "original" template during instantiation. However, I want to
make
// as many as possible into literals, for efficiency and
debugability.
ctor.getBody().append(
"org.xml.sax.helpers.LocatorImpl locator=new
org.xml.sax.helpers.LocatorImpl();\n"
+"locator.setLineNumber("+source.getLineNumber()+");\n"
+"locator.setColumnNumber("+source.getColumnNumber()+");\n"
+"locator.setPublicId("+makeQuotedString(source.getPublicId())+");\n"
+"locator.setSystemId("+makeQuotedString(source.getSystemId())+");\n"
+"setLocaterInfo(locator); // yes, or/er clash\n"
+"// uncompiled descendents/children\n"
+"this.interpretArray=interpretArray;\n"
+"// other context values which seem to be needed\n"
+"setMatch(original.getMatch());\n"
+"setMode(original.getMode());\n"
+"setName(original.getName());\n"
+"setPriority(original.getPriority());\n"
+"setStylesheet(original.getStylesheet());\n\n"
//+"setLocatorInfo(new
Locator(original.getPublicID(),original.getSystemID()));\n"
// Reparent the interpreted ElemTemplateElements,
// to break their dependency on interpretive code.
// AVTs in this array don't need proceessing, as they are
// apparently not directly aware of their Elements.
+"for(int i=0;i<interpretArray.length;++i)\n{\n"
+"\tif(interpretArray[i] instanceof
org.apache.xalan.templates.ElemTemplateElement)\n"
+"\t{\n"
+"\torg.apache.xalan.templates.ElemTemplateElement
ete=(org.apache.xalan.templates.ElemTemplateElement)interpretArray[i];\n"
// Append alone is not enough; it's lightweight, and assumes
// the child had no previous parent. Need to remove first.
// (We know that there _was_ a previous parent, of course!)
+"\tappendChild(ete.getParentElem().removeChild(ete));\n"
+"\t}\n}\n"
);
// Corresponding vector built during compilation
Vector interpretVector=new Vector();
// Now for the big guns: the execute() method is where all the
// actual work of the template is performed, and is what we've
// really set out to compile.
// public void execute(TransformerImpl transformer,
// Node sourceNode, QName mode)
synthetic.reflection.Method exec=
tClass.declareMethod("execute");
exec.setModifiers(java.lang.reflect.Modifier.PUBLIC);
exec.addParameter(
tClass.forClass(org.apache.xalan.transformer.TransformerImpl.class),
"transformer");
exec.addParameter(
tClass.forClass(org.w3c.dom.Node.class),"sourceNode");
exec.addParameter(
tClass.forClass(org.apache.xalan.utils.QName.class),"mode");
exec.addExceptionType(
tClass.forClass(org.xml.sax.SAXException.class));
// If there are no kids, the body is a no-op.
ElemTemplateElement firstChild = source.getFirstChildElem();
if(null == firstChild)
{
exec.getBody().append("//empty template");
}
else
{
// Body startup
// **** FIRST DRAFT, I'm continuing to use ResultTreeHandler
// In future we might want to move toward direct SAX generation,
// (though that requires reordering data into normal SAX
// event order, generating trace events, and figuring
// out how to cooperate w/ template fragments not yet
// switched over to compiled mode)... or to raw text output,
// though I doubt that's significantly faster than SAX and it's
// definitely less convenient if further processing is desired.
StringBuffer body=exec.getBody().append(
"if(transformer.S_DEBUG)\n"
+" transformer.getTraceManager().fireTraceEvent(sourceNode,
mode, this);\n"
+"org.apache.xalan.transformer.ResultTreeHandler rhandler =
transformer.getResultTreeHandler();\n"
+"org.xml.sax.ContentHandler saxChandler =
rhandler.getContentHandler();\n"
+"if(null == sourceNode) {\n"
// throws(org.xml.sax.SAXException
+" transformer.getMsgMgr().error(this, sourceNode,\n"
+"
org.apache.xalan.res.XSLTErrorResources.ER_NULL_SOURCENODE_HANDLEAPPLYTEMPLATES);\n"
//sourceNode is null in handleApplyTemplatesInstruction!
+" return; }\n"
+"org.apache.xpath.XPathContext xctxt =
transformer.getXPathContext();\n"
+"// Check for infinite loops if requested\n"
+"boolean check = (transformer.getRecursionLimit() > -1);\n"
+"if (check)\n"
+" transformer.getStackGuard().push(this, sourceNode);\n"
+"String avtStringedValue; // ***** Optimize away?\n\n"
);
compileChildTemplates(source,body,interpretVector);
body.append(
"// Decrement infinite-loop check\n"
+"if (check)\n"
+" transformer.getStackGuard().pop();\n"
);
}
// Compile the new class
// TODO: ***** ISSUE: Where write out the class? Needs to
// be somewhere on the classpath.
// TODO: ***** ISSUE: What if file already exists?
// I think the answer in this case is "overwrite it.".
Class realclass=compileSyntheticClass(tClass,".");
// Prepare the array of execute()ables
Object[] eteParms=new Object[interpretVector.size()];
interpretVector.copyInto(eteParms);
// Instantiate -- note that this will be a singleton,
// as each template is probably unique
synthetic.reflection.Constructor c=
tClass.getConstructor(ctor.getParameterTypes());
Object[] parms={source,eteParms};
instance=(ElemTemplate)c.newInstance(parms);
}
catch(synthetic.SynthesisException e)
{
System.out.println("CompilingStylesheetHandler class synthesis
error");
e.printStackTrace();
}
catch(java.lang.ClassNotFoundException e)
{
System.out.println("CompilingStylesheetHandler class resolution
error");
e.printStackTrace();
}
catch(java.lang.IllegalAccessException e)
{
System.out.println("CompilingStylesheetHandler class comilation
error");
e.printStackTrace();
}
catch(java.lang.NoSuchMethodException e)
{
System.out.println("CompilingStylesheetHandler constructor resolution
error");
e.printStackTrace();
}
catch(java.lang.InstantiationException e)
{
System.out.println("CompilingStylesheetHandler constructor invocation
error");
e.printStackTrace();
}
catch(java.lang.reflect.InvocationTargetException e)
{
System.out.println("CompilingStylesheetHandler constructor invocation
error");
e.printStackTrace();
}
return instance;
}
void compileElemTemplateElement(ElemTemplateElement kid,StringBuffer
body,Vector interpretVector)
{
++uniqueVarSuffix; // Maintain unique variable naming
switch(kid.getXSLToken())
{
case Constants.ELEMNAME_LITERALRESULT:
compileElemLiteralResult((ElemLiteralResult)kid,body,interpretVector);
break;
// TODO: ***** Redirection of attr value not working yet.
//case Constants.ELEMNAME_ATTRIBUTE:
// compileElemAttribute((ElemAttribute)kid,body,interpretVector);
// break;
default:
// Safety net: We don't yet know how to compile this
// type of node, so instead we'll pass it into the
// compiled instance and invoke it interpretively.
int offset=interpretVector.size();
interpretVector.addElement(kid);
body.append(
"((org.apache.xalan.templates.ElemTemplateElement)interpretArray["+offset+"]).execute(transformer,sourceNode,mode);\n"
);
break;
}
}
void compileElemLiteralResult(ElemLiteralResult ele,StringBuffer
body,Vector interpretVector)
{
++uniqueVarSuffix; // Maintain unique variable naming
body.append("rhandler.startElement(\""
+ele.getNamespace()+"\",\""
+ele.getLocalName()+"\",\""
+ele.getRawName()+"\");\n");
// Handle xsl:use-attribute-sets
// expand ElemUse.execute(transformer, sourceNode, mode);
compileUseAttrSet(ele,body,interpretVector);
// Add stylesheet namespace declarations -- unrolling of
// ElemTemplateElement.executeNSDecls, plus additional logic to
// track that information within the generated code.
// (I really wish I could avoid the latter replication of data.
// Unfortunately the possibility of <xsl:attribute name="prefix:foo"/>
// with no explicit namespace requires that we be able to resolve a
// prefix in terms of the stylesheet, _not_ in terms of the output
// document... which means we need to track the stylesheet's NS context.)
Vector prefixTable=ele.getPrefixes();
int n = prefixTable.size();
boolean newNSlevel=(n>0);
if(newNSlevel)
body.append("m_nsSupport.pushContext();\n");
for(int i = 0; i < n; i++)
{
XMLNSDecl decl = (XMLNSDecl)prefixTable.elementAt(i);
// Output document
if(!decl.getIsExcluded())
body.append(
"rhandler.startPrefixMapping(\""
+decl.getPrefix()+"\",\""
+decl.getURI()+"\");\n"
);
// CompiledTemplate state
body.append(
"m_nsSupport.declarePrefix(\""
+decl.getPrefix()+"\",\""
+decl.getURI()+"\");\n"
);
}
// Process AVTs.
// TODO: Should be be checking for excluded namespace prefixes?
// ***** That wasn't done in non-compiled version, but was an open issue.
java.util.Enumeration avts=ele.enumerateLiteralResultAttributes();
if (avts!=null)
{
while(avts.hasMoreElements())
{
org.apache.xalan.templates.AVT avt =
(org.apache.xalan.templates.AVT)avts.nextElement();
String avtValueExpression=null;
boolean literal=avt.isContextInsensitive();
if(literal)
{
// Literal value, can fully resolve at compile time.
// Exception won't be thrown, but we've gotta catch
try{
avtValueExpression=makeQuotedString(
avt.evaluate(null,null,null,null)
);
}catch(SAXException e)
{
}
}
else
{
// Expression. Must resolve at runtime.
// TODO: ***** It might be possible to unwind this
// evaluation too. Consider.
int offset=interpretVector.size();
interpretVector.addElement(avt);
body.append(
"avtStringedValue=((org.apache.xalan.templates.AVT)"
+"(interpretArray["+offset+"])"
+").evaluate(xctxt,sourceNode,this,new StringBuffer());\n"
+"if(null!=avtStringedValue)\n{\n"
);
avtValueExpression="avtStringedValue";
}
body.append(
"rhandler.addAttribute(\""
+avt.getURI()+"\",\""
+avt.getName()+"\",\""
+avt.getRawName()
+"\",\"CDATA\","
+avtValueExpression
+");\n");
// Match the open brace, if one was issued
if(!literal)
body.append("} // endif\n");
} // end while more AVTs
} // end process AVTs
// Process children
// TODO:***** "Process m_extensionElementPrefixes && m_attributeSetsNames"
compileChildTemplates(ele,body,interpretVector);
// Close the patient
body.append("rhandler.endElement(\""
+ele.getNamespace()+"\",\""
+ele.getLocalName()+"\",\""
+ele.getRawName()+"\");\n");
if(newNSlevel)
body.append("m_nsSupport.popContext();\n");
}
// Detect and report AttributeSet loops.
Stack attrSetStack=new Stack();
void compileUseAttrSet(ElemTemplateElement ete,StringBuffer body,Vector
interpretVector)
{
++uniqueVarSuffix; // Maintain unique variable naming
body.append(
"if(transformer.S_DEBUG)\n"
+" transformer.getTraceManager().fireTraceEvent(sourceNode, mode,
this);\n"
);
// expand ElemUse.applyAttrSets(transformer, getStylesheetComposed(),
// ele.getUseAttributeSets(), sourceNode, mode);
// ***** DOES THIS CAST NEED TO BE CHECKED?
org.apache.xalan.utils.QName[]
attributeSetsNames=((org.apache.xalan.templates.ElemUse)ete).getUseAttributeSets();
if(null != attributeSetsNames)
{
org.apache.xalan.templates.StylesheetComposed
stylesheet=ete.getStylesheetComposed();
int nNames = attributeSetsNames.length;
for(int i = 0; i < nNames; i++)
{
org.apache.xalan.utils.QName qname = attributeSetsNames[i];
Vector attrSets = stylesheet.getAttributeSetComposed(qname);
int nSets = attrSets.size();
for(int k = 0; k < nSets; k++)
{
ElemAttributeSet attrSet =
(ElemAttributeSet)attrSets.elementAt(k);
// expand ElemAttributeSet.execute(transformer, sourceNode,
mode);
if(attrSetStack.contains(attrSet))
{
// TODO: ***** WHAT'S THE RIGHT WAY TO REPORT THIS ERROR?
String errmsg="TEMPLATE COMPILATION ERROR: ATTRIBUTE SET
RECURSION SUPPRESSED in "+attrSet.getName().getLocalPart();
/**/System.err.println(errmsg);
/**/body.append("// ***** "+errmsg+" *****/\n");
/**/return;
//throw new
SAXException(XSLMessages.createMessage(XSLTErrorResources.ER_XSLATTRSET_USED_ITSELF,
new Object[]{attrSet.getName().getLocalPart()})); //"xsl:attribute-set
'"+m_qname.m_localpart+
}
attrSetStack.push(attrSet);
// Recurse, since attrsets can reference attrsets
compileUseAttrSet(attrSet,body,interpretVector);
ElemAttribute attr = (ElemAttribute)attrSet.getFirstChild();
while(null != attr)
{
compileElemTemplateElement(attr,body,interpretVector);
attr = (ElemAttribute)attr.getNextSibling();
}
attrSetStack.pop();
}
}
}
}
String compileAVTvalue(org.apache.xalan.templates.AVT avt,StringBuffer
body,Vector interpretVector)
{
// Literal string is easy -- except for potential of " within "".
if(avt.isContextInsensitive())
try
{
return makeQuotedString(avt.evaluate(null,null,null,null));
} catch(SAXException e)
{
// Should never arise
String s=">UNEXPECTED ERROR evaluating context-insensitive
AVT<";
System.err.println(s);
e.printStackTrace();
return "\""+s+'"';
}
// Otherwise no compilation yet, just reference and return expression.
// Note that we do _not_ code-gen directly into the body, due to
// some concerns about where this might be used.
// YES, this is inconsistant.
int offset=interpretVector.size();
interpretVector.addElement(avt);
// TODO: ***** I'm assuming I can get away with "this" as the
prefixResolver
// even in the compiled code. I'm not really convinced that's true...
return
"(
((org.apache.xalan.templates.AVT)interpretArray["+offset+"]).evaluate(transformer.getXPathContext(),sourceNode,this,new
StringBuffer()) )"
;
}
// Wrap quotes around a text string.
// Escapes any contained quotes, converts null to the string "null".
// Used to prepare literal string arguments.
String makeQuotedString(String in)
{
if(in==null)
return "null";
StringBuffer out=new StringBuffer("\""); // don't use '"', it's taken as
int
int startpos=0,quotepos;
for(quotepos=in.indexOf('"',startpos);
quotepos!=-1;
startpos=quotepos+1,quotepos=in.indexOf('"',startpos))
{
out.append(in.substring(startpos,quotepos)).append('\\').append('\"');
}
out.append(in.substring(startpos)).append('"');
return out.toString();
}
// Get the children of the xsl:attribute element as the string value.
// String val = transformer.transformToString(this, sourceNode, mode);
// Returns string econtaiing result expression
String compileTransformToString(ElemTemplateElement ea,StringBuffer
body,Vector interpretVector)
{
++uniqueVarSuffix; // Maintain unique variable naming
String savedResultTreeHandler="savedResultTreeHandler"+uniqueVarSuffix;
String shandler="shandler"+uniqueVarSuffix;
String sw="sw"+uniqueVarSuffix;
String sfactory="sfactory"+uniqueVarSuffix;
String format="format"+uniqueVarSuffix;
String serializer="serializer"+uniqueVarSuffix;
String ioe="ioe"+uniqueVarSuffix;
body.append("\n// Begin transformToString (probably of attribute
contents)\n"
+"// by redirecting output into a StringWriter.\n"
+"org.apache.xalan.transformer.ResultTreeHandler
"+savedResultTreeHandler+"=rhandler;\n"
+"org.xml.sax.ContentHandler "+shandler+";\n"
+"java.io.StringWriter "+sw+";\n"
+"try\n{\n"
+"org.apache.xml.serialize.SerializerFactory
"+sfactory+"=org.apache.xml.serialize.SerializerFactory.getSerializerFactory(\"text\");\n"
+sw+"=new java.io.StringWriter();\n"
+"org.apache.xml.serialize.OutputFormat "+format+"=new
org.apache.xml.serialize.OutputFormat();\n"
+format+".setPreserveSpace(true);\n"
+"org.apache.xml.serialize.Serializer
"+serializer+"="+sfactory+".makeSerializer("+sw+","+format+");\n"
+shandler+"="+serializer+".asContentHandler();\n"
+"}\ncatch (java.io.IOException "+ioe+")\n{\n"
+"throw new org.xml.sax.SAXException("+ioe+");\n}\n"
// TODO: ***** DO WE NEED transformer.setResultTreeHandler()?
+"rhandler=new
org.apache.xalan.transformer.ResultTreeHandler(transformer,"+shandler+");\n\n"
+"rhandler.startDocument();\n"
+"\n//unwind executeChildTemplates\n"
);
compileChildTemplates(ea,body,interpretVector);
body.append(
"\nrhandler.flushPending();\n"
+"rhandler.endDocument();\n"
+"rhandler="+savedResultTreeHandler+";\n"
+"//End transformToString unwind; result in "+sw+"\n\n"
);
return "("+sw+".toString())";
}
void compileElemAttribute(ElemAttribute ea,StringBuffer body,Vector
interpretVector)
{
++uniqueVarSuffix; // Maintain unique variable naming
String attrName="attrName"+uniqueVarSuffix;
String val="val"+uniqueVarSuffix;
String attrNameSpace="attrNameSpace"+uniqueVarSuffix;
String prefix="prefix"+uniqueVarSuffix;
String ns="ns"+uniqueVarSuffix;
String attributeHandled="attributeHandled"+uniqueVarSuffix;
String nsprefix="nsprefix"+uniqueVarSuffix;
String ex="ex"+uniqueVarSuffix;
String localName="localName"+uniqueVarSuffix;
// The attribute name has to be evaluated as an AVT.
// We may add/alter the prefix later.
//String origAttrName="origAttrName"+uniqueVarSuffix;
String origAttrName=compileAVTvalue(ea.getName(),body,interpretVector);
// If they are trying to add an attribute when there isn't an
// element pending, it is an error.
// TODO: ****** Could these tests occur _before_ we transform?
body.append(
"if(null == rhandler.getPendingElementName())\n"
+"{\n"
+"transformer.getMsgMgr().warn(org.apache.xalan.res.XSLTErrorResources.WG_ILLEGAL_ATTRIBUTE_NAME,
new Object[]{"+origAttrName+"}); \n"
+"// warn(templateChild, sourceNode, \"Trying to add attribute after
element child has been added, ignoring...\");\n"
+"}\n"
);
// This check was done in the interpretive code... is it Really Needed?
if(null==origAttrName)
return;
body.append("boolean "+attributeHandled+"=false;\n");
// The attribute name has to be evaluated as an AVT,
// and gets stashed because we may add a prefix later
body.append("String "+attrName+"="+origAttrName+";\n");
// Get the children of the xsl:attribute element as the string value.
// String val = transformer.transformToString(this, sourceNode, mode);
String strval=compileTransformToString(ea,body,interpretVector);
body.append("String "+val+"="+strval+";\n");
// If they are trying to add an attribute when there isn't an
// element pending, it is an error.
// TODO: ****** Could these tests occur _before_ we transform?
body.append(
"if(null == rhandler.getPendingElementName())\n"
+"{\n"
+"transformer.getMsgMgr().warn(org.apache.xalan.res.XSLTErrorResources.WG_ILLEGAL_ATTRIBUTE_NAME,
new Object[]{"+origAttrName+"}); \n"
+"// warn(templateChild, sourceNode, \"Trying to add attribute after
element child has been added, ignoring...\");\n"
+"}\n"
+"if(null=="+attrName+")\n return;\n\n"
);
// Namespace is also an AVT, which means we can't count on it having been
// fully evaluated at stylesheet load time.
body.append(
"String "+attrNameSpace+"=null; // by default\n"
);
if(null!=ea.getNamespace()) // Can/must decide at compile time!
{
String
avtValueExpression=compileAVTvalue(ea.getNamespace(),body,interpretVector);
body.append(
attrNameSpace+"="+avtValueExpression+";\n"
+"if(null!="+attrNameSpace+" && "+attrNameSpace+".length()>0)\n"
+"{\n"
+" String "+prefix+"=rhandler.getPrefix("+attrNameSpace+");\n"
+" if(null=="+prefix+")\n"
+" {\n"
+" "+prefix+"=rhandler.getNewUniqueNSPrefix();\n"
+"
rhandler.startPrefixMapping("+prefix+","+attrNameSpace+");\n"
+" }\n"
+"
"+attrName+"=("+prefix+"+\':'+org.apache.xalan.utils.QName.getLocalPart("+attrName+"));\n"
+"}\n"
);
}
// Else: Is attribute xmlns type?
// TODO: ***** Must this retest for non-null namespace, due to
// compile-time decision above? Shouldn't really need to since the
// qname test ought to cover it, but....
body.append(
"if(org.apache.xalan.utils.QName.isXMLNSDecl("+origAttrName+"))\n"
+"{ // Just declare namespace prefix \n"
+" String
"+prefix+"=org.apache.xalan.utils.QName.getPrefixFromXMLNSDecl("+origAttrName+");\n"
+" String "+ns+"=rhandler.getURI("+prefix+");\n"
+" if(null=="+ns+")\n"
+" rhandler.startPrefixMapping("+prefix+","+val+");\n"
);
// Here the original code returned from ea.execute. Since
// we're expanding inline, we can't return, so instead we gate
// the later code via a boolean flag. (Could generate a jump to a
// label instead, but I think this is clearer.)
body.append(" "+attributeHandled+"=true;\n");
body.append("}\n"
+"else\n{\n"
+" String
"+nsprefix+"=org.apache.xalan.utils.QName.getPrefixPart("+origAttrName+");\n"
+" if(null=="+nsprefix+") "+nsprefix+"=\"\";\n"
// attrNameSpace = getNamespaceForPrefix(nsprefix);
// Handles the case where name was specified, namespace wasn't
// Resolves prefix in terms of STYLESHEET context, not input
// or output doc context... which requires that we track
// that context in our compiled code. Grrr.
// TODO: ***** BIG ISSUE: What about uncompiled nodes? Do we
need
// to become their parent and support gNFP()?
// Bigger restructuring than I've been doing.
+attrNameSpace+"=m_nsSupport.getURI("+nsprefix+");\n"
// The if here substitutes for early returns in original code
+"if(!"+attributeHandled+")\n{\n"
+"String
"+localName+"=org.apache.xalan.utils.QName.getLocalPart("+attrName+");\n"
+"rhandler.addAttribute("+attrNameSpace+","+localName+","+attrName+",\"CDATA\","+val+");\n"
+"} //end attributeHandled\n"
+"} //end else\n"
);
}
// Issue: When unrolling code, variable name resuse becomes an issue.
// Java doesn't permit local name scoping such as
// int i; { int i; }
// so we have to generate unique names. We don't really need a stack,
// since we don't care whether the suffix matches level of unrolling.
int uniqueVarSuffix=0;
void compileChildTemplates(ElemTemplateElement source,StringBuffer
body,Vector interpretVector)
{
++uniqueVarSuffix; // Maintain unique variable naming
// If no kids, no code gen.
if(source.getFirstChildElem()!=null)
{
// ***** TransformerImpl.executeChildTemplates does a
// bunch of additional setup/shutdown work. Since I
// don't know otherwise, I'm assuming I have to
// emulate that work as part of unwinding this.
//
// Set up the TransformerImpl context:
String savedLocatorName="savedLocator"+uniqueVarSuffix;
String varstackName="varstack"+uniqueVarSuffix;
body.append(
"\n// Unwound Transformer.executeChildTemplates: //\n\n"
+"// We need to push an element frame in the variables stack,\n"
+"// so all the variables can be popped at once when we're done.\n"
+"org.apache.xpath.VariableStack "+varstackName+" =
transformer.getXPathContext().getVarStack();\n"
+varstackName+".pushElemFrame();\n"
+"org.xml.sax.Locator "+savedLocatorName+" = xctxt.getSAXLocator();\n"
);
body.append("try {\n\n");
// Process the kids
for(ElemTemplateElement kid=source.getFirstChildElem();
kid!=null;
kid=kid.getNextSiblingElem())
{
//TODO: *PROBLEM* NEED EQUIVALENT? Node is Going Away...
// body.append("transformer.pushElemTemplateElement(kid);\n");
compileElemTemplateElement(kid,body,interpretVector);
//TODO: *PROBLEM* NEED EQUIVALENT? Node is Going Away...
// body.append("transformer.popElemTemplateElement(kid);\n");
}
// End the class wrapper
body.append(
"\n\n}\nfinally {\n"
+" xctxt.setSAXLocator("+savedLocatorName+");\n"
+" // Pop all the variables in this element frame.\n"
+" "+varstackName+".popElemFrame();\n"
+"}\n"
);
}
}
// Run this class description through the Java compiler,
// and patch the result back into the Synthetic system.
// Note that classLocation is treated as a directory iff
// it ends in FileLocator; if not, it's treated as a file
// name and output is written to the directory that file
// would be found in (possibly relative). However, "."
// is treated as being found in itself rather than in "..".
// TODO: ***** A more elegant version of this should be moved into
synthetic.Class?
Class compileSyntheticClass(synthetic.Class tClass, String classLocation)
{
Class resolved=null;
// Write class relative to specified starting location
// (which should be on the classpath, so we can load
// the resulting class!).
String filename=classLocation;
int fnstart=filename.lastIndexOf(File.separator);
StringBuffer subdir=new StringBuffer(
(fnstart>=0)
? filename.substring(0,fnstart)
: ".");
StringTokenizer parts=
new StringTokenizer(tClass.getPackageName(),".");
while(parts.hasMoreTokens())
{
subdir.append(File.separator).append(parts.nextToken());
}
if(fnstart<0)
subdir.append(File.separator);
File jfile=new File(subdir.toString());
jfile.mkdirs();
subdir.append(tClass.getShortName()).append(".java");
filename=subdir.toString();
jfile=new File(filename);
// Write Java source
try {
java.io.PrintWriter out=
new java.io.PrintWriter(new java.io.FileWriter(filename));
tClass.toSource(out,0);
out.close();
}
catch(java.io.IOException e)
{
System.err.println("ERR: File open failed for "+
filename);
e.printStackTrace();
return null;
}
// Compile
String classpath=System.getProperty ("java.class.path");
boolean debug=true;// *****
boolean generateDebug=true;// *****
//com.ibm.cs.util.JavaUtils.setDebug(generateDebug);
boolean compileOK=false;
boolean internalCompile=true;
String javac=System.getProperty("xalan.javac","javac");
if("msvj_workaround".equals(javac))
internalCompile=false;
if(internalCompile)
{
// ***** Part of the BSF package.
// This is actually supposed to go through the whole routine of trying
// to call the JDK directly (via the 1.2 options, then via
undocumented
// 1.1 calls), then fall back on command line if necessary.
// But I'm having some odd problems with it right now under
// both VisualCafe and VisualJ++.
compileOK=com.ibm.cs.util.JavaUtils.JDKcompile(filename,classpath);
}
else
{
try
{
// ***** LAUNCH PROBLEMS
// Microsoft Visual J++ has a number of problems, from
// insisting on running the .exec() via a shell that has
// only the NT "system" environment, to truncating the
// parameters passed to their JView tool, to having trouble
// launghing the compiler directly. Hence the following
// ugliness with propertie and workarounds
String extraClassPath="";
if("msvj_workaround".equals(javac))
{
javac="cmd /c D:\\LOCAL\\APPS\\SUN\\JDK1.2.2\\BIN\\JAVAC.EXE";
extraClassPath=".;d:\\user\\apache\\xml-xalan\\xerces.jar;";
}
else
{
extraClassPath=System.getProperty("xalan.classpathprefix","");
}
String cmd=""
+" "+javac
+" -g"
// +" -verbose"
+" -classpath "+extraClassPath+";"+classpath
+" "+filename
;
Process p;
// Used this when trying to figure out why javac wouldn't run
if(false)
{
System.out.println(cmd);
p=Runtime.getRuntime().exec("cmd /c start /wait set path");
compileOK=(waitHardFor(p)==0);
}
p=Runtime.getRuntime().exec(cmd);
compileOK=(waitHardFor(p)==0);
}
catch(java.io.IOException e)
{
System.err.println("ERR: javac failed for "+
tClass.getName());
e.printStackTrace();
}
}
if (compileOK)
{
if(debug)
System.err.println("\tCompilation successful. Debug specified,
.java file retained.");
else if(jfile.exists())
jfile.delete();
// Now try to load it!
try {
resolved=Class.forName(tClass.getName());
tClass.setRealClass(resolved);
}
catch(ClassNotFoundException e)
{
System.err.println("ERR: Class load failed for "+
tClass.getName());
e.printStackTrace();
}
catch(synthetic.SynthesisException e)
{
System.err.println("ERR: Synthetic class realization failed for "+
tClass.getName());
e.printStackTrace();
}
}
else
{
if(debug)
System.err.println("\tCompilation failed; retaining .java file");
// This should probably be an exception instead
System.err.println("ERR: Java compilation failed for "+
filename);
System.exit(1);
}
return resolved;
}
static long templateCounter=0;
/** There is probably a serious implementation of this
already existing, so I'm not going to spend a great
deal of time or effort on it in the prototype.
*/
String generateUniqueClassName()
{
//TODO: ***** ISSUE: CLASS NAMING. This is kluged
//as a temporary measure; we need to think about a
//more formal solution. Each compilation of each
//stylesheet winds up with its own set of classes,
//which need to be made globally unique if they aren't
//to collide with other compilations loaded into the same
//JVM. We really need classnames based on something like
//UUID or GID, combining the source stylesheet's URI,
//where it was processed, exactly when it was processed
//... and then going the extra step to guard against
//multitasking causing two stylesheets to start within
//the same clock tick.
// TODO: ***** Subissue: Package name components will correspond
// to directories when we compile. We shouldn't spawn more
// directories than we must. That may mean we'd rather
// flatten the source address.
long intAddr=0;
try
{
byte[] ipAddr=java.net.InetAddress.getLocalHost().getAddress();
for(int i=0;i<ipAddr.length;++i)
intAddr=(intAddr<<8)+ipAddr[i];
}
catch(java.net.UnknownHostException e)
{
// Should never occur
e.printStackTrace();
}
long templateNumber;
synchronized(this)
{ // I don't know if ++ is atomic or not. I'd hope so,
// but until that's been checked we should synchronize
// this operation.
templateNumber = ++templateCounter;
}
// Generate a mostly-unique name.
// TODO: ***** NOT GUARANTEED TO BE COMPLETELY UNIQUE, since
// there could, theoretically, be two seperate processes
// which are running this code simultaneously -- so
// even the combination of server, timestamp, and
// unique index may not suffice. The officious answer
// would be to tap into a system-level unique ID service,
// like UUID or GID.
// ***** Unclear these names will be supportable on all
// systems. Some may have limits on filename length.
// Could move more of the fields into directory names if
// that helps.
String className=
"org.apache.xalan.processor.ACompiledTemplateOn"
+intAddr
+"at"
+new java.util.Date().getTime() // msec since 1970 epoch
+'n'
+templateNumber // minor multithread protection
;
return className;
}
int waitHardFor(Process p)
{
boolean done=false;
while(!done)
try
{
p.waitFor();
done=true;
}
catch(InterruptedException e)
{
System.out.println("(Process wait interrupted, resuming)");
}
int ev=p.exitValue(); // Pause for debugging...
return ev;
}
}
1.1
xml-xalan/java/src/org/apache/xalan/processor/CompilingStylesheetProcessor.java
Index: CompilingStylesheetProcessor.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 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 [EMAIL PROTECTED]
*
* 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) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xalan.processor;
import java.io.IOException;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import org.w3c.dom.Node;
import trax.Processor;
import trax.ProcessorException;
import trax.Templates;
import trax.TemplatesBuilder;
/**
* The StylesheetProcessor, which implements the TRaX Processor
* interface, processes XSLT Stylesheets into a Templates object.
* This version has been modified to kick off conversion to JAVA,
* via CompilingStylesheetHandler
*/
public class CompilingStylesheetProcessor extends StylesheetProcessor
{
/**
* Get a TemplatesBuilder object that can process SAX
* events into a Templates object, if the processor supports the
* "http://xml.org/trax/features/sax/input" feature.
*
* <h3>Open issues:</h3>
* <dl>
* <dt><h4>Should Processor derive from
org.xml.sax.ContentHandler?</h4></dt>
* <dd>Instead of requesting an object from the Processor class, should
* the Processor class simply derive from
org.xml.sax.ContentHandler?</dd>
* </dl>
* @return A TemplatesBuilder object, or null if not supported.
* @exception May throw a ProcessorException if a StylesheetHandler can
* not be constructed for some reason.
*/
public TemplatesBuilder getTemplatesBuilder()
throws ProcessorException
{
return new CompilingStylesheetHandler(this);
}
}
1.4 +20 -3 xml-xalan/java/src/org/apache/xalan/templates/AVT.java
Index: AVT.java
===================================================================
RCS file: /home/cvs/xml-xalan/java/src/org/apache/xalan/templates/AVT.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- AVT.java 2000/08/09 04:26:40 1.3
+++ AVT.java 2000/09/20 23:33:09 1.4
@@ -91,8 +91,9 @@
/**
* Get the raw name of the attribute, with the prefix unprocessed.
+ * MADE PUBLIC 9/5/2000 to support compilation experiment
*/
- String getRawName()
+ public String getRawName()
{
return m_rawName;
}
@@ -104,8 +105,9 @@
/**
* Get the local name of the attribute.
+ * MADE PUBLIC 9/5/2000 to support compilation experiment
*/
- String getName()
+ public String getName()
{
return m_name;
}
@@ -117,8 +119,9 @@
/**
* Get the namespace URI of the attribute.
+ * MADE PUBLIC 9/5/2000 to support compilation experiment
*/
- String getURI()
+ public String getURI()
{
return m_uri;
}
@@ -373,4 +376,18 @@
return "";
}
}
+
+ /** Test whether the AVT is insensitive to the context in which
+ * it is being evaluated. This is intended to facilitate
+ * compilation of templates, by allowing simple AVTs to be
+ * converted back into strings.
+ *
+ * Currently the only case we recognize is simple strings.
+ * ADDED 9/5/2000 to support compilation experiment
+ */
+ public boolean isContextInsensitive()
+ {
+ return null != m_simpleString;
+ }
+
}
1.6 +8 -0
xml-xalan/java/src/org/apache/xalan/templates/ElemLiteralResult.java
Index: ElemLiteralResult.java
===================================================================
RCS file:
/home/cvs/xml-xalan/java/src/org/apache/xalan/templates/ElemLiteralResult.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- ElemLiteralResult.java 2000/07/31 22:09:55 1.5
+++ ElemLiteralResult.java 2000/09/20 23:33:10 1.6
@@ -447,4 +447,12 @@
rhandler.endElement (getNamespace(), getLocalName(), getRawName());
}
+ /** Compiling templates requires that we be able to list the AVTs
+ * ADDED 9/5/2000 to support compilation experiment
+ */
+ public Enumeration enumerateLiteralResultAttributes()
+ {
+ return (null==m_avts) ? null : m_avts.elements();
+ }
+
}
1.7 +96 -40
xml-xalan/java/src/org/apache/xalan/templates/ElemTemplateElement.java
Index: ElemTemplateElement.java
===================================================================
RCS file:
/home/cvs/xml-xalan/java/src/org/apache/xalan/templates/ElemTemplateElement.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- ElemTemplateElement.java 2000/08/10 23:30:47 1.6
+++ ElemTemplateElement.java 2000/09/20 23:33:10 1.7
@@ -237,6 +237,11 @@
/**
* Add a child to the child list.
+ * NOTE: This presumes the child did not previously have a parent.
+ * Making that assumption makes this a less expensive operation -- but
+ * requires that if you *do* want to reparent a node, you use removeChild()
+ * first to remove it from its previous context. Failing to do so will
+ * damage the tree.
*
* @exception DOMException
* @param newChild
@@ -286,46 +291,72 @@
return this;
}
+ /** Remove a child.
+ * ADDED 9/8/200 to support compilation.
+ * TODO: ***** Alternative is "removeMe() from my parent if any"
+ * ... which is less well checked, but more convenient in some cases.
+ * Given that we assume only experts are calling this class, it might
+ * be preferable. It's less DOMish, though.
+ * @param oldChild The child to remove. This operation is a no-op
+ * if oldChild is not a child of this node.
+ * @return the removed child, or null if the specified
+ * node was not a child of this element.
+ */
+ public Node removeChild(ElemTemplateElement childETE)
+ throws DOMException
+ {
+ if(childETE==null || childETE.m_parentNode!=this)
+ return null;
+
+ // Pointers to the child
+ if(childETE==m_firstChild)
+ m_firstChild=childETE.m_nextSibling;
+ else
+ {
+ ElemTemplateElement
prev=(ElemTemplateElement)(childETE.getPreviousSibling());
+ prev.m_nextSibling=childETE.m_nextSibling;
+ }
+
+ // Pointers from the child
+ childETE.m_parentNode = null;
+ childETE.m_nextSibling = null;
+ return childETE;
+ }
- /** Replace the old child with a new child. */
+ /** Replace the old child with a new child.
+ */
public Node replaceChild(Node newChild,
Node oldChild)
throws DOMException
{
- ElemTemplateElement node = (ElemTemplateElement)getFirstChild();
- while(null != node)
- {
- if(node == oldChild)
- {
- ElemTemplateElement newChildElem
- = ((ElemTemplateElement)newChild);
- ElemTemplateElement oldChildElem
- = ((ElemTemplateElement)oldChild);
-
- // Fix up previous sibling.
- ElemTemplateElement prev
- = (ElemTemplateElement)oldChildElem.getPreviousSibling();
- if(null != prev)
- prev.m_nextSibling = newChildElem;
-
- // Fix up parent.
- if(newChildElem.m_parentNode.m_firstChild == oldChildElem)
- newChildElem.m_parentNode.m_firstChild = newChildElem;
+ if(oldChild==null || oldChild.getParentNode()!=this)
+ return null;
+
+ ElemTemplateElement newChildElem
+ = ((ElemTemplateElement)newChild);
+ ElemTemplateElement oldChildElem
+ = ((ElemTemplateElement)oldChild);
+
+ // Fix up previous sibling.
+ ElemTemplateElement prev
+ = (ElemTemplateElement)oldChildElem.getPreviousSibling();
+ if(null != prev)
+ prev.m_nextSibling = newChildElem;
+
+ // Fix up parent (this)
+ if(m_firstChild == oldChildElem)
+ m_firstChild = newChildElem;
- newChildElem.m_parentNode = oldChildElem.m_parentNode;
- oldChildElem.m_parentNode = null;
+ newChildElem.m_parentNode = this;
+ oldChildElem.m_parentNode = null;
- newChildElem.m_nextSibling = oldChildElem.m_nextSibling;
- oldChildElem.m_nextSibling = null;
+ newChildElem.m_nextSibling = oldChildElem.m_nextSibling;
+ oldChildElem.m_nextSibling = null;
- // newChildElem.m_stylesheet = oldChildElem.m_stylesheet;
- // oldChildElem.m_stylesheet = null;
+ // newChildElem.m_stylesheet = oldChildElem.m_stylesheet;
+ // oldChildElem.m_stylesheet = null;
- return newChildElem;
- }
- node = (ElemTemplateElement)node.getNextSibling();
- }
- return null;
+ return newChildElem;
}
/**
@@ -405,7 +436,6 @@
return m_lineNumber;
}
-
private int m_columnNumber;
/**
@@ -539,14 +569,16 @@
/**
* Given a namespace, get the corrisponding prefix.
+ * 9/15/00: This had been iteratively examining the m_declaredPrefixes
+ * field for this node and its parents. That makes life difficult for
+ * the compilation experiment, which doesn't have a static vector of
+ * local declarations. Replaced a recursive solution, which permits
+ * easier subclassing/overriding.
*/
public String getNamespaceForPrefix(String prefix)
{
- ElemTemplateElement elem = this;
- while(null != elem)
- {
- Vector nsDecls = elem.m_declaredPrefixes;
- if(null != nsDecls)
+ Vector nsDecls = m_declaredPrefixes;
+ if(null != nsDecls)
{
int n = nsDecls.size();
for(int i = 0; i < n; i++)
@@ -556,9 +588,12 @@
return decl.getURI();
}
}
- elem = elem.m_parentNode;
- }
+ // Not found; ask our ancestors
+ if(null!=m_parentNode)
+ return m_parentNode.getNamespaceForPrefix(prefix);
+
+ // No parent, so no definition
return null;
}
@@ -711,7 +746,7 @@
}
/**
- * Get the parent as a Node.
+ * Get the parent as an ElemTemplateElement.
*/
public ElemTemplateElement getParentElem()
{
@@ -731,6 +766,27 @@
{
return m_nextSibling;
}
+
+ /**
+ * Get the previous sibling (as a Node) or return null.
+ * Note that this may be expensive if the parent has many kids;
+ * we accept that price in exchange for avoiding the prev pointer
+ * TODO: If we were sure parents and sibs are always ElemTemplateElements,
+ * we could hit the fields directly rather than thru accessors.
+ */
+ public Node getPreviousSibling()
+ {
+ Node walker=getParentNode(),prev=null;
+ if(walker!=null)
+ for(walker=walker.getFirstChild();
+ walker!=null;
+ prev=walker,walker=walker.getNextSibling())
+ if(walker==this)
+ return prev;
+ return null;
+ }
+
+
/**
* Get the next sibling (as a ElemTemplateElement) or return null.
1.6 +17 -0
xml-xalan/java/src/org/apache/xalan/templates/Stylesheet.java
Index: Stylesheet.java
===================================================================
RCS file:
/home/cvs/xml-xalan/java/src/org/apache/xalan/templates/Stylesheet.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- Stylesheet.java 2000/08/16 16:22:09 1.5
+++ Stylesheet.java 2000/09/20 23:33:10 1.6
@@ -1095,4 +1095,21 @@
return Constants.ELEMNAME_STYLESHEET_STRING;
}
+ /**
+ * Replace an "xsl:template" property.
+ * This is a hook for CompilingStylesheetHandler, to allow
+ * us to access a template, compile it, instantiate it,
+ * and replace the original with the compiled instance.
+ * ADDED 9/5/2000 to support compilation experiment
+ */
+ public void replaceTemplate(ElemTemplate v, int i)
+ throws SAXException
+ {
+ if(null == m_templates)
+ throw new ArrayIndexOutOfBoundsException();
+ replaceChild(v,(Node)m_templates.elementAt(i));
+ m_templates.setElementAt(v,i);
+ v.setStylesheet(this);
+ }
+
}
1.7 +16 -0
xml-xalan/java/src/org/apache/xalan/templates/StylesheetComposed.java
Index: StylesheetComposed.java
===================================================================
RCS file:
/home/cvs/xml-xalan/java/src/org/apache/xalan/templates/StylesheetComposed.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- StylesheetComposed.java 2000/08/10 23:30:47 1.6
+++ StylesheetComposed.java 2000/09/20 23:33:10 1.7
@@ -580,6 +580,22 @@
}
/**
+ * For compilation support, we need the option of overwriting
+ * (rather than appending to) previous composition.
+ * We could phase out the old API in favor of this one, but I'm
+ * holding off until we've made up our minds about compilation.
+ * ADDED 9/5/2000 to support compilation experiment
+ * @see <a
href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules
in XSLT Specification</a>
+ */
+ public void recomposeTemplates(boolean flushFirst)
+ throws SAXException
+ {
+ if(flushFirst)
+ m_templateList = new TemplateList(this);
+ recomposeTemplates();
+ }
+
+ /**
* Get an "xsl:template" property by node match. This looks in the imports
as
* well as this stylesheet.
* @see <a
href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules
in XSLT Specification</a>
1.20 +15 -3
xml-xalan/java/src/org/apache/xalan/transformer/TransformerImpl.java
Index: TransformerImpl.java
===================================================================
RCS file:
/home/cvs/xml-xalan/java/src/org/apache/xalan/transformer/TransformerImpl.java,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- TransformerImpl.java 2000/09/11 19:45:36 1.19
+++ TransformerImpl.java 2000/09/20 23:33:12 1.20
@@ -1178,12 +1178,24 @@
}
else
{
- // Fire a trace event for the template.
+ // 9/11/00: If template has been compiled, hand off to it
+ // since much (most? all?) of the processing has been inlined.
+ // (It would be nice if there was a single entry point that
+ // worked for both... but the interpretive system works by
+ // having the Tranformer execute the children, while the
+ // compiled obviously has to run its own code. It's
+ // also unclear that "execute" is really the right name for
+ // that entry point.)
+
+ // Fire a trace event for the template.
if(TransformerImpl.S_DEBUG)
- getTraceManager().fireTraceEvent(child, mode, template);
+ getTraceManager().fireTraceEvent(child, mode, template);
// And execute the child templates.
- executeChildTemplates(template, child, mode);
+ if(template instanceof
org.apache.xalan.processor.CompiledTemplate)
+ template.execute(this,child,mode);
+ else
+ executeChildTemplates(template, child, mode);
}
}
finally