Package org.ozoneDB.xml.util

Source Code of org.ozoneDB.xml.util.SAXChunkProducer

// You can redistribute this software and/or modify it under the terms of
// the Ozone Library License version 1 published by ozone-db.org.
//
// The original code and portions created by SMB are
// Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
//
// $Id: SAXChunkProducer.java,v 1.1 2001/12/18 10:31:31 per_nyfelt Exp $

package org.ozoneDB.xml.util;

import java.io.IOException;
import java.io.Serializable;

import org.ozoneDB.DxLib.DxDeque;
import org.ozoneDB.DxLib.DxArrayDeque;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;

import org.xml.sax.ContentHandler;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;


/**
* This class produces a sequence of {@link SAXEventChunk}s out of SAX events or
* out of a DOM tree. These chunks are used to communicate between client and
* server.
*
*
* @version $Revision: 1.1 $ $Date: 2001/12/18 10:31:31 $
* @author <a href="http://www.smb-tec.com">SMB</a>
*/
public final class SAXChunkProducer implements ContentHandler, LexicalHandler, Serializable {
   
    // Constants
   
    private final static boolean debug = false;
   
    private final static int DEFAULT_CHUNK_SIZE = 100000;
   
    private final static int DEFAULT_CHUNK_INCREASE = 4096;
   
    /** the default state */
    private final static int CHUNK_STATE_INVALID = -1;
   
    /** the initial state */
    private final static int CHUNK_STATE_INIT = 0;
   
    /** a new node has to be processed */
    private final static int CHUNK_STATE_NODE = 1;
   
    /** next step is to close an element (or document) */
    private final static int CHUNK_STATE_CLOSE = 3;
   
    /** conversion was finished. i.e. there are no more events to throw */
    private final static int CHUNK_STATE_FINISH = 4;
   
   
    // Data
   
    /**
     *  Used for SAX storage. Because chunk processing is done using
     *  {@link SAXChunkProducerDelegate} (which means consumer creation and
     *  consumer usage happens in different methods) the consumer has to be
     *  stored somewhere. I choose the corresponding producer.
     */
    public SAXChunkConsumer dbConsumer;
   
   
    private final SAXChunkProducerDelegate delegate;
   
    private final ChunkOutputStream chunkOutput;
   
    private final CXMLContentHandler contentHandler;
   
    private final LexicalHandler lexicalHandler;
   
    /**
     *  True if all descendant children shall be traversed, false otherwise.
     *  @see #depth
     */
    private final boolean deep;
   
   
    /**
     *  The depth of the traversal. If {@link #deep} is false this determines
     *  how deep descendant shall be traversed, otherwise this member is ignored.
     */
    private int depth;
   
   
    private int processLevel = 0;
   
    /**
     * Keeps information about which nodes have already been opened.
     * (used by {@link #createNextChunk() createNextChunk})
     */
    private final DxDeque endEvents;
   
   
    private NodeList sourceNodes;
    private int sourceNodesIndex;
   
    /**
     * The node that has to be converted next; used by
     * {@link createNextChunk() createNextChunk}. This has to be initialized to
     * the start node before calling {@link createNextChunk() createNextChunk}
     * the first time.
     */
    private Node nextNode;
   
   
    /**
     * implies that the node to be converted was created by a DOM Level 2
     * implementation.
     * used by {@link createNextChunk() createNextChunk}.
     */
    private final boolean domLevel2;
   
    /**
     *  the state of the {@link createNextChunk() createNextChunk} method
     *  @see #CHUNK_STATE_INVALID
     *  @see #CHUNK_STATE_INIT
     *  @see #CHUNK_STATE_NODE
     *  @see #CHUNK_STATE_CLOSE
     */
    private int chunkState = CHUNK_STATE_INVALID;
   
   
    /**
     */
/*    public SAXChunkProducer( Node node ) throws IOException{
        this( node, SAXChunkProducer.DEFAULT_CHUNK_SIZE, -1 );
    }
*/   
   
    /**
     */
/*    public SAXChunkProducer( Node node, int _depth ) throws IOException{
        this( node, SAXChunkProducer.DEFAULT_CHUNK_SIZE, _depth );
    }
*/   
   
    /**
     */
/*    public SAXChunkProducer( Node _node, int _maxParts, int _depth ) throws IOException{
        ModifiableNodeList mnl = new ModifiableNodeList(1);
        mnl.addNode(_node);
        this(mnl, _maxParts, _depth);
    }
*/   
   
    /**
     */
    public SAXChunkProducer(NodeList _nodelist) throws IOException{
        this(_nodelist, SAXChunkProducer.DEFAULT_CHUNK_SIZE, -1);
    }
   
   
    /**
     */
    public SAXChunkProducer(NodeList _nodelist, int _depth) throws IOException{
        this(_nodelist, SAXChunkProducer.DEFAULT_CHUNK_SIZE, _depth);
    }
   
   
    /**
     */
    public SAXChunkProducer(NodeList _nodelist, int _maxParts, int _depth) throws IOException{
        this.deep = _depth < 0;
        this.depth = _depth < 0 ? 0 : _depth;
       
        this.delegate = null;
       
        this.sourceNodes = _nodelist;
        this.sourceNodesIndex = 0;
        this.nextNode = _nodelist.item(0);
       
        this.chunkState = CHUNK_STATE_NODE;
        this.endEvents = new DxArrayDeque();
       
        Document factory = (Document)((this.nextNode.getNodeType() == Node.DOCUMENT_NODE)
                ? this.nextNode : this.nextNode.getOwnerDocument());
        this.domLevel2 = factory.getImplementation().hasFeature( "XML", "2.0" );
       
        this.chunkOutput = new ChunkOutputStream( SAXChunkProducer.DEFAULT_CHUNK_SIZE, SAXChunkProducer.DEFAULT_CHUNK_INCREASE );
        this.contentHandler = new CXMLContentHandler( this.chunkOutput );
        this.lexicalHandler = (this.contentHandler instanceof LexicalHandler)
                ? this.contentHandler
                : null;
    }
   
   
    /**
     */
    public SAXChunkProducer( SAXChunkProducerDelegate _delegate ) throws IOException{
        this( _delegate, SAXChunkProducer.DEFAULT_CHUNK_SIZE, -1 );
    }
   
   
    /**
     */
    public SAXChunkProducer( SAXChunkProducerDelegate _delegate, int _depth ) throws IOException{
        this( _delegate, SAXChunkProducer.DEFAULT_CHUNK_SIZE, _depth );
    }
   
   
    /**
     */
    public SAXChunkProducer( SAXChunkProducerDelegate _delegate, int _maxParts, int _depth ) throws IOException{
        this.deep = _depth == -1;
        this.depth = this.deep ? 0 : _depth;
       
        this.delegate = _delegate;
       
        this.chunkOutput = new ChunkOutputStream( SAXChunkProducer.DEFAULT_CHUNK_SIZE,
                SAXChunkProducer.DEFAULT_CHUNK_INCREASE );
        this.contentHandler = new CXMLContentHandler( this.chunkOutput );
        this.lexicalHandler = (this.contentHandler instanceof LexicalHandler)
                ? this.contentHandler
                : null;
       
        this.domLevel2 = false;
        this.endEvents = null;
    }
   
   
    /**
     * @return the data of the chunk created during the last call of
     *          {@link #createNextChunk()}.
     */
    public ChunkOutputStream chunkStream() throws SAXException {
        return this.chunkOutput;
    }
   
   
    /**
     *  Converts a given DOM tree in multiple steps to SAX events.
     *  Every call throws the specified number of events (or less, if the
     *  conversion is finished).
     *  Before calling this method the first time {@link #nextNode nextNode} has to
     *  be set to the root node of the DOM tree to convert, and
     *  {@link #chunkState} has to be set to
     *  {@link #CHUNK_STATE_INIT}. (The same way you can reset the nextChunk
     *  context.)
     *
     *  @param _contentHandler SAX content handler that receives the events
     *  @param _eventsToThrow the number of events to throw
     *  @return true if there are still events to throw, false if processing has
     *          finished
     *  @throws SAXException
     *  @throws IllegalStateException if {@link #chunkState} has
     *          not been set or has been set to an unknown value.
     *  @throws IllegalArgumentException if the value of _eventsToThrow was
     *          equal or less than 0.
     */
    public final void createNextChunk() throws SAXException {
        String uri;
        String qName;
        String lName;

        // now on the client
       // chunkOutput.reset();
       
        while (chunkOutput.getState() != ChunkOutputStream.STATE_OVERFLOW && chunkState != CHUNK_STATE_FINISH) {
            //throw events until the chunk is filled
           
            switch (chunkState) {
            case CHUNK_STATE_NODE:
                switch (nextNode.getNodeType()) {
                case Node.DOCUMENT_NODE:
                    contentHandler.startDocument();
                    depth -= deep ? 0 : 1;

                    endEvents.push( nextNode );
                   
                    //any children to process ?
                    nextNode = nextNode.getFirstChild();
                    chunkState = ((nextNode == null) || (depth < 0))
                            ? CHUNK_STATE_CLOSE
                            : CHUNK_STATE_NODE;
                    nextNode = (chunkState == CHUNK_STATE_CLOSE) ? (Node)endEvents.pop() : nextNode;
                    break;
                case Node.ELEMENT_NODE:
                    Element elem = (Element)nextNode;
                    Attributes saxAttr = createSAXAttributes(elem);
                    uri = domLevel2 ? elem.getNamespaceURI() : "";
                    qName = elem.getNodeName();
                    lName = domLevel2 ? elem.getLocalName() : qName;

                    contentHandler.startElement(
                            uri == null ? ""  : uri,
                            lName == null ? qName : lName,
                            qName, saxAttr );

                    endEvents.push(nextNode);
                   
                    depth -= deep ? 0 : 1;
                   
                    nextNode = nextNode.getFirstChild();
                    chunkState = ((nextNode == null) || (depth < 0)) ? CHUNK_STATE_CLOSE : CHUNK_STATE_NODE;
                    nextNode = (chunkState == CHUNK_STATE_CLOSE) ? (Node)endEvents.pop() : nextNode;
                    break;
                default:
                    convertSingleEventNode( nextNode );

                    nextNode = nextNode.getNextSibling();
                    chunkState = ((nextNode != null) && (endEvents.peek() != null))
                            ? CHUNK_STATE_NODE
                            : ((nextNode = (Node)endEvents.pop()) != null)
                                ? CHUNK_STATE_CLOSE
                                : CHUNK_STATE_FINISH;

                    if (this.chunkState == CHUNK_STATE_FINISH) {
                        this.sourceNodesIndex++;
                        if (this.sourceNodesIndex < this.sourceNodes.getLength()) {
                            this.nextNode = this.sourceNodes.item(this.sourceNodesIndex);
                            this.chunkState = CHUNK_STATE_NODE;
                        }
                       
                    }

                    break;
                }
                break;
           
            case CHUNK_STATE_CLOSE: {
                switch (nextNode.getNodeType()) {
                case Node.ELEMENT_NODE:
                    depth += deep ? 0 : 1;
                   
                    Element elem = (Element)nextNode;
                    uri = domLevel2 ? elem.getNamespaceURI() : "";
                    qName = elem.getNodeName();
                    lName = domLevel2 ? elem.getLocalName() : qName;
                    contentHandler.endElement(
                            uri == null ? "" : uri,
                            lName == null ? qName : lName,
                            qName);

                    nextNode = elem.getNextSibling();
                    break;
                case Node.DOCUMENT_NODE:
                    depth += deep ? 0 : 1;
                    contentHandler.endDocument();
                    nextNode = null;
                    break;
                default:
                    throw new IllegalStateException( "endEvents stack contains unproper value: " + nextNode );
                    //break;
                }
               
                chunkState = ((nextNode != null) && (endEvents.peek() != null))
                        ? CHUNK_STATE_NODE
                        : ((nextNode = (Node)endEvents.pop()) != null)
                            ? CHUNK_STATE_CLOSE
                            : CHUNK_STATE_FINISH;

                if (this.chunkState == CHUNK_STATE_FINISH) {
                    this.sourceNodesIndex++;
                    if (this.sourceNodesIndex < this.sourceNodes.getLength()) {
                        this.nextNode = this.sourceNodes.item(this.sourceNodesIndex);
                        this.chunkState = CHUNK_STATE_NODE;
                    }
                   
                }

                break;
                }
            default:
                throw new IllegalStateException("Unsupported value in member chunkState: " + chunkState);
            }
        }
       
        if (chunkState == CHUNK_STATE_FINISH) {
            chunkOutput.setEndFlag();
        }
    }
   
   
    /**
     *  This method is used for DOM nodes that don't require two SAX events.
     *  i.e. every node except document and element nodes.
     *  @param _node the node to convert
     */
    private void convertSingleEventNode(Node _node) throws SAXException {
       
        switch (_node.getNodeType()) {
        case Node.TEXT_NODE:
            char[] ch = _node.getNodeValue().toCharArray();
            this.contentHandler.characters( ch, 0, ch.length );
            break;
        case Node.CDATA_SECTION_NODE:
            char[] cdata = _node.getNodeValue().toCharArray();
            if (this.lexicalHandler != null) {
                this.lexicalHandler.startCDATA();
            }
           
            this.contentHandler.characters( cdata, 0, cdata.length );

            if (this.lexicalHandler != null) {
                this.lexicalHandler.endCDATA();
            }
            break;
        case Node.PROCESSING_INSTRUCTION_NODE:
            this.contentHandler.processingInstruction( _node.getNodeName(), _node.getNodeValue() );
            break;
        case Node.ENTITY_REFERENCE_NODE:
            this.contentHandler.skippedEntity( _node.getNodeName() );
            break;
        case Node.COMMENT_NODE:
            if (this.lexicalHandler != null) {
                char[] comment = _node.getNodeValue().toCharArray();
                this.lexicalHandler.comment(comment, 0, comment.length);
            }
            break;
        case Node.DOCUMENT_TYPE_NODE:
            //"Document type node can't be translated: "
            break;
        case Node.DOCUMENT_FRAGMENT_NODE:
            //"Document fragment node can't be translated: "
            break;
        case Node.NOTATION_NODE:
            //"Notation node can't be translated: "
            break;
        case Node.ENTITY_NODE:
            //"Entity node can't be translated: "
            break;
        case Node.ATTRIBUTE_NODE:
            //"Standalone attribute node can't be translated: "
            break;
        default:
            //"unknown node type or node type not supported by this method
            break;
        }
    }
   
   
    /**
     *  creates a Attributes object (list of SAX attributes) containing the
     *  attributes of a given DOM element node.
     *  @param elem the element whose attributes shall be converted
     *  @return the SAX attribute list
     */
    private Attributes createSAXAttributes( Element elem ) {
        NamedNodeMap domAttributes = elem.getAttributes();
        AttributesImpl saxAttributes = new AttributesImpl();
       
        int length = domAttributes.getLength();
       
        for (int i = 0; i < length; i++) {

            Node node = domAttributes.item(i);
            String uri = domLevel2 ? node.getNamespaceURI() : "";
            String qName = node.getNodeName();
            String lName = domLevel2 ? node.getLocalName() : qName;

            saxAttributes.addAttribute(
                    (uri == null) ? "" : uri,
                    (lName == null) ? qName : lName,
                    qName, "",
                    node.getNodeValue());
        }
       
        return saxAttributes;
    }
   
   
    // SAX ContentHandler implementation
   
   
    /**
     *  Received notification of the beginning of the document.
     */
    public final void startDocument() throws SAXException {
        if (deep || depth >= 0) {
            if (debug) {
                System.out.println( "SAXChunkProducer.startDocument()..." );
            }
           
            this.contentHandler.startDocument();
            this.processLevel++;
            checkChunk();
        }
       
        depth -= deep ? 0 : 1;
    }
   
   
    /**
     *  Received notification of the end of the document.
     */
    public final void endDocument() throws SAXException {
        depth += deep ? 0 : 1;
       
        if (deep || depth >= 0) {
            if (debug) {
                System.out.println( "SAXChunkProducer.endDocument()..." );
            }
           
            this.contentHandler.endDocument();
            this.processLevel--;
            checkChunk();
        }
    }
   
   
    /**
     *  Receive notification of the start of an element.
     */
    public final void startElement( String namespaceURI, String localName, String rawName, Attributes atts )
            throws SAXException {
        if (deep || depth >= 0) {
            if (debug) {
                System.out.println( "SAXChunkProducer.startElement()..." );
            }
           
            this.contentHandler.startElement( namespaceURI, localName, rawName, atts );
            this.processLevel++;
            checkChunk();
        }
       
        depth -= deep ? 0 : 1;
    }
   
   
    /**
     *  Receive notification of the end of an element.
     */
    public final void endElement( String _namespaceURI, String _localName, String _rawName ) throws SAXException {
        depth = deep ? 0 : 1;
       
        if (deep || depth >= 0) {
            if (debug) {
                System.out.println( "SAXChunkProducer.endElement()..." );
            }
           
            this.contentHandler.endElement( _namespaceURI, _localName, _rawName );
            this.processLevel--;
            checkChunk();
        }
    }
   
   
    /**
     *  Receive notification of character data inside an element.
     */
    public final void characters( char[] ch, int start, int length ) throws SAXException {
        if (deep || depth >= 0) {
            if (debug) {
                System.out.println( "SAXChunkProducer.characters()..." );
            }
           
            char[] characters = new char[length];
            System.arraycopy( ch, start, characters, 0, length );
           
            this.contentHandler.characters( characters, 0, characters.length );
            checkChunk();
        }
    }
   
   
    /**
     *  Receive notification of a processing instruction.
     */
    public final void processingInstruction( String target, String data ) throws SAXException {
        if (deep || depth >= 0) {
            if (debug) {
                System.out.println( "SAXChunkProducer.pi ..." );
            }
           
            this.contentHandler.processingInstruction( target, data );
            checkChunk();
        }
    }
   
   
    /**
     *  Receive notification of a skipped entity.
     */
    public final void skippedEntity( java.lang.String name ) throws SAXException {
        if (deep || depth >= 0) {
            this.contentHandler.skippedEntity( name );
            checkChunk();
        }
    }
   
   
    /**
     *  Begin the scope of a prefix-URI Namespace mapping.
     */
    public final void startPrefixMapping( String prefix, String uri ) throws SAXException {
        if (deep || depth >= 0) {
            if (debug) {
                System.out.println( "SAXChunkProducer.startPrefixMapping ..." );
            }
           
            this.contentHandler.startPrefixMapping( prefix, uri );
            checkChunk();
        }
    }
   
   
    /**
     *  End the scope of a prefix-URI mapping.
     */
    public final void endPrefixMapping( String prefix ) throws SAXException {
        if (deep || depth >= 0) {
            if (debug) {
                System.out.println( "SAXChunkProducer.endPrefixMapping()..." );
            }
           
            this.contentHandler.endPrefixMapping( prefix );
            checkChunk();
        }
    }
   
   
    /**
     *  Receive notification of ignorable whitespace in element content.
     */
    public final void ignorableWhitespace( char[] ch, int start, int length ) throws SAXException {
        // ignorable whitespaces will be ignored
    }
   
   
    /**
     *  Receive an object for locating the origin of SAX document events.
     */
    public final void setDocumentLocator( Locator locator ) {
    // we don't care about the origin of the document
    }
   
   
    //
    // SAX LexicalHandler implemenation
    //


    public void comment( char[] ch, int start, int length ) throws SAXException {
        if ((this.lexicalHandler != null)
                && (deep || depth >= 0)) {
            this.lexicalHandler.comment(ch, start, length);
            checkChunk();
        }
    }
   

    public void startCDATA() throws SAXException {
        if ((this.lexicalHandler != null)
                && (deep || depth >= 0)) {
            this.lexicalHandler.startCDATA();
            checkChunk();
        }
    }
   
   
    public void endCDATA() throws SAXException {
        if ((this.lexicalHandler != null)
                && (deep || depth >= 0)) {
            this.lexicalHandler.endCDATA();
            checkChunk();
        }
    }
   

    public void startDTD(String name, String publicId, String systemId)
            throws SAXException {
        if ((this.lexicalHandler != null)
                && (deep || depth >= 0)) {
            this.lexicalHandler.startDTD(name, publicId, systemId);
            checkChunk();
        }
    }


    public void endDTD() throws SAXException {
        if ((this.lexicalHandler != null)
                && (deep || depth >= 0)) {
            this.lexicalHandler.endDTD();
            checkChunk();
        }
    }


    public void startEntity(String name) throws SAXException {
        if ((this.lexicalHandler != null)
                && (deep || depth >= 0)) {
            this.lexicalHandler.startEntity(name);
            checkChunk();
        }
    }
   
   
    public void endEntity(String name) throws SAXException {
        if ((this.lexicalHandler != null)
                && (deep || depth >= 0)) {
            this.lexicalHandler.startEntity(name);
            checkChunk();
        }
    }


    protected final void checkChunk() {
        if (this.delegate == null) {
            return;
        }

        try {
            if ((this.chunkOutput.getState() == ChunkOutputStream.STATE_OVERFLOW)
                    || (this.processLevel == 0)) {
                this.delegate.processChunk(this);
                this.chunkOutput.reset();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.toString());
        }
    }
   
}
TOP

Related Classes of org.ozoneDB.xml.util.SAXChunkProducer

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.