KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ozoneDB > xml > util > SAXChunkProducer


1 // You can redistribute this software and/or modify it under the terms of
2
// the Ozone Library License version 1 published by ozone-db.org.
3
//
4
// The original code and portions created by SMB are
5
// Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
6
//
7
// $Id: SAXChunkProducer.java,v 1.4 2003/11/07 21:34:22 per_nyfelt Exp $
8

9 package org.ozoneDB.xml.util;
10
11 import java.io.IOException JavaDoc;
12 import java.io.Serializable JavaDoc;
13
14 import org.ozoneDB.DxLib.DxDeque;
15 import org.ozoneDB.DxLib.DxArrayDeque;
16
17 import org.w3c.dom.Node JavaDoc;
18 import org.w3c.dom.NodeList JavaDoc;
19 import org.w3c.dom.Document JavaDoc;
20 import org.w3c.dom.Element JavaDoc;
21 import org.w3c.dom.NamedNodeMap JavaDoc;
22
23 import org.xml.sax.ContentHandler JavaDoc;
24 import org.xml.sax.ext.LexicalHandler JavaDoc;
25 import org.xml.sax.Attributes JavaDoc;
26 import org.xml.sax.Locator JavaDoc;
27 import org.xml.sax.SAXException JavaDoc;
28 import org.xml.sax.helpers.AttributesImpl JavaDoc;
29
30
31 /**
32  * This class produces a sequence of SAXEventChunks out of SAX events or
33  * out of a DOM tree. These chunks are used to communicate between client and
34  * server.
35  *
36  *
37  * @version $Revision: 1.4 $ $Date: 2003/11/07 21:34:22 $
38  * @author <a HREF="http://www.smb-tec.com">SMB</a>
39  */

40 public final class SAXChunkProducer implements ContentHandler JavaDoc, LexicalHandler JavaDoc, Serializable JavaDoc {
41
42     // Constants
43

44     private final static boolean debug = false;
45
46     private final static int DEFAULT_CHUNK_SIZE = 100000;
47
48     private final static int DEFAULT_CHUNK_INCREASE = 4096;
49
50     /** the default state */
51     private final static int CHUNK_STATE_INVALID = -1;
52
53     /** the initial state */
54     private final static int CHUNK_STATE_INIT = 0;
55
56     /** a new node has to be processed */
57     private final static int CHUNK_STATE_NODE = 1;
58
59     /** Next step is to close an element (or document) */
60     private final static int CHUNK_STATE_CLOSE = 3;
61
62     /** Conversion was finished. I.e. there are no more events to throw */
63     private final static int CHUNK_STATE_FINISH = 4;
64
65
66     // Data
67

68     /**
69      * Used for SAX storage. Because chunk processing is done using
70      * {@link SAXChunkProducerDelegate} (which means consumer creation and
71      * consumer usage happens in different methods) the consumer has to be
72      * stored somewhere. I choose the corresponding producer.
73      */

74     public SAXChunkConsumer dbConsumer;
75
76
77     private final SAXChunkProducerDelegate delegate;
78
79     private final ChunkOutputStream chunkOutput;
80
81     private final CXMLContentHandler contentHandler;
82
83     private final LexicalHandler JavaDoc lexicalHandler;
84
85     /**
86      * True if all descendant children shall be traversed, false otherwise.
87      * @see #depth
88      */

89     private final boolean deep;
90
91
92     /**
93      * The depth of the traversal. If {@link #deep} is false this determines
94      * how deep descendant shall be traversed, otherwise this member is ignored.
95      */

96     private int depth;
97
98
99     private int processLevel = 0;
100
101     /**
102      * Keeps information about which nodes have already been opened.
103      * (used by {@link #createNextChunk() createNextChunk})
104      */

105     private final DxDeque endEvents;
106
107
108     private NodeList JavaDoc sourceNodes;
109     private int sourceNodesIndex;
110
111     /**
112      * The node that has to be converted next; used by
113      * {@link #createNextChunk() createNextChunk}. This has to be initialized to
114      * the start node before calling {@link #createNextChunk() createNextChunk}
115      * the first time.
116      */

117     private Node JavaDoc nextNode;
118
119
120     /**
121      * implies that the node to be converted was created by a DOM Level 2
122      * implementation.
123      * used by {@link #createNextChunk() createNextChunk}.
124      */

125     private final boolean domLevel2;
126
127     /**
128      * the state of the {@link #createNextChunk() createNextChunk} method
129      * @see #CHUNK_STATE_INVALID
130      * @see #CHUNK_STATE_INIT
131      * @see #CHUNK_STATE_NODE
132      * @see #CHUNK_STATE_CLOSE
133      */

134     private int chunkState = CHUNK_STATE_INVALID;
135
136
137     /**
138      */

139 /* public SAXChunkProducer( Node node ) throws IOException{
140         this( node, SAXChunkProducer.DEFAULT_CHUNK_SIZE, -1 );
141     }
142 */

143
144     /**
145      */

146 /* public SAXChunkProducer( Node node, int _depth ) throws IOException{
147         this( node, SAXChunkProducer.DEFAULT_CHUNK_SIZE, _depth );
148     }
149 */

150
151     /**
152      */

153 /* public SAXChunkProducer( Node _node, int _maxParts, int _depth ) throws IOException{
154         ModifiableNodeList mnl = new ModifiableNodeList(1);
155         mnl.addNode(_node);
156         this(mnl, _maxParts, _depth);
157     }
158 */

159
160     /**
161      */

162     public SAXChunkProducer(NodeList JavaDoc _nodelist) throws IOException JavaDoc{
163         this(_nodelist, SAXChunkProducer.DEFAULT_CHUNK_SIZE, -1);
164     }
165
166
167     /**
168      */

169     public SAXChunkProducer(NodeList JavaDoc _nodelist, int _depth) throws IOException JavaDoc{
170         this(_nodelist, SAXChunkProducer.DEFAULT_CHUNK_SIZE, _depth);
171     }
172
173
174     /**
175      */

176     public SAXChunkProducer(NodeList JavaDoc _nodelist, int _maxParts, int _depth) throws IOException JavaDoc{
177         this.deep = _depth < 0;
178         this.depth = _depth < 0 ? 0 : _depth;
179
180         this.delegate = null;
181
182         this.sourceNodes = _nodelist;
183         this.sourceNodesIndex = 0;
184         this.nextNode = _nodelist.item(0);
185
186         this.chunkState = CHUNK_STATE_NODE;
187         this.endEvents = new DxArrayDeque();
188
189         Document JavaDoc factory = (Document JavaDoc)((this.nextNode.getNodeType() == Node.DOCUMENT_NODE)
190                 ? this.nextNode : this.nextNode.getOwnerDocument());
191         this.domLevel2 = factory.getImplementation().hasFeature( "XML", "2.0" );
192
193         this.chunkOutput = new ChunkOutputStream( SAXChunkProducer.DEFAULT_CHUNK_SIZE, SAXChunkProducer.DEFAULT_CHUNK_INCREASE );
194         this.contentHandler = new CXMLContentHandler( this.chunkOutput );
195         this.lexicalHandler = (this.contentHandler instanceof LexicalHandler JavaDoc)
196                 ? this.contentHandler
197                 : null;
198     }
199
200
201     /**
202      */

203     public SAXChunkProducer( SAXChunkProducerDelegate _delegate ) throws IOException JavaDoc{
204         this( _delegate, SAXChunkProducer.DEFAULT_CHUNK_SIZE, -1 );
205     }
206
207
208     /**
209      */

210     public SAXChunkProducer( SAXChunkProducerDelegate _delegate, int _depth ) throws IOException JavaDoc{
211         this( _delegate, SAXChunkProducer.DEFAULT_CHUNK_SIZE, _depth );
212     }
213
214
215     /**
216      */

217     public SAXChunkProducer( SAXChunkProducerDelegate _delegate, int _maxParts, int _depth ) throws IOException JavaDoc{
218         this.deep = _depth == -1;
219         this.depth = this.deep ? 0 : _depth;
220
221         this.delegate = _delegate;
222
223         this.chunkOutput = new ChunkOutputStream( SAXChunkProducer.DEFAULT_CHUNK_SIZE,
224                 SAXChunkProducer.DEFAULT_CHUNK_INCREASE );
225         this.contentHandler = new CXMLContentHandler( this.chunkOutput );
226         this.lexicalHandler = (this.contentHandler instanceof LexicalHandler JavaDoc)
227                 ? this.contentHandler
228                 : null;
229
230         this.domLevel2 = false;
231         this.endEvents = null;
232     }
233
234
235     /**
236      * @return the data of the chunk created during the last call of
237      * {@link #createNextChunk()}.
238      */

239     public ChunkOutputStream chunkStream() throws SAXException JavaDoc {
240         return this.chunkOutput;
241     }
242
243
244     /**
245      * Converts a given DOM tree in multiple steps to SAX events.
246      * Every call throws the specified number of events (or less, if the
247      * conversion is finished).
248      * Before calling this method the first time {@link #nextNode nextNode} has to
249      * be set to the root node of the DOM tree to convert, and
250      * {@link #chunkState} has to be set to
251      * {@link #CHUNK_STATE_INIT}. (The same way you can reset the nextChunk
252      * context.)
253      *
254      * @throws SAXException
255      * @throws IllegalStateException if {@link #chunkState} has
256      * not been set or has been set to an unknown value.
257      * @throws IllegalArgumentException if the value of _eventsToThrow was
258      * equal or less than 0.
259      */

260     public final void createNextChunk() throws SAXException JavaDoc {
261         String JavaDoc uri;
262         String JavaDoc qName;
263         String JavaDoc lName;
264
265         // now on the client
266
// chunkOutput.reset();
267

268         while (chunkOutput.getState() != ChunkOutputStream.STATE_OVERFLOW && chunkState != CHUNK_STATE_FINISH) {
269             //throw events until the chunk is filled
270

271             switch (chunkState) {
272             case CHUNK_STATE_NODE:
273                 switch (nextNode.getNodeType()) {
274                 case Node.DOCUMENT_NODE:
275                     contentHandler.startDocument();
276                     depth -= deep ? 0 : 1;
277
278                     endEvents.push( nextNode );
279
280                     //any children to process ?
281
nextNode = nextNode.getFirstChild();
282                     chunkState = ((nextNode == null) || (depth < 0))
283                             ? CHUNK_STATE_CLOSE
284                             : CHUNK_STATE_NODE;
285                     nextNode = (chunkState == CHUNK_STATE_CLOSE) ? (Node JavaDoc)endEvents.pop() : nextNode;
286                     break;
287                 case Node.ELEMENT_NODE:
288                     Element JavaDoc elem = (Element JavaDoc)nextNode;
289                     Attributes JavaDoc saxAttr = createSAXAttributes(elem);
290                     uri = domLevel2 ? elem.getNamespaceURI() : "";
291                     qName = elem.getNodeName();
292                     lName = domLevel2 ? elem.getLocalName() : qName;
293
294                     contentHandler.startElement(
295                             uri == null ? "" : uri,
296                             lName == null ? qName : lName,
297                             qName, saxAttr );
298
299                     endEvents.push(nextNode);
300
301                     depth -= deep ? 0 : 1;
302
303                     nextNode = nextNode.getFirstChild();
304                     chunkState = ((nextNode == null) || (depth < 0)) ? CHUNK_STATE_CLOSE : CHUNK_STATE_NODE;
305                     nextNode = (chunkState == CHUNK_STATE_CLOSE) ? (Node JavaDoc)endEvents.pop() : nextNode;
306                     break;
307                 default:
308                     convertSingleEventNode( nextNode );
309
310                     nextNode = nextNode.getNextSibling();
311                     chunkState = ((nextNode != null) && (endEvents.peek() != null))
312                             ? CHUNK_STATE_NODE
313                             : ((nextNode = (Node JavaDoc)endEvents.pop()) != null)
314                                 ? CHUNK_STATE_CLOSE
315                                 : CHUNK_STATE_FINISH;
316
317                     if (this.chunkState == CHUNK_STATE_FINISH) {
318                         this.sourceNodesIndex++;
319                         if (this.sourceNodesIndex < this.sourceNodes.getLength()) {
320                             this.nextNode = this.sourceNodes.item(this.sourceNodesIndex);
321                             this.chunkState = CHUNK_STATE_NODE;
322                         }
323
324                     }
325
326                     break;
327                 }
328                 break;
329
330             case CHUNK_STATE_CLOSE: {
331                 switch (nextNode.getNodeType()) {
332                 case Node.ELEMENT_NODE:
333                     depth += deep ? 0 : 1;
334
335                     Element JavaDoc elem = (Element JavaDoc)nextNode;
336                     uri = domLevel2 ? elem.getNamespaceURI() : "";
337                     qName = elem.getNodeName();
338                     lName = domLevel2 ? elem.getLocalName() : qName;
339                     contentHandler.endElement(
340                             uri == null ? "" : uri,
341                             lName == null ? qName : lName,
342                             qName);
343
344                     nextNode = elem.getNextSibling();
345                     break;
346                 case Node.DOCUMENT_NODE:
347                     depth += deep ? 0 : 1;
348                     contentHandler.endDocument();
349                     nextNode = null;
350                     break;
351                 default:
352                     throw new IllegalStateException JavaDoc( "endEvents stack contains unproper value: " + nextNode );
353                     //break;
354
}
355
356                 chunkState = ((nextNode != null) && (endEvents.peek() != null))
357                         ? CHUNK_STATE_NODE
358                         : ((nextNode = (Node JavaDoc)endEvents.pop()) != null)
359                             ? CHUNK_STATE_CLOSE
360                             : CHUNK_STATE_FINISH;
361
362                 if (this.chunkState == CHUNK_STATE_FINISH) {
363                     this.sourceNodesIndex++;
364                     if (this.sourceNodesIndex < this.sourceNodes.getLength()) {
365                         this.nextNode = this.sourceNodes.item(this.sourceNodesIndex);
366                         this.chunkState = CHUNK_STATE_NODE;
367                     }
368
369                 }
370
371                 break;
372                 }
373             default:
374                 throw new IllegalStateException JavaDoc("Unsupported value in member chunkState: " + chunkState);
375             }
376         }
377
378         if (chunkState == CHUNK_STATE_FINISH) {
379             chunkOutput.setEndFlag();
380         }
381     }
382
383
384     /**
385      * This method is used for DOM nodes that don't require two SAX events.
386      * i.e. every node except document and element nodes.
387      * @param _node the node to convert
388      */

389     private void convertSingleEventNode(Node JavaDoc _node) throws SAXException JavaDoc {
390
391         switch (_node.getNodeType()) {
392         case Node.TEXT_NODE:
393             char[] ch = _node.getNodeValue().toCharArray();
394             this.contentHandler.characters( ch, 0, ch.length );
395             break;
396         case Node.CDATA_SECTION_NODE:
397             char[] cdata = _node.getNodeValue().toCharArray();
398             if (this.lexicalHandler != null) {
399                 this.lexicalHandler.startCDATA();
400             }
401
402             this.contentHandler.characters( cdata, 0, cdata.length );
403
404             if (this.lexicalHandler != null) {
405                 this.lexicalHandler.endCDATA();
406             }
407             break;
408         case Node.PROCESSING_INSTRUCTION_NODE:
409             this.contentHandler.processingInstruction( _node.getNodeName(), _node.getNodeValue() );
410             break;
411         case Node.ENTITY_REFERENCE_NODE:
412             this.contentHandler.skippedEntity( _node.getNodeName() );
413             break;
414         case Node.COMMENT_NODE:
415             if (this.lexicalHandler != null) {
416                 char[] comment = _node.getNodeValue().toCharArray();
417                 this.lexicalHandler.comment(comment, 0, comment.length);
418             }
419             break;
420         case Node.DOCUMENT_TYPE_NODE:
421             //"Document type node can't be translated: "
422
break;
423         case Node.DOCUMENT_FRAGMENT_NODE:
424             //"Document fragment node can't be translated: "
425
break;
426         case Node.NOTATION_NODE:
427             //"Notation node can't be translated: "
428
break;
429         case Node.ENTITY_NODE:
430             //"Entity node can't be translated: "
431
break;
432         case Node.ATTRIBUTE_NODE:
433             //"Standalone attribute node can't be translated: "
434
break;
435         default:
436             //"unknown node type or node type not supported by this method
437
break;
438         }
439     }
440
441
442     /**
443      * creates a Attributes object (list of SAX attributes) containing the
444      * attributes of a given DOM element node.
445      * @param elem the element whose attributes shall be converted
446      * @return the SAX attribute list
447      */

448     private Attributes JavaDoc createSAXAttributes( Element JavaDoc elem ) {
449         NamedNodeMap JavaDoc domAttributes = elem.getAttributes();
450         AttributesImpl JavaDoc saxAttributes = new AttributesImpl JavaDoc();
451
452         int length = domAttributes.getLength();
453
454         for (int i = 0; i < length; i++) {
455
456             Node JavaDoc node = domAttributes.item(i);
457             String JavaDoc uri = domLevel2 ? node.getNamespaceURI() : "";
458             String JavaDoc qName = node.getNodeName();
459             String JavaDoc lName = domLevel2 ? node.getLocalName() : qName;
460
461             saxAttributes.addAttribute(
462                     (uri == null) ? "" : uri,
463                     (lName == null) ? qName : lName,
464                     qName, "",
465                     node.getNodeValue());
466         }
467
468         return saxAttributes;
469     }
470
471
472     // SAX ContentHandler implementation
473

474
475     /**
476      * Received notification of the beginning of the document.
477      */

478     public final void startDocument() throws SAXException JavaDoc {
479         if (deep || depth >= 0) {
480             if (debug) {
481                 System.out.println( "SAXChunkProducer.startDocument()..." );
482             }
483
484             this.contentHandler.startDocument();
485             this.processLevel++;
486             checkChunk();
487         }
488
489         depth -= deep ? 0 : 1;
490     }
491
492
493     /**
494      * Received notification of the end of the document.
495      */

496     public final void endDocument() throws SAXException JavaDoc {
497         depth += deep ? 0 : 1;
498
499         if (deep || depth >= 0) {
500             if (debug) {
501                 System.out.println( "SAXChunkProducer.endDocument()..." );
502             }
503
504             this.contentHandler.endDocument();
505             this.processLevel--;
506             checkChunk();
507         }
508     }
509
510
511     /**
512      * Receive notification of the start of an element.
513      */

514     public final void startElement( String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc rawName, Attributes JavaDoc atts )
515             throws SAXException JavaDoc {
516         if (deep || depth >= 0) {
517             if (debug) {
518                 System.out.println( "SAXChunkProducer.startElement()..." );
519             }
520
521             this.contentHandler.startElement( namespaceURI, localName, rawName, atts );
522             this.processLevel++;
523             checkChunk();
524         }
525
526         depth -= deep ? 0 : 1;
527     }
528
529
530     /**
531      * Receive notification of the end of an element.
532      */

533     public final void endElement( String JavaDoc _namespaceURI, String JavaDoc _localName, String JavaDoc _rawName ) throws SAXException JavaDoc {
534         depth = deep ? 0 : 1;
535
536         if (deep || depth >= 0) {
537             if (debug) {
538                 System.out.println( "SAXChunkProducer.endElement()..." );
539             }
540
541             this.contentHandler.endElement( _namespaceURI, _localName, _rawName );
542             this.processLevel--;
543             checkChunk();
544         }
545     }
546
547
548     /**
549      * Receive notification of character data inside an element.
550      */

551     public final void characters( char[] ch, int start, int length ) throws SAXException JavaDoc {
552         if (deep || depth >= 0) {
553             if (debug) {
554                 System.out.println( "SAXChunkProducer.characters()..." );
555             }
556
557             char[] characters = new char[length];
558             System.arraycopy( ch, start, characters, 0, length );
559
560             this.contentHandler.characters( characters, 0, characters.length );
561             checkChunk();
562         }
563     }
564
565
566     /**
567      * Receive notification of a processing instruction.
568      */

569     public final void processingInstruction( String JavaDoc target, String JavaDoc data ) throws SAXException JavaDoc {
570         if (deep || depth >= 0) {
571             if (debug) {
572                 System.out.println( "SAXChunkProducer.pi ..." );
573             }
574
575             this.contentHandler.processingInstruction( target, data );
576             checkChunk();
577         }
578     }
579
580
581     /**
582      * Receive notification of a skipped entity.
583      */

584     public final void skippedEntity( java.lang.String JavaDoc name ) throws SAXException JavaDoc {
585         if (deep || depth >= 0) {
586             this.contentHandler.skippedEntity( name );
587             checkChunk();
588         }
589     }
590
591
592     /**
593      * Begin the scope of a prefix-URI Namespace mapping.
594      */

595     public final void startPrefixMapping( String JavaDoc prefix, String JavaDoc uri ) throws SAXException JavaDoc {
596         if (deep || depth >= 0) {
597             if (debug) {
598                 System.out.println( "SAXChunkProducer.startPrefixMapping ..." );
599             }
600
601             this.contentHandler.startPrefixMapping( prefix, uri );
602             checkChunk();
603         }
604     }
605
606
607     /**
608      * End the scope of a prefix-URI mapping.
609      */

610     public final void endPrefixMapping( String JavaDoc prefix ) throws SAXException JavaDoc {
611         if (deep || depth >= 0) {
612             if (debug) {
613                 System.out.println( "SAXChunkProducer.endPrefixMapping()..." );
614             }
615
616             this.contentHandler.endPrefixMapping( prefix );
617             checkChunk();
618         }
619     }
620
621
622     /**
623      * Receive notification of ignorable whitespace in element content.
624      */

625     public final void ignorableWhitespace( char[] ch, int start, int length ) throws SAXException JavaDoc {
626         // ignorable whitespaces will be ignored
627
}
628
629
630     /**
631      * Receive an object for locating the origin of SAX document events.
632      */

633     public final void setDocumentLocator( Locator JavaDoc locator ) {
634     // we don't care about the origin of the document
635
}
636
637
638     //
639
// SAX LexicalHandler implemenation
640
//
641

642
643     public void comment( char[] ch, int start, int length ) throws SAXException JavaDoc {
644         if ((this.lexicalHandler != null)
645                 && (deep || depth >= 0)) {
646             this.lexicalHandler.comment(ch, start, length);
647             checkChunk();
648         }
649     }
650
651
652     public void startCDATA() throws SAXException JavaDoc {
653         if ((this.lexicalHandler != null)
654                 && (deep || depth >= 0)) {
655             this.lexicalHandler.startCDATA();
656             checkChunk();
657         }
658     }
659
660
661     public void endCDATA() throws SAXException JavaDoc {
662         if ((this.lexicalHandler != null)
663                 && (deep || depth >= 0)) {
664             this.lexicalHandler.endCDATA();
665             checkChunk();
666         }
667     }
668
669
670     public void startDTD(String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId)
671             throws SAXException JavaDoc {
672         if ((this.lexicalHandler != null)
673                 && (deep || depth >= 0)) {
674             this.lexicalHandler.startDTD(name, publicId, systemId);
675             checkChunk();
676         }
677     }
678
679
680     public void endDTD() throws SAXException JavaDoc {
681         if ((this.lexicalHandler != null)
682                 && (deep || depth >= 0)) {
683             this.lexicalHandler.endDTD();
684             checkChunk();
685         }
686     }
687
688
689     public void startEntity(String JavaDoc name) throws SAXException JavaDoc {
690         if ((this.lexicalHandler != null)
691                 && (deep || depth >= 0)) {
692             this.lexicalHandler.startEntity(name);
693             checkChunk();
694         }
695     }
696
697
698     public void endEntity(String JavaDoc name) throws SAXException JavaDoc {
699         if ((this.lexicalHandler != null)
700                 && (deep || depth >= 0)) {
701             this.lexicalHandler.startEntity(name);
702             checkChunk();
703         }
704     }
705
706
707     protected final void checkChunk() {
708         if (this.delegate == null) {
709             return;
710         }
711
712         try {
713             if ((this.chunkOutput.getState() == ChunkOutputStream.STATE_OVERFLOW)
714                     || (this.processLevel == 0)) {
715                 this.delegate.processChunk(this);
716                 this.chunkOutput.reset();
717             }
718         } catch (Exception JavaDoc e) {
719             e.printStackTrace();
720             throw new RuntimeException JavaDoc(e.toString());
721         }
722     }
723
724 }
725
Popular Tags