/* * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ package com.sun.org.apache.xalan.internal.xsltc.dom; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.StripFilter; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xml.internal.dtm.Axis; import com.sun.org.apache.xml.internal.dtm.DTM; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.dtm.DTMAxisTraverser; import com.sun.org.apache.xml.internal.dtm.DTMManager; import com.sun.org.apache.xml.internal.dtm.ref.DTMAxisIteratorBase; import com.sun.org.apache.xml.internal.dtm.ref.DTMManagerDefault; import com.sun.org.apache.xml.internal.serializer.EmptySerializer; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import com.sun.org.apache.xml.internal.utils.XMLString; import com.sun.org.apache.xml.internal.utils.XMLStringDefault; import java.util.Map; import javax.xml.transform.SourceLocator; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * This class represents a light-weight DOM model for simple result tree fragment(RTF). * A simple RTF is an RTF that has only one Text node. The Text node can be produced by a * combination of Text, xsl:value-of and xsl:number instructions. It can also be produced * by a control structure (xsl:if or xsl:choose) whose body is pure Text. *

* A SimpleResultTreeImpl has only two nodes, i.e. the ROOT node and its Text child. All DOM * interfaces are overridden with this in mind. For example, the getStringValue() interface * returns the value of the Text node. This class receives the character data from the * characters() interface. *

* This class implements DOM and SerializationHandler. It also implements the DTM interface * for support in MultiDOM. The nested iterators (SimpleIterator and SingletonIterator) are * used to support the nodeset() extension function. */ public class SimpleResultTreeImpl extends EmptySerializer implements DOM, DTM { /** * The SimpleIterator is designed to support the nodeset() extension function. It has * a traversal direction parameter. The DOWN direction is used for child and descendant * axes, while the UP direction is used for parent and ancestor axes. * * This iterator only handles two nodes (RTF_ROOT and RTF_TEXT). If the type is set, * it will also match the node type with the given type. */ public final class SimpleIterator extends DTMAxisIteratorBase { static final int DIRECTION_UP = 0; static final int DIRECTION_DOWN = 1; static final int NO_TYPE = -1; // The direction of traversal (default to DOWN). // DOWN is for child and descendant. UP is for parent and ancestor. int _direction = DIRECTION_DOWN; int _type = NO_TYPE; int _currentNode; public SimpleIterator() { } public SimpleIterator(int direction) { _direction = direction; } public SimpleIterator(int direction, int type) { _direction = direction; _type = type; } public int next() { // Increase the node ID for down traversal. Also match the node type // if the type is given. if (_direction == DIRECTION_DOWN) { while (_currentNode < NUMBER_OF_NODES) { if (_type != NO_TYPE) { if ((_currentNode == RTF_ROOT && _type == DTM.ROOT_NODE) || (_currentNode == RTF_TEXT && _type == DTM.TEXT_NODE)) return returnNode(getNodeHandle(_currentNode++)); else _currentNode++; } else return returnNode(getNodeHandle(_currentNode++)); } return END; } // Decrease the node ID for up traversal. else { while (_currentNode >= 0) { if (_type != NO_TYPE) { if ((_currentNode == RTF_ROOT && _type == DTM.ROOT_NODE) || (_currentNode == RTF_TEXT && _type == DTM.TEXT_NODE)) return returnNode(getNodeHandle(_currentNode--)); else _currentNode--; } else return returnNode(getNodeHandle(_currentNode--)); } return END; } } public DTMAxisIterator setStartNode(int nodeHandle) { int nodeID = getNodeIdent(nodeHandle); _startNode = nodeID; // Increase the node ID by 1 if self is not included. if (!_includeSelf && nodeID != DTM.NULL) { if (_direction == DIRECTION_DOWN) nodeID++; else if (_direction == DIRECTION_UP) nodeID--; } _currentNode = nodeID; return this; } public void setMark() { _markedNode = _currentNode; } public void gotoMark() { _currentNode = _markedNode; } } // END of SimpleIterator /** * The SingletonIterator is used for the self axis. */ public final class SingletonIterator extends DTMAxisIteratorBase { static final int NO_TYPE = -1; int _type = NO_TYPE; int _currentNode; public SingletonIterator() { } public SingletonIterator(int type) { _type = type; } public void setMark() { _markedNode = _currentNode; } public void gotoMark() { _currentNode = _markedNode; } public DTMAxisIterator setStartNode(int nodeHandle) { _currentNode = _startNode = getNodeIdent(nodeHandle); return this; } public int next() { if (_currentNode == END) return END; _currentNode = END; if (_type != NO_TYPE) { if ((_currentNode == RTF_ROOT && _type == DTM.ROOT_NODE) || (_currentNode == RTF_TEXT && _type == DTM.TEXT_NODE)) return getNodeHandle(_currentNode); } else return getNodeHandle(_currentNode); return END; } } // END of SingletonIterator // empty iterator to be returned when there are no children private final static DTMAxisIterator EMPTY_ITERATOR = new DTMAxisIteratorBase() { public DTMAxisIterator reset() { return this; } public DTMAxisIterator setStartNode(int node) { return this; } public int next() { return DTM.NULL; } public void setMark() {} public void gotoMark() {} public int getLast() { return 0; } public int getPosition() { return 0; } public DTMAxisIterator cloneIterator() { return this; } public void setRestartable(boolean isRestartable) { } }; // The root node id of the simple RTF public static final int RTF_ROOT = 0; // The Text node id of the simple RTF (simple RTF has only one Text node). public static final int RTF_TEXT = 1; // The number of nodes. public static final int NUMBER_OF_NODES = 2; // Document URI index, which increases by 1 at each getDocumentURI() call. private static int _documentURIIndex = 0; // Constant for empty String private static final String EMPTY_STR = ""; // The String value of the Text node. // This is set at the endDocument() call. private String _text; // The array of Text items, which is built by the characters() call. // The characters() interface can be called multiple times. Each character item // can have different escape settings. protected String[] _textArray; // The DTMManager protected XSLTCDTMManager _dtmManager; // Number of character items protected int _size = 0; // The document ID private int _documentID; // A BitArray, each bit holding the escape setting for a character item. private BitArray _dontEscape = null; // The current escape setting private boolean _escaping = true; // Create a SimpleResultTreeImpl from a DTMManager and a document ID. public SimpleResultTreeImpl(XSLTCDTMManager dtmManager, int documentID) { _dtmManager = dtmManager; _documentID = documentID; _textArray = new String[4]; } public DTMManagerDefault getDTMManager() { return _dtmManager; } // Return the document ID public int getDocument() { return _documentID; } // Return the String value of the RTF public String getStringValue() { return _text; } public DTMAxisIterator getIterator() { return new SingletonIterator(getDocument()); } public DTMAxisIterator getChildren(final int node) { return new SimpleIterator().setStartNode(node); } public DTMAxisIterator getTypedChildren(final int type) { return new SimpleIterator(SimpleIterator.DIRECTION_DOWN, type); } // Return the axis iterator for a given axis. // The SimpleIterator is used for the child, descendant, parent and ancestor axes. public DTMAxisIterator getAxisIterator(final int axis) { switch (axis) { case Axis.CHILD: case Axis.DESCENDANT: return new SimpleIterator(SimpleIterator.DIRECTION_DOWN); case Axis.PARENT: case Axis.ANCESTOR: return new SimpleIterator(SimpleIterator.DIRECTION_UP); case Axis.ANCESTORORSELF: return (new SimpleIterator(SimpleIterator.DIRECTION_UP)).includeSelf(); case Axis.DESCENDANTORSELF: return (new SimpleIterator(SimpleIterator.DIRECTION_DOWN)).includeSelf(); case Axis.SELF: return new SingletonIterator(); default: return EMPTY_ITERATOR; } } public DTMAxisIterator getTypedAxisIterator(final int axis, final int type) { switch (axis) { case Axis.CHILD: case Axis.DESCENDANT: return new SimpleIterator(SimpleIterator.DIRECTION_DOWN, type); case Axis.PARENT: case Axis.ANCESTOR: return new SimpleIterator(SimpleIterator.DIRECTION_UP, type); case Axis.ANCESTORORSELF: return (new SimpleIterator(SimpleIterator.DIRECTION_UP, type)).includeSelf(); case Axis.DESCENDANTORSELF: return (new SimpleIterator(SimpleIterator.DIRECTION_DOWN, type)).includeSelf(); case Axis.SELF: return new SingletonIterator(type); default: return EMPTY_ITERATOR; } } // %REVISIT% Can this one ever get used? public DTMAxisIterator getNthDescendant(int node, int n, boolean includeself) { return null; } public DTMAxisIterator getNamespaceAxisIterator(final int axis, final int ns) { return null; } // %REVISIT% Can this one ever get used? public DTMAxisIterator getNodeValueIterator(DTMAxisIterator iter, int returnType, String value, boolean op) { return null; } public DTMAxisIterator orderNodes(DTMAxisIterator source, int node) { return source; } public String getNodeName(final int node) { if (getNodeIdent(node) == RTF_TEXT) return "#text"; else return EMPTY_STR; } public String getNodeNameX(final int node) { return EMPTY_STR; } public String getNamespaceName(final int node) { return EMPTY_STR; } // Return the expanded type id of a given node public int getExpandedTypeID(final int nodeHandle) { int nodeID = getNodeIdent(nodeHandle); if (nodeID == RTF_TEXT) return DTM.TEXT_NODE; else if (nodeID == RTF_ROOT) return DTM.ROOT_NODE; else return DTM.NULL; } public int getNamespaceType(final int node) { return 0; } public int getParent(final int nodeHandle) { int nodeID = getNodeIdent(nodeHandle); return (nodeID == RTF_TEXT) ? getNodeHandle(RTF_ROOT) : DTM.NULL; } public int getAttributeNode(final int gType, final int element) { return DTM.NULL; } public String getStringValueX(final int nodeHandle) { int nodeID = getNodeIdent(nodeHandle); if (nodeID == RTF_ROOT || nodeID == RTF_TEXT) return _text; else return EMPTY_STR; } public void copy(final int node, SerializationHandler handler) throws TransletException { characters(node, handler); } public void copy(DTMAxisIterator nodes, SerializationHandler handler) throws TransletException { int node; while ((node = nodes.next()) != DTM.NULL) { copy(node, handler); } } public String shallowCopy(final int node, SerializationHandler handler) throws TransletException { characters(node, handler); return null; } public boolean lessThan(final int node1, final int node2) { if (node1 == DTM.NULL) { return false; } else if (node2 == DTM.NULL) { return true; } else return (node1 < node2); } /** * Dispatch the character content of a node to an output handler. * * The escape setting should be taken care of when outputting to * a handler. */ public void characters(final int node, SerializationHandler handler) throws TransletException { int nodeID = getNodeIdent(node); if (nodeID == RTF_ROOT || nodeID == RTF_TEXT) { boolean escapeBit = false; boolean oldEscapeSetting = false; try { for (int i = 0; i < _size; i++) { if (_dontEscape != null) { escapeBit = _dontEscape.getBit(i); if (escapeBit) { oldEscapeSetting = handler.setEscaping(false); } } handler.characters(_textArray[i]); if (escapeBit) { handler.setEscaping(oldEscapeSetting); } } } catch (SAXException e) { throw new TransletException(e); } } } // %REVISIT% Can the makeNode() and makeNodeList() interfaces ever get used? public Node makeNode(int index) { return null; } public Node makeNode(DTMAxisIterator iter) { return null; } public NodeList makeNodeList(int index) { return null; } public NodeList makeNodeList(DTMAxisIterator iter) { return null; } public String getLanguage(int node) { return null; } public int getSize() { return 2; } public String getDocumentURI(int node) { return "simple_rtf" + _documentURIIndex++; } public void setFilter(StripFilter filter) { } public void setupMapping(String[] names, String[] uris, int[] types, String[] namespaces) { } public boolean isElement(final int node) { return false; } public boolean isAttribute(final int node) { return false; } public String lookupNamespace(int node, String prefix) throws TransletException { return null; } /** * Return the node identity from a node handle. */ public int getNodeIdent(final int nodehandle) { return (nodehandle != DTM.NULL) ? (nodehandle - _documentID) : DTM.NULL; } /** * Return the node handle from a node identity. */ public int getNodeHandle(final int nodeId) { return (nodeId != DTM.NULL) ? (nodeId + _documentID) : DTM.NULL; } public DOM getResultTreeFrag(int initialSize, int rtfType) { return null; } public DOM getResultTreeFrag(int initialSize, int rtfType, boolean addToManager) { return null; } public SerializationHandler getOutputDomBuilder() { return this; } public int getNSType(int node) { return 0; } public String getUnparsedEntityURI(String name) { return null; } public Map getElementsWithIDs() { return null; } /** Implementation of the SerializationHandler interfaces **/ /** * We only need to override the endDocument, characters, and * setEscaping interfaces. A simple RTF does not have element * nodes. We do not need to touch startElement and endElement. */ public void startDocument() throws SAXException { } public void endDocument() throws SAXException { // Set the String value when the document is built. if (_size == 1) _text = _textArray[0]; else { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < _size; i++) { buffer.append(_textArray[i]); } _text = buffer.toString(); } } public void characters(String str) throws SAXException { // Resize the text array if necessary if (_size >= _textArray.length) { String[] newTextArray = new String[_textArray.length * 2]; System.arraycopy(_textArray, 0, newTextArray, 0, _textArray.length); _textArray = newTextArray; } // If the escape setting is false, set the corresponding bit in // the _dontEscape BitArray. if (!_escaping) { // The _dontEscape array is only created when needed. if (_dontEscape == null) { _dontEscape = new BitArray(8); } // Resize the _dontEscape array if necessary if (_size >= _dontEscape.size()) _dontEscape.resize(_dontEscape.size() * 2); _dontEscape.setBit(_size); } _textArray[_size++] = str; } public void characters(char[] ch, int offset, int length) throws SAXException { if (_size >= _textArray.length) { String[] newTextArray = new String[_textArray.length * 2]; System.arraycopy(_textArray, 0, newTextArray, 0, _textArray.length); _textArray = newTextArray; } if (!_escaping) { if (_dontEscape == null) { _dontEscape = new BitArray(8); } if (_size >= _dontEscape.size()) _dontEscape.resize(_dontEscape.size() * 2); _dontEscape.setBit(_size); } _textArray[_size++] = new String(ch, offset, length); } public boolean setEscaping(boolean escape) throws SAXException { final boolean temp = _escaping; _escaping = escape; return temp; } /** Implementation of the DTM interfaces **/ /** * The DTM interfaces are not used in this class. Implementing the DTM * interface is a requirement from MultiDOM. If we have a better way * of handling multiple documents, we can get rid of the DTM dependency. * * The following interfaces are just placeholders. The implementation * does not have an impact because they will not be used. */ public void setFeature(String featureId, boolean state) { } public void setProperty(String property, Object value) { } public DTMAxisTraverser getAxisTraverser(final int axis) { return null; } public boolean hasChildNodes(int nodeHandle) { return (getNodeIdent(nodeHandle) == RTF_ROOT); } public int getFirstChild(int nodeHandle) { int nodeID = getNodeIdent(nodeHandle); if (nodeID == RTF_ROOT) return getNodeHandle(RTF_TEXT); else return DTM.NULL; } public int getLastChild(int nodeHandle) { return getFirstChild(nodeHandle); } public int getAttributeNode(int elementHandle, String namespaceURI, String name) { return DTM.NULL; } public int getFirstAttribute(int nodeHandle) { return DTM.NULL; } public int getFirstNamespaceNode(int nodeHandle, boolean inScope) { return DTM.NULL; } public int getNextSibling(int nodeHandle) { return DTM.NULL; } public int getPreviousSibling(int nodeHandle) { return DTM.NULL; } public int getNextAttribute(int nodeHandle) { return DTM.NULL; } public int getNextNamespaceNode(int baseHandle, int namespaceHandle, boolean inScope) { return DTM.NULL; } public int getOwnerDocument(int nodeHandle) { return getDocument(); } public int getDocumentRoot(int nodeHandle) { return getDocument(); } public XMLString getStringValue(int nodeHandle) { return new XMLStringDefault(getStringValueX(nodeHandle)); } public int getStringValueChunkCount(int nodeHandle) { return 0; } public char[] getStringValueChunk(int nodeHandle, int chunkIndex, int[] startAndLen) { return null; } public int getExpandedTypeID(String namespace, String localName, int type) { return DTM.NULL; } public String getLocalNameFromExpandedNameID(int ExpandedNameID) { return EMPTY_STR; } public String getNamespaceFromExpandedNameID(int ExpandedNameID) { return EMPTY_STR; } public String getLocalName(int nodeHandle) { return EMPTY_STR; } public String getPrefix(int nodeHandle) { return null; } public String getNamespaceURI(int nodeHandle) { return EMPTY_STR; } public String getNodeValue(int nodeHandle) { return (getNodeIdent(nodeHandle) == RTF_TEXT) ? _text : null; } public short getNodeType(int nodeHandle) { int nodeID = getNodeIdent(nodeHandle); if (nodeID == RTF_TEXT) return DTM.TEXT_NODE; else if (nodeID == RTF_ROOT) return DTM.ROOT_NODE; else return DTM.NULL; } public short getLevel(int nodeHandle) { int nodeID = getNodeIdent(nodeHandle); if (nodeID == RTF_TEXT) return 2; else if (nodeID == RTF_ROOT) return 1; else return DTM.NULL; } public boolean isSupported(String feature, String version) { return false; } public String getDocumentBaseURI() { return EMPTY_STR; } public void setDocumentBaseURI(String baseURI) { } public String getDocumentSystemIdentifier(int nodeHandle) { return null; } public String getDocumentEncoding(int nodeHandle) { return null; } public String getDocumentStandalone(int nodeHandle) { return null; } public String getDocumentVersion(int documentHandle) { return null; } public boolean getDocumentAllDeclarationsProcessed() { return false; } public String getDocumentTypeDeclarationSystemIdentifier() { return null; } public String getDocumentTypeDeclarationPublicIdentifier() { return null; } public int getElementById(String elementId) { return DTM.NULL; } public boolean supportsPreStripping() { return false; } public boolean isNodeAfter(int firstNodeHandle, int secondNodeHandle) { return lessThan(firstNodeHandle, secondNodeHandle); } public boolean isCharacterElementContentWhitespace(int nodeHandle) { return false; } public boolean isDocumentAllDeclarationsProcessed(int documentHandle) { return false; } public boolean isAttributeSpecified(int attributeHandle) { return false; } public void dispatchCharactersEvents( int nodeHandle, org.xml.sax.ContentHandler ch, boolean normalize) throws org.xml.sax.SAXException { } public void dispatchToEvents(int nodeHandle, org.xml.sax.ContentHandler ch) throws org.xml.sax.SAXException { } public org.w3c.dom.Node getNode(int nodeHandle) { return makeNode(nodeHandle); } public boolean needsTwoThreads() { return false; } public org.xml.sax.ContentHandler getContentHandler() { return null; } public org.xml.sax.ext.LexicalHandler getLexicalHandler() { return null; } public org.xml.sax.EntityResolver getEntityResolver() { return null; } public org.xml.sax.DTDHandler getDTDHandler() { return null; } public org.xml.sax.ErrorHandler getErrorHandler() { return null; } public org.xml.sax.ext.DeclHandler getDeclHandler() { return null; } public void appendChild(int newChild, boolean clone, boolean cloneDepth) { } public void appendTextChild(String str) { } public SourceLocator getSourceLocatorFor(int node) { return null; } public void documentRegistration() { } public void documentRelease() { } public void migrateTo(DTMManager manager) { } public void release() { if (_documentID != 0) { _dtmManager.release(this, true); _documentID = 0; } } }