📄 saxchunkproducer.java
字号:
// 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.4 2003/11/07 21:34:22 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 SAXEventChunks out of SAX events or
* out of a DOM tree. These chunks are used to communicate between client and
* server.
*
*
* @version $Revision: 1.4 $ $Date: 2003/11/07 21:34:22 $
* @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.)
*
* @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++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -