/*
 * reserved comment block
 * DO NOT REMOVE OR ALTER!
 */
/*
 * Copyright 1999-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: SimpleResultTreeImpl.java,v 1.2.4.1 2005/09/06 10:09:25 pvedula Exp $
 */
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.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.StripFilter;
import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;

import com.sun.org.apache.xml.internal.dtm.DTM;
import com.sun.org.apache.xml.internal.dtm.Axis;
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 org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.xml.sax.SAXException;

import javax.xml.transform.SourceLocator;

/**
 * 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.
 * <p>
 * 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.
 * <p>
 * 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 Hashtable 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)
    {
    }
}
