diff -r 16ba58282d11 -r a754d69d5e60 jaxp/src/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/src/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet.java Sun Mar 04 11:55:34 2012 -0800 @@ -0,0 +1,756 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * $Id: AbstractTranslet.java,v 1.6 2006/06/19 19:49:03 spericas Exp $ + */ + +package com.sun.org.apache.xalan.internal.xsltc.runtime; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.BufferedOutputStream; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; +import javax.xml.transform.Templates; +import javax.xml.parsers.DocumentBuilderFactory; +import org.w3c.dom.Document; +import org.w3c.dom.DOMImplementation; +import javax.xml.parsers.ParserConfigurationException; + +import com.sun.org.apache.xml.internal.dtm.DTM; + +import com.sun.org.apache.xalan.internal.xsltc.DOM; +import com.sun.org.apache.xalan.internal.xsltc.DOMCache; +import com.sun.org.apache.xalan.internal.xsltc.DOMEnhancedForDTM; +import com.sun.org.apache.xalan.internal.xsltc.Translet; +import com.sun.org.apache.xalan.internal.xsltc.TransletException; +import com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter; +import com.sun.org.apache.xalan.internal.xsltc.dom.KeyIndex; +import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory; +import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; +import com.sun.org.apache.xml.internal.serializer.SerializationHandler; + +/** + * @author Jacek Ambroziak + * @author Santiago Pericas-Geertsen + * @author Morten Jorgensen + * @author G. Todd Miller + * @author John Howard, JohnH@schemasoft.com + */ +public abstract class AbstractTranslet implements Translet { + + // These attributes are extracted from the xsl:output element. They also + // appear as fields (with the same type, only public) in Output.java + public String _version = "1.0"; + public String _method = null; + public String _encoding = "UTF-8"; + public boolean _omitHeader = false; + public String _standalone = null; + public String _doctypePublic = null; + public String _doctypeSystem = null; + public boolean _indent = false; + public String _mediaType = null; + public Vector _cdata = null; + public int _indentamount = -1; + + public static final int FIRST_TRANSLET_VERSION = 100; + public static final int VER_SPLIT_NAMES_ARRAY = 101; + public static final int CURRENT_TRANSLET_VERSION = VER_SPLIT_NAMES_ARRAY; + + // Initialize Translet version field to base value. A class that extends + // AbstractTranslet may override this value to a more recent translet + // version; if it doesn't override the value (because it was compiled + // before the notion of a translet version was introduced, it will get + // this default value). + protected int transletVersion = FIRST_TRANSLET_VERSION; + + // DOM/translet handshaking - the arrays are set by the compiled translet + protected String[] namesArray; + protected String[] urisArray; + protected int[] typesArray; + protected String[] namespaceArray; + + // The Templates object that is used to create this Translet instance + protected Templates _templates = null; + + // Boolean flag to indicate whether this translet has id functions. + protected boolean _hasIdCall = false; + + // TODO - these should only be instanciated when needed + protected StringValueHandler stringValueHandler = new StringValueHandler(); + + // Use one empty string instead of constantly instanciating String(""); + private final static String EMPTYSTRING = ""; + + // This is the name of the index used for ID attributes + private final static String ID_INDEX_NAME = "##id"; + + + /************************************************************************ + * Debugging + ************************************************************************/ + public void printInternalState() { + System.out.println("-------------------------------------"); + System.out.println("AbstractTranslet this = " + this); + System.out.println("pbase = " + pbase); + System.out.println("vframe = " + pframe); + System.out.println("paramsStack.size() = " + paramsStack.size()); + System.out.println("namesArray.size = " + namesArray.length); + System.out.println("namespaceArray.size = " + namespaceArray.length); + System.out.println(""); + System.out.println("Total memory = " + Runtime.getRuntime().totalMemory()); + } + + /** + * Wrap the initial input DOM in a dom adapter. This adapter is wrapped in + * a DOM multiplexer if the document() function is used (handled by compiled + * code in the translet - see compiler/Stylesheet.compileTransform()). + */ + public final DOMAdapter makeDOMAdapter(DOM dom) + throws TransletException { + setRootForKeys(dom.getDocument()); + return new DOMAdapter(dom, namesArray, urisArray, typesArray, namespaceArray); + } + + /************************************************************************ + * Parameter handling + ************************************************************************/ + + // Parameter's stack: pbase and pframe are used + // to denote the current parameter frame. + protected int pbase = 0, pframe = 0; + protected ArrayList paramsStack = new ArrayList(); + + /** + * Push a new parameter frame. + */ + public final void pushParamFrame() { + paramsStack.add(pframe, new Integer(pbase)); + pbase = ++pframe; + } + + /** + * Pop the topmost parameter frame. + */ + public final void popParamFrame() { + if (pbase > 0) { + final int oldpbase = ((Integer)paramsStack.get(--pbase)).intValue(); + for (int i = pframe - 1; i >= pbase; i--) { + paramsStack.remove(i); + } + pframe = pbase; pbase = oldpbase; + } + } + + /** + * Add a new global parameter if not already in the current frame. + * To setParameters of the form {http://foo.bar}xyz + * This needs to get mapped to an instance variable in the class + * The mapping created so that + * the global variables in the generated class become + * http$colon$$flash$$flash$foo$dot$bar$colon$xyz + */ + public final Object addParameter(String name, Object value) { + name = BasisLibrary.mapQNameToJavaName (name); + return addParameter(name, value, false); + } + + /** + * Add a new global or local parameter if not already in the current frame. + * The 'isDefault' parameter is set to true if the value passed is the + * default value from the element's select attribute or + * element body. + */ + public final Object addParameter(String name, Object value, + boolean isDefault) + { + // Local parameters need to be re-evaluated for each iteration + for (int i = pframe - 1; i >= pbase; i--) { + final Parameter param = (Parameter) paramsStack.get(i); + + if (param._name.equals(name)) { + // Only overwrite if current value is the default value and + // the new value is _NOT_ the default value. + if (param._isDefault || !isDefault) { + param._value = value; + param._isDefault = isDefault; + return value; + } + return param._value; + } + } + + // Add new parameter to parameter stack + paramsStack.add(pframe++, new Parameter(name, value, isDefault)); + return value; + } + + /** + * Clears the parameter stack. + */ + public void clearParameters() { + pbase = pframe = 0; + paramsStack.clear(); + } + + /** + * Get the value of a parameter from the current frame or + * null if undefined. + */ + public final Object getParameter(String name) { + + name = BasisLibrary.mapQNameToJavaName (name); + + for (int i = pframe - 1; i >= pbase; i--) { + final Parameter param = (Parameter)paramsStack.get(i); + if (param._name.equals(name)) return param._value; + } + return null; + } + + /************************************************************************ + * Message handling - implementation of + ************************************************************************/ + + // Holds the translet's message handler - used for . + // The deault message handler dumps a string stdout, but anything can be + // used, such as a dialog box for applets, etc. + private MessageHandler _msgHandler = null; + + /** + * Set the translet's message handler - must implement MessageHandler + */ + public final void setMessageHandler(MessageHandler handler) { + _msgHandler = handler; + } + + /** + * Pass a message to the message handler - used by Message class. + */ + public final void displayMessage(String msg) { + if (_msgHandler == null) { + System.err.println(msg); + } + else { + _msgHandler.displayMessage(msg); + } + } + + /************************************************************************ + * Decimal number format symbol handling + ************************************************************************/ + + // Contains decimal number formatting symbols used by FormatNumberCall + public Hashtable _formatSymbols = null; + + /** + * Adds a DecimalFormat object to the _formatSymbols hashtable. + * The entry is created with the input DecimalFormatSymbols. + */ + public void addDecimalFormat(String name, DecimalFormatSymbols symbols) { + // Instanciate hashtable for formatting symbols if needed + if (_formatSymbols == null) _formatSymbols = new Hashtable(); + + // The name cannot be null - use empty string instead + if (name == null) name = EMPTYSTRING; + + // Construct a DecimalFormat object containing the symbols we got + final DecimalFormat df = new DecimalFormat(); + if (symbols != null) { + df.setDecimalFormatSymbols(symbols); + } + _formatSymbols.put(name, df); + } + + /** + * Retrieves a named DecimalFormat object from _formatSymbols hashtable. + */ + public final DecimalFormat getDecimalFormat(String name) { + + if (_formatSymbols != null) { + // The name cannot be null - use empty string instead + if (name == null) name = EMPTYSTRING; + + DecimalFormat df = (DecimalFormat)_formatSymbols.get(name); + if (df == null) df = (DecimalFormat)_formatSymbols.get(EMPTYSTRING); + return df; + } + return(null); + } + + /** + * Give the translet an opportunity to perform a prepass on the document + * to extract any information that it can store in an optimized form. + * + * Currently, it only extracts information about attributes of type ID. + */ + public final void prepassDocument(DOM document) { + setIndexSize(document.getSize()); + buildIDIndex(document); + } + + /** + * Leverages the Key Class to implement the XSLT id() function. + * buildIdIndex creates the index (##id) that Key Class uses. + * The index contains the element node index (int) and Id value (String). + */ + private final void buildIDIndex(DOM document) { + setRootForKeys(document.getDocument()); + + if (document instanceof DOMEnhancedForDTM) { + DOMEnhancedForDTM enhancedDOM = (DOMEnhancedForDTM)document; + + // If the input source is DOMSource, the KeyIndex table is not + // built at this time. It will be built later by the lookupId() + // and containsId() methods of the KeyIndex class. + if (enhancedDOM.hasDOMSource()) { + buildKeyIndex(ID_INDEX_NAME, document); + return; + } + else { + final Hashtable elementsByID = enhancedDOM.getElementsWithIDs(); + + if (elementsByID == null) { + return; + } + + // Given a Hashtable of DTM nodes indexed by ID attribute values, + // loop through the table copying information to a KeyIndex + // for the mapping from ID attribute value to DTM node + final Enumeration idValues = elementsByID.keys(); + boolean hasIDValues = false; + + while (idValues.hasMoreElements()) { + final Object idValue = idValues.nextElement(); + final int element = + document.getNodeHandle( + ((Integer)elementsByID.get(idValue)) + .intValue()); + + buildKeyIndex(ID_INDEX_NAME, element, idValue); + hasIDValues = true; + } + + if (hasIDValues) { + setKeyIndexDom(ID_INDEX_NAME, document); + } + } + } + } + + /** + * After constructing the translet object, this method must be called to + * perform any version-specific post-initialization that's required. + */ + public final void postInitialization() { + // If the version of the translet had just one namesArray, split + // it into multiple fields. + if (transletVersion < VER_SPLIT_NAMES_ARRAY) { + int arraySize = namesArray.length; + String[] newURIsArray = new String[arraySize]; + String[] newNamesArray = new String[arraySize]; + int[] newTypesArray = new int[arraySize]; + + for (int i = 0; i < arraySize; i++) { + String name = namesArray[i]; + int colonIndex = name.lastIndexOf(':'); + int lNameStartIdx = colonIndex+1; + + if (colonIndex > -1) { + newURIsArray[i] = name.substring(0, colonIndex); + } + + // Distinguish attribute and element names. Attribute has + // @ before local part of name. + if (name.charAt(lNameStartIdx) == '@') { + lNameStartIdx++; + newTypesArray[i] = DTM.ATTRIBUTE_NODE; + } else if (name.charAt(lNameStartIdx) == '?') { + lNameStartIdx++; + newTypesArray[i] = DTM.NAMESPACE_NODE; + } else { + newTypesArray[i] = DTM.ELEMENT_NODE; + } + newNamesArray[i] = + (lNameStartIdx == 0) ? name + : name.substring(lNameStartIdx); + } + + namesArray = newNamesArray; + urisArray = newURIsArray; + typesArray = newTypesArray; + } + + // Was translet compiled using a more recent version of the XSLTC + // compiler than is known by the AbstractTranslet class? If, so + // and we've made it this far (which is doubtful), we should give up. + if (transletVersion > CURRENT_TRANSLET_VERSION) { + BasisLibrary.runTimeError(BasisLibrary.UNKNOWN_TRANSLET_VERSION_ERR, + this.getClass().getName()); + } + } + + /************************************************************************ + * Index(es) for / key() / id() + ************************************************************************/ + + // Container for all indexes for xsl:key elements + private Hashtable _keyIndexes = null; + private KeyIndex _emptyKeyIndex = null; + private int _indexSize = 0; + private int _currentRootForKeys = 0; + + /** + * This method is used to pass the largest DOM size to the translet. + * Needed to make sure that the translet can index the whole DOM. + */ + public void setIndexSize(int size) { + if (size > _indexSize) _indexSize = size; + } + + /** + * Creates a KeyIndex object of the desired size - don't want to resize!!! + */ + public KeyIndex createKeyIndex() { + return(new KeyIndex(_indexSize)); + } + + /** + * Adds a value to a key/id index + * @param name is the name of the index (the key or ##id) + * @param node is the node handle of the node to insert + * @param value is the value that will look up the node in the given index + */ + public void buildKeyIndex(String name, int node, Object value) { + if (_keyIndexes == null) _keyIndexes = new Hashtable(); + + KeyIndex index = (KeyIndex)_keyIndexes.get(name); + if (index == null) { + _keyIndexes.put(name, index = new KeyIndex(_indexSize)); + } + index.add(value, node, _currentRootForKeys); + } + + /** + * Create an empty KeyIndex in the DOM case + * @param name is the name of the index (the key or ##id) + * @param dom is the DOM + */ + public void buildKeyIndex(String name, DOM dom) { + if (_keyIndexes == null) _keyIndexes = new Hashtable(); + + KeyIndex index = (KeyIndex)_keyIndexes.get(name); + if (index == null) { + _keyIndexes.put(name, index = new KeyIndex(_indexSize)); + } + index.setDom(dom, dom.getDocument()); + } + + /** + * Returns the index for a given key (or id). + * The index implements our internal iterator interface + */ + public KeyIndex getKeyIndex(String name) { + // Return an empty key index iterator if none are defined + if (_keyIndexes == null) { + return (_emptyKeyIndex != null) + ? _emptyKeyIndex + : (_emptyKeyIndex = new KeyIndex(1)); + } + + // Look up the requested key index + final KeyIndex index = (KeyIndex)_keyIndexes.get(name); + + // Return an empty key index iterator if the requested index not found + if (index == null) { + return (_emptyKeyIndex != null) + ? _emptyKeyIndex + : (_emptyKeyIndex = new KeyIndex(1)); + } + + return(index); + } + + private void setRootForKeys(int root) { + _currentRootForKeys = root; + } + + /** + * This method builds key indexes - it is overridden in the compiled + * translet in cases where the element is used + */ + public void buildKeys(DOM document, DTMAxisIterator iterator, + SerializationHandler handler, + int root) throws TransletException { + + } + + /** + * This method builds key indexes - it is overridden in the compiled + * translet in cases where the element is used + */ + public void setKeyIndexDom(String name, DOM document) { + getKeyIndex(name).setDom(document, document.getDocument()); + } + + /************************************************************************ + * DOM cache handling + ************************************************************************/ + + // Hold the DOM cache (if any) used with this translet + private DOMCache _domCache = null; + + /** + * Sets the DOM cache used for additional documents loaded using the + * document() function. + */ + public void setDOMCache(DOMCache cache) { + _domCache = cache; + } + + /** + * Returns the DOM cache used for this translet. Used by the LoadDocument + * class (if present) when the document() function is used. + */ + public DOMCache getDOMCache() { + return(_domCache); + } + + /************************************************************************ + * Multiple output document extension. + * See compiler/TransletOutput for actual implementation. + ************************************************************************/ + + public SerializationHandler openOutputHandler(String filename, boolean append) + throws TransletException + { + try { + final TransletOutputHandlerFactory factory + = TransletOutputHandlerFactory.newInstance(); + + String dirStr = new File(filename).getParent(); + if ((null != dirStr) && (dirStr.length() > 0)) { + File dir = new File(dirStr); + dir.mkdirs(); + } + + factory.setEncoding(_encoding); + factory.setOutputMethod(_method); + factory.setOutputStream(new BufferedOutputStream(new FileOutputStream(filename, append))); + factory.setOutputType(TransletOutputHandlerFactory.STREAM); + + final SerializationHandler handler + = factory.getSerializationHandler(); + + transferOutputSettings(handler); + handler.startDocument(); + return handler; + } + catch (Exception e) { + throw new TransletException(e); + } + } + + public SerializationHandler openOutputHandler(String filename) + throws TransletException + { + return openOutputHandler(filename, false); + } + + public void closeOutputHandler(SerializationHandler handler) { + try { + handler.endDocument(); + handler.close(); + } + catch (Exception e) { + // what can you do? + } + } + + /************************************************************************ + * Native API transformation methods - _NOT_ JAXP/TrAX + ************************************************************************/ + + /** + * Main transform() method - this is overridden by the compiled translet + */ + public abstract void transform(DOM document, DTMAxisIterator iterator, + SerializationHandler handler) + throws TransletException; + + /** + * Calls transform() with a given output handler + */ + public final void transform(DOM document, SerializationHandler handler) + throws TransletException { + try { + transform(document, document.getIterator(), handler); + } finally { + _keyIndexes = null; + } + } + + /** + * Used by some compiled code as a shortcut for passing strings to the + * output handler + */ + public final void characters(final String string, + SerializationHandler handler) + throws TransletException { + if (string != null) { + //final int length = string.length(); + try { + handler.characters(string); + } catch (Exception e) { + throw new TransletException(e); + } + } + } + + /** + * Add's a name of an element whose text contents should be output as CDATA + */ + public void addCdataElement(String name) { + if (_cdata == null) { + _cdata = new Vector(); + } + + int lastColon = name.lastIndexOf(':'); + + if (lastColon > 0) { + String uri = name.substring(0, lastColon); + String localName = name.substring(lastColon+1); + _cdata.addElement(uri); + _cdata.addElement(localName); + } else { + _cdata.addElement(null); + _cdata.addElement(name); + } + } + + /** + * Transfer the output settings to the output post-processor + */ + protected void transferOutputSettings(SerializationHandler handler) { + if (_method != null) { + if (_method.equals("xml")) { + if (_standalone != null) { + handler.setStandalone(_standalone); + } + if (_omitHeader) { + handler.setOmitXMLDeclaration(true); + } + handler.setCdataSectionElements(_cdata); + if (_version != null) { + handler.setVersion(_version); + } + handler.setIndent(_indent); + handler.setIndentAmount(_indentamount); + if (_doctypeSystem != null) { + handler.setDoctype(_doctypeSystem, _doctypePublic); + } + } + else if (_method.equals("html")) { + handler.setIndent(_indent); + handler.setDoctype(_doctypeSystem, _doctypePublic); + if (_mediaType != null) { + handler.setMediaType(_mediaType); + } + } + } + else { + handler.setCdataSectionElements(_cdata); + if (_version != null) { + handler.setVersion(_version); + } + if (_standalone != null) { + handler.setStandalone(_standalone); + } + if (_omitHeader) { + handler.setOmitXMLDeclaration(true); + } + handler.setIndent(_indent); + handler.setDoctype(_doctypeSystem, _doctypePublic); + } + } + + private Hashtable _auxClasses = null; + + public void addAuxiliaryClass(Class auxClass) { + if (_auxClasses == null) _auxClasses = new Hashtable(); + _auxClasses.put(auxClass.getName(), auxClass); + } + + public void setAuxiliaryClasses(Hashtable auxClasses) { + _auxClasses = auxClasses; + } + + public Class getAuxiliaryClass(String className) { + if (_auxClasses == null) return null; + return((Class)_auxClasses.get(className)); + } + + // GTM added (see pg 110) + public String[] getNamesArray() { + return namesArray; + } + + public String[] getUrisArray() { + return urisArray; + } + + public int[] getTypesArray() { + return typesArray; + } + + public String[] getNamespaceArray() { + return namespaceArray; + } + + public boolean hasIdCall() { + return _hasIdCall; + } + + public Templates getTemplates() { + return _templates; + } + + public void setTemplates(Templates templates) { + _templates = templates; + } + + /************************************************************************ + * DOMImplementation caching for basis library + ************************************************************************/ + protected DOMImplementation _domImplementation = null; + + public Document newDocument(String uri, String qname) + throws ParserConfigurationException + { + if (_domImplementation == null) { + _domImplementation = DocumentBuilderFactory.newInstance() + .newDocumentBuilder().getDOMImplementation(); + } + return _domImplementation.createDocument(uri, qname, null); + } +}