Package org.exist.stax

Source Code of org.exist.stax.EmbeddedXMLStreamReader$ElementEvent

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2001-2007 The eXist team
*  http://exist-db.org
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program; if not, write to the Free Software Foundation
*  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*  $Id$
*/
package org.exist.stax;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;

import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.StreamFilter;

import org.apache.log4j.Logger;
import org.exist.dom.AttrImpl;
import org.exist.dom.CharacterDataImpl;
import org.exist.dom.DocumentImpl;
import org.exist.dom.ElementImpl;
import org.exist.dom.NodeHandle;
import org.exist.dom.StoredNode;
import org.exist.numbering.NodeId;
import org.exist.storage.DBBroker;
import org.exist.storage.Signatures;
import org.exist.storage.btree.Value;
import org.exist.storage.dom.RawNodeIterator;
import org.exist.util.ByteConversion;
import org.exist.util.XMLString;
import org.exist.util.serializer.AttrList;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;

/**
* Lazy implementation of a StAX {@link javax.xml.stream.XMLStreamReader}, which directly reads
* information from the persistent DOM. The class is optimized to support fast scanning of the DOM, where only
* a few selected node properties are requested. Node properties are extracted on demand. For example, the QName of
* an element will not be read unless {@link #getText()} is called.
*/
public class EmbeddedXMLStreamReader implements ExtendedXMLStreamReader {

  private static final Logger LOG = Logger.getLogger(EmbeddedXMLStreamReader.class);

    private RawNodeIterator iterator;

    private Value current = null;

    private Value previous = null;

    private Stack<ElementEvent> elementStack = new Stack<ElementEvent>();

    private int state = START_DOCUMENT;

    private boolean beforeRoot = false;
   
    private DocumentImpl document;

    private NodeId nodeId;

    private NodeHandle origin;

    private QName qname = null;

    private XMLString text = new XMLString(256);

    private List<String[]> namespaces = new ArrayList<String[]>(6);
    private boolean nsRead = false;
   
    private AttrList attributes = null;

    private boolean reportAttribs = false;

    private DBBroker broker;

    /**
     * Construct an EmbeddedXMLStreamReader.
     *
     * @param doc the document to which the start node belongs.
     * @param iterator a RawNodeIterator positioned on the start node.
     * @param origin an optional NodeHandle whose nodeId should match the first node in the stream
     *     (or null if no need to check)
     * @param reportAttributes if set to true, attributes will be reported as top-level events.
     * @throws XMLStreamException
     */
    public EmbeddedXMLStreamReader(DBBroker broker, DocumentImpl doc, RawNodeIterator iterator, NodeHandle origin, boolean reportAttributes)
            throws XMLStreamException {
        this.broker = broker;
        this.document = doc;
        this.iterator = iterator;
        this.reportAttribs = reportAttributes;
        this.origin = origin;
    }

    public void filter(StreamFilter filter) throws XMLStreamException {
        while (hasNext()) {
            next();
            if (!filter.accept(this))
                {break;}
        }
    }

    /**
     * Reposition the stream reader to another start node, maybe in a different document.
     *
     * @param node the new start node.
     * @param reportAttributes if set to true, attributes will be reported as top-level events.
     * @throws IOException
     */
    public void reposition(DBBroker broker, NodeHandle node, boolean reportAttributes) throws IOException {
        this.broker = broker;
        // Seeking to a node with unknown address will reuse this reader, so do it before setting all
        // the fields otherwise they could get overwritten.
        iterator.seek(node);
        reset();
        this.current = null;
        this.previous = null;
        this.elementStack.clear();
        this.state = START_DOCUMENT;
        this.reportAttribs = reportAttributes;
        this.document = (DocumentImpl) node.getOwnerDocument();
        this.origin = node;
    }

    public short getNodeType() {
        return Signatures.getType(current.data()[current.start()]);
    }
   
    private void initNode() {
        final short type = Signatures.getType(current.data()[current.start()]);    // TODO: remove potential NPE
        if (state == START_DOCUMENT && type != Node.ELEMENT_NODE)
            {beforeRoot = true;}
        switch (type) {
            case Node.ELEMENT_NODE :
                state = START_ELEMENT;
                elementStack.push(new ElementEvent(current));
                beforeRoot = false;
                break;
            case Node.ATTRIBUTE_NODE :
                state = ATTRIBUTE;
                break;
            case Node.TEXT_NODE :
                state = CHARACTERS;
                break;
            case Node.COMMENT_NODE:
                state = COMMENT;
                break;
            case Node.CDATA_SECTION_NODE:
                state = CDATA;
                break;
            case Node.PROCESSING_INSTRUCTION_NODE:
                state = PROCESSING_INSTRUCTION;
                break;
        }
        reset();
        readNodeId();
    }

    public int getChildCount() {
        if (state == START_ELEMENT)
            {return elementStack.peek().getChildCount();}
        return 0;
    }
   
    private void skipAttributes() throws XMLStreamException {
        if (attributes == null) {
            // attributes were not yet read. skip them...
            final ElementEvent parent = elementStack.peek();
            final int attrs = getAttributeCount();
            for (int i = 0; i < attrs; i++) {
                iterator.next();
                parent.incrementChild();
            }
        }
    }

    private void readAttributes() {
        if (attributes == null) {
            final ElementEvent parent = elementStack.peek();
            final int count = getAttributeCount();
            attributes = new AttrList();
            for (int i = 0; i < count; i++) {
                final Value v = iterator.next();
                AttrImpl.addToList(broker, v.data(), v.start(), v.getLength(), attributes);
                parent.incrementChild();
            }
        }
    }
   
    private void readNodeId() {
        int offset = current.start() + StoredNode.LENGTH_SIGNATURE_LENGTH;
        if (state == START_ELEMENT || state == END_ELEMENT)
          {offset += ElementImpl.LENGTH_ELEMENT_CHILD_COUNT;}
        final int dlnLen = ByteConversion.byteToShort(current.data(), offset);
        offset += NodeId.LENGTH_NODE_ID_UNITS;
        nodeId = broker.getBrokerPool().getNodeFactory().createFromData(dlnLen, current.data(), offset);
    }
   
    public int next() throws XMLStreamException {
        if (state != END_ELEMENT)
            {previous = current;}
        if (state == START_ELEMENT && !reportAttribs)
            {skipAttributes();}
        if (!elementStack.isEmpty()) {
            final ElementEvent parent = elementStack.peek();
            if (parent.getChildCount() == parent.getCurrentChild()) {
                elementStack.pop();
                state = END_ELEMENT;
                current = parent.data;
                reset();
                return state;
            } else {
                parent.incrementChild();
            }
        } else if (state != START_DOCUMENT && !beforeRoot)
            {throw new NoSuchElementException();}
        final boolean first = state == START_DOCUMENT;
        current = iterator.next();
        initNode();
        if (first && origin != null) {
          verifyOriginNodeId();
          origin = null;
        }
        return state;
    }

  private void verifyOriginNodeId() throws XMLStreamException {
    if (!nodeId.equals(origin.getNodeId())) {
      // Node got moved, we had the wrong address.  Resync iterator by nodeid.
      LOG.warn("expected node id " + origin.getNodeId() + ", got " + nodeId + "; resyncing address");
      origin.setInternalAddress(StoredNode.UNKNOWN_NODE_IMPL_ADDRESS);
      boolean reportAttribsBackup = reportAttribs;
      DocumentImpl documentBackup = document;
      try {
        iterator.seek(origin);
      } catch (final IOException e) {
        throw new XMLStreamException(e);
      }
      // Seeking the iterator might've reused this reader, so reset all fields.
       reset();
       previous = null;
       elementStack.clear();
       reportAttribs = reportAttribsBackup;
       document = documentBackup;
      current = iterator.next();
      initNode();
      origin.setInternalAddress(iterator.currentAddress());
    }
  }

    private void reset() {
        nodeId = null;
        qname = null;
        attributes = null;
        text.reuse();
        if (state != END_ELEMENT) {
            namespaces.clear();
            nsRead = false;
        }
    }

    public void require(int i, String string, String string1) throws XMLStreamException {
        throw new UnsupportedOperationException();
    }

    public String getElementText() throws XMLStreamException {
        if(getEventType() != START_ELEMENT) {
            throw new XMLStreamException(
                "parser must be on START_ELEMENT to read next text");
        }
        int eventType = next();
        final StringBuffer content = new StringBuffer();
        while(eventType != END_ELEMENT ) {
            if(eventType == CHARACTERS
                || eventType == CDATA
                || eventType == SPACE
                || eventType == ENTITY_REFERENCE) {
                content.append(getText());
            } else if(eventType == PROCESSING_INSTRUCTION
                || eventType == COMMENT) {
                // skipping
            } else if(eventType == END_DOCUMENT) {
                throw new XMLStreamException("unexpected end of document when reading element text content");
            } else if(eventType == START_ELEMENT) {
                throw new XMLStreamException(
                    "element text content may not contain START_ELEMENT");
            } else {
                throw new XMLStreamException(
                    "Unexpected event type "+eventType);
            }
            eventType = next();
        }
        return content.toString();
    }

    public Object getProperty(String string) throws IllegalArgumentException {
        if (string.equals(PROPERTY_NODE_ID)) {
            if (nodeId == null)
                {readNodeId();}
            return nodeId;
        }
        return null;
    }

    public int nextTag() throws XMLStreamException {
        throw new UnsupportedOperationException();
    }

    public boolean hasNext() throws XMLStreamException {
        return state == START_DOCUMENT || beforeRoot || !elementStack.isEmpty();
    }

    public void close() throws XMLStreamException {
        iterator.closeDocument();
    }

    public boolean isStartElement() {
        return state == START_ELEMENT;
    }

    public boolean isEndElement() {
        return state == END_ELEMENT;
    }

    public boolean isCharacters() {
        return state == CHARACTERS;
    }

    public boolean isWhiteSpace() {
        return false;
    }

    public String getAttributeValue(String namespaceURI, String localName) {
        readAttributes();
        for (int i = 0; i < attributes.getLength(); i++) {
            final org.exist.dom.QName qn = attributes.getQName(i);
            if (qn.getNamespaceURI().equals(namespaceURI) && qn.getLocalName().equals(localName))
                {return attributes.getValue(i);}
        }
        return null;
    }

    public int getAttributeCount() {
        final int offset = current.start() + StoredNode.LENGTH_SIGNATURE_LENGTH + ElementImpl.LENGTH_ELEMENT_CHILD_COUNT + NodeId.LENGTH_NODE_ID_UNITS + nodeId.size();
        return ByteConversion.byteToShort(current.data(), offset);
    }

    public QName getAttributeName(int i) {
        if (state != START_ELEMENT)
            {throw new IllegalStateException("Cursor is not at an element");}
        readAttributes();
        if (i > attributes.getLength())
            {throw new ArrayIndexOutOfBoundsException("index should be < " + attributes.getLength());}
        return attributes.getQName(i).toJavaQName();
    }

    public org.exist.dom.QName getAttributeQName(int i) {
        if (state != START_ELEMENT)
            {throw new IllegalStateException("Cursor is not at an element");}
        readAttributes();
        if (i > attributes.getLength())
            {throw new ArrayIndexOutOfBoundsException("index should be < " + attributes.getLength());}
        return attributes.getQName(i);
    }

    public String getAttributeNamespace(int i) {
        if (state != START_ELEMENT)
            {throw new IllegalStateException("Cursor is not at an element");}
        readAttributes();
        if (i > attributes.getLength())
            {throw new ArrayIndexOutOfBoundsException("index should be < " + attributes.getLength());}
        return attributes.getQName(i).getNamespaceURI();
    }

    public String getAttributeLocalName(int i) {
        if (state != START_ELEMENT)
            {throw new IllegalStateException("Cursor is not at an element");}
        readAttributes();
        if (i > attributes.getLength())
            {throw new ArrayIndexOutOfBoundsException("index should be < " + attributes.getLength());}
        return attributes.getQName(i).getLocalName();
    }

    public String getAttributePrefix(int i) {
        if (state != START_ELEMENT)
            {throw new IllegalStateException("Cursor is not at an element");}
        readAttributes();
        if (i > attributes.getLength())
            {throw new ArrayIndexOutOfBoundsException("index should be < " + attributes.getLength());}
        return attributes.getQName(i).getPrefix();
    }

    public String getAttributeType(int i) {
        if (state != START_ELEMENT)
            {throw new IllegalStateException("Cursor is not at an element");}
        readAttributes();
        if (i > attributes.getLength())
            {throw new ArrayIndexOutOfBoundsException("index should be < " + attributes.getLength());}
        final int type = attributes.getType(i);
        return AttrImpl.getAttributeType(type);
    }

    public String getAttributeValue(int i) {
        if (state != START_ELEMENT)
            {throw new IllegalStateException("Cursor is not at an element");}
        readAttributes();
        if (i > attributes.getLength())
            {throw new ArrayIndexOutOfBoundsException("index should be < " + attributes.getLength());}
        return attributes.getValue(i);
    }

    public NodeId getAttributeId(int i) {
        if (state != START_ELEMENT)
            {throw new IllegalStateException("Cursor is not at an element");}
        readAttributes();
        if (i > attributes.getLength())
            {throw new ArrayIndexOutOfBoundsException("index should be < " + attributes.getLength());}
        return attributes.getNodeId(i);
    }

    public boolean isAttributeSpecified(int i) {
        return false;
    }

    public int getNamespaceCount() {
        readNamespaceDecls();
        return namespaces.size();
    }

    public String getNamespacePrefix(int i) {
        readNamespaceDecls();
        if (i < 0 || i > namespaces.size())
            {return null;}
        final String[] decl = namespaces.get(i);
        return decl[0];
    }

    public String getNamespaceURI(int i) {
        readNamespaceDecls();
        if (i < 0 || i > namespaces.size())
            {return null;}
        final String[] decl = namespaces.get(i);
        return decl[1];
    }

    public NamespaceContext getNamespaceContext() {
        throw new UnsupportedOperationException();
    }

    public int getEventType() {
        return state;
    }

    public XMLString getXMLText() {
        if (state == CHARACTERS || state == COMMENT || state == CDATA) {
            if (text.length() == 0) {
                CharacterDataImpl.readData(nodeId, current, text);
            }
            return text;
        }
        return new XMLString();
    }

    public String getText() {
        return getXMLText().toString();
    }

    public char[] getTextCharacters() {
        final String s = getText();
        final char[] dst = new char[s.length()];
        s.getChars(0, dst.length, dst, 0);
        return dst;
    }

    public int getTextCharacters(int sourceStart, char[] chars, int targetStart, int length) throws XMLStreamException {
        throw new UnsupportedOperationException();
    }

    public int getTextStart() {
        throw new UnsupportedOperationException();
    }

    public int getTextLength() {
        if (state == CHARACTERS || state == COMMENT || state == CDATA) {
            if (text.length() == 0)
                {return CharacterDataImpl.getStringLength(nodeId, current);}
            return text.length();
        }
        return 0;
    }

    public String getEncoding() {
        throw new UnsupportedOperationException();
    }

    public boolean hasText() {
        return state == CHARACTERS || state == COMMENT || state == CDATA;
    }

    public Location getLocation() {
        throw new UnsupportedOperationException();
    }

    public String getNamespaceURI(String string) {
        return null;
    }

    public QName getName() {
        if (qname != null)
            {return qname;}
        if (state == START_ELEMENT || state == END_ELEMENT) {
            if (nodeId == null)
                {readNodeId();}
            qname = ElementImpl.readQName(current, document, nodeId).toJavaQName();
        }
        return qname;
    }

    public org.exist.dom.QName getQName() {
        if (state == START_ELEMENT || state == END_ELEMENT) {
            if (nodeId == null)
                {readNodeId();}
            return ElementImpl.readQName(current, document, nodeId);
        }
        return null;
    }

    /**
     * Read all namespace declarations defined on the current element.
     * Cache them in the namespaces map.
     */
    private void readNamespaceDecls() {
        if (nsRead)
            {return;}
        if (state == START_ELEMENT || state == END_ELEMENT) {
            if (nodeId == null)
                {readNodeId();}
            ElementImpl.readNamespaceDecls(namespaces, current, document, nodeId);
        }
        nsRead = true;
    }
   
    public String getPrefix() {
        return getName().getPrefix();
    }

    public String getLocalName() {
        return getName().getLocalPart();
    }

    public String getNamespaceURI() {
        return getName().getNamespaceURI();
    }

    public boolean hasName() {
        return (state == START_ELEMENT || state == END_ELEMENT);
    }

    /**
     * Deserialize the node at the current position of the cursor and return
     * it as a {@link org.exist.dom.StoredNode}.
     *
     * @return the node at the current position.
     */
    public StoredNode getNode() {
        final StoredNode node = StoredNode.deserialize(current.data(), current.start(), current.getLength(), document);
        node.setOwnerDocument(document);
        node.setInternalAddress(current.getAddress());
        return node;
    }

    /**
     * Returns the last node in document sequence that occurs before the
     * current node. Usually used to find the last child before an END_ELEMENT
     * event.
     *
     * @return the last node in document sequence before the current node
     */
    public StoredNode getPreviousNode() {
        final StoredNode node = StoredNode.deserialize(previous.data(), previous.start(), previous.getLength(), document);
        node.setOwnerDocument(document);
        node.setInternalAddress(previous.getAddress());
        return node;
    }

    /**
     * Returns the (internal) address of the node at the cursor's current
     * position.
     *
     * @return internal address of node
     */
    public long getCurrentPosition() {
        return iterator.currentAddress();
    }

    public String getVersion() {
        return "1.0";
    }

    public boolean isStandalone() {
        return false;
    }

    public boolean standaloneSet() {
        return false;
    }

    public String getCharacterEncodingScheme() {
        return null;
    }

    public String getPITarget() {
        readPI();
        return qname.getLocalPart();
    }

    public String getPIData() {
        readPI();
        return text.toString();
    }

    private void readPI() {
        if (qname == null) {
            if (state != PROCESSING_INSTRUCTION)
                {throw new IllegalStateException("Cursor is not at a processing instruction");}
            final ProcessingInstruction pi = (ProcessingInstruction)
                    StoredNode.deserialize(current.data(), current.start(), current.getLength(), document);
            qname = new QName("", pi.getTarget(), "");
            text.append(pi.getData());
        }
    }

    private static class ElementEvent {

        private Value data;

        private int childCount = 0;

        private int currentChild = 0;

        public ElementEvent(Value data) {
            this.data = data;
            childCount = ByteConversion.byteToInt(data.data(), data.start() + StoredNode.LENGTH_SIGNATURE_LENGTH);
        }

        @SuppressWarnings("unused")
    public Value getData() {
            return data;
        }

        public int getChildCount() {
            return childCount;
        }

        public int getCurrentChild() {
            return currentChild;
        }

        @SuppressWarnings("unused")
    public void setCurrentChild(int currentChild) {
            this.currentChild = currentChild;
        }

        public void incrementChild() {
            currentChild++;
        }
    }
}
TOP

Related Classes of org.exist.stax.EmbeddedXMLStreamReader$ElementEvent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.