001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.xml;
018:
019: import org.xml.sax.ContentHandler;
020: import org.xml.sax.SAXException;
021: import org.xml.sax.Locator;
022: import org.xml.sax.Attributes;
023: import org.xml.sax.ext.LexicalHandler;
024: import org.apache.excalibur.xml.sax.XMLizable;
025: import org.apache.avalon.excalibur.pool.Recyclable;
026:
027: import java.io.Serializable;
028: import java.io.Writer;
029: import java.io.IOException;
030: import java.util.ArrayList;
031: import java.util.Iterator;
032: import java.util.List;
033: import java.util.Collections;
034:
035: /**
036: * A class that can record SAX events and replay them later.
037: *
038: * <p>Compared to {@link org.apache.cocoon.components.sax.XMLByteStreamCompiler},
039: * this class is many times faster at sending out the recorded SAX events since
040: * it doesn't need to convert between byte and char representations etc.
041: * On the other hand, its data structure is more complex then a simple byte array,
042: * making XMLByteStreamCompiler better in case the recorded SAX should be stored long-term.
043: *
044: * <p>Use this class if you need to frequently generate smaller amounts of SAX events,
045: * or replay a set of recorded start events immediately.</p>
046: *
047: * <p>Both {@link ContentHandler} and {@link LexicalHandler} are supported, the only
048: * exception is that the setDocumentLocator event is not recorded.</p>
049: *
050: * @version CVS $Id: SaxBuffer.java 433543 2006-08-22 06:22:54Z crossley $
051: */
052: public class SaxBuffer extends AbstractSAXFragment implements
053: XMLConsumer, Recyclable, Serializable {
054:
055: /**
056: * Stores list of {@link SaxBit} objects.
057: */
058: protected List saxbits;
059:
060: /**
061: * Creates empty SaxBuffer
062: */
063: public SaxBuffer() {
064: this .saxbits = new ArrayList();
065: }
066:
067: /**
068: * Creates SaxBuffer based on the provided bits list.
069: */
070: public SaxBuffer(List bits) {
071: this .saxbits = bits;
072: }
073:
074: /**
075: * Creates copy of another SaxBuffer
076: */
077: public SaxBuffer(SaxBuffer saxBuffer) {
078: this .saxbits = new ArrayList(saxBuffer.saxbits);
079: }
080:
081: //
082: // ContentHandler Interface
083: //
084:
085: public void skippedEntity(String name) throws SAXException {
086: saxbits.add(new SkippedEntity(name));
087: }
088:
089: public void setDocumentLocator(Locator locator) {
090: // Don't record this event
091: }
092:
093: public void ignorableWhitespace(char ch[], int start, int length)
094: throws SAXException {
095: saxbits.add(new IgnorableWhitespace(ch, start, length));
096: }
097:
098: public void processingInstruction(String target, String data)
099: throws SAXException {
100: saxbits.add(new PI(target, data));
101: }
102:
103: public void startDocument() throws SAXException {
104: saxbits.add(StartDocument.SINGLETON);
105: }
106:
107: public void startElement(String namespaceURI, String localName,
108: String qName, Attributes atts) throws SAXException {
109: saxbits.add(new StartElement(namespaceURI, localName, qName,
110: atts));
111: }
112:
113: public void endPrefixMapping(String prefix) throws SAXException {
114: saxbits.add(new EndPrefixMapping(prefix));
115: }
116:
117: public void characters(char ch[], int start, int length)
118: throws SAXException {
119: saxbits.add(new Characters(ch, start, length));
120: }
121:
122: public void endElement(String namespaceURI, String localName,
123: String qName) throws SAXException {
124: saxbits.add(new EndElement(namespaceURI, localName, qName));
125: }
126:
127: public void endDocument() throws SAXException {
128: saxbits.add(EndDocument.SINGLETON);
129: }
130:
131: public void startPrefixMapping(String prefix, String uri)
132: throws SAXException {
133: saxbits.add(new StartPrefixMapping(prefix, uri));
134: }
135:
136: //
137: // LexicalHandler Interface
138: //
139:
140: public void endCDATA() throws SAXException {
141: saxbits.add(EndCDATA.SINGLETON);
142: }
143:
144: public void comment(char ch[], int start, int length)
145: throws SAXException {
146: saxbits.add(new Comment(ch, start, length));
147: }
148:
149: public void startEntity(String name) throws SAXException {
150: saxbits.add(new StartEntity(name));
151: }
152:
153: public void endDTD() throws SAXException {
154: saxbits.add(EndDTD.SINGLETON);
155: }
156:
157: public void startDTD(String name, String publicId, String systemId)
158: throws SAXException {
159: saxbits.add(new StartDTD(name, publicId, systemId));
160: }
161:
162: public void startCDATA() throws SAXException {
163: saxbits.add(StartCDATA.SINGLETON);
164: }
165:
166: public void endEntity(String name) throws SAXException {
167: saxbits.add(new EndEntity(name));
168: }
169:
170: //
171: // Public Methods
172: //
173:
174: /**
175: * Add a bit containing XMLizable object
176: */
177: public void xmlizable(XMLizable xml) {
178: saxbits.add(new XMLizableBit(xml));
179: }
180:
181: /**
182: * @return true if buffer is empty
183: */
184: public boolean isEmpty() {
185: return saxbits.isEmpty();
186: }
187:
188: /**
189: * @return unmodifiable list of SAX bits
190: */
191: public List getBits() {
192: return Collections.unmodifiableList(saxbits);
193: }
194:
195: /**
196: * Stream this buffer into the provided content handler.
197: * If contentHandler object implements LexicalHandler, it will get lexical
198: * events as well.
199: */
200: public void toSAX(ContentHandler contentHandler)
201: throws SAXException {
202: for (Iterator i = saxbits.iterator(); i.hasNext();) {
203: SaxBit saxbit = (SaxBit) i.next();
204: saxbit.send(contentHandler);
205: }
206: }
207:
208: /**
209: * @return String value of the buffer
210: */
211: public String toString() {
212: // NOTE: This method is used in i18n XML bundle implementation
213: final StringBuffer value = new StringBuffer();
214: for (Iterator i = saxbits.iterator(); i.hasNext();) {
215: final SaxBit saxbit = (SaxBit) i.next();
216: if (saxbit instanceof Characters) {
217: ((Characters) saxbit).toString(value);
218: }
219: }
220:
221: return value.toString();
222: }
223:
224: /**
225: * Clear this buffer
226: */
227: public void recycle() {
228: saxbits.clear();
229: }
230:
231: /**
232: * Dump buffer contents into the provided writer.
233: */
234: public void dump(Writer writer) throws IOException {
235: Iterator i = saxbits.iterator();
236: while (i.hasNext()) {
237: final SaxBit saxbit = (SaxBit) i.next();
238: saxbit.dump(writer);
239: }
240: writer.flush();
241: }
242:
243: //
244: // Implementation Methods
245: //
246:
247: /**
248: * Adds a SaxBit to the bits list
249: */
250: protected final void addBit(SaxBit bit) {
251: saxbits.add(bit);
252: }
253:
254: /**
255: * Iterates through the bits list
256: */
257: protected final Iterator bits() {
258: return saxbits.iterator();
259: }
260:
261: /**
262: * SaxBit is a representation of the SAX event. Every SaxBit is immutable object.
263: */
264: interface SaxBit {
265: public void send(ContentHandler contentHandler)
266: throws SAXException;
267:
268: public void dump(Writer writer) throws IOException;
269: }
270:
271: public final static class StartDocument implements SaxBit,
272: Serializable {
273: public static final StartDocument SINGLETON = new StartDocument();
274:
275: public void send(ContentHandler contentHandler)
276: throws SAXException {
277: contentHandler.startDocument();
278: }
279:
280: public void dump(Writer writer) throws IOException {
281: writer.write("[StartDocument]\n");
282: }
283: }
284:
285: public final static class EndDocument implements SaxBit,
286: Serializable {
287: public static final EndDocument SINGLETON = new EndDocument();
288:
289: public void send(ContentHandler contentHandler)
290: throws SAXException {
291: contentHandler.endDocument();
292: }
293:
294: public void dump(Writer writer) throws IOException {
295: writer.write("[EndDocument]\n");
296: }
297: }
298:
299: public final static class PI implements SaxBit, Serializable {
300: public final String target;
301: public final String data;
302:
303: public PI(String target, String data) {
304: this .target = target;
305: this .data = data;
306: }
307:
308: public void send(ContentHandler contentHandler)
309: throws SAXException {
310: contentHandler.processingInstruction(target, data);
311: }
312:
313: public void dump(Writer writer) throws IOException {
314: writer.write("[ProcessingInstruction] target=" + target
315: + ",data=" + data + "\n");
316: }
317: }
318:
319: public final static class StartDTD implements SaxBit, Serializable {
320: public final String name;
321: public final String publicId;
322: public final String systemId;
323:
324: public StartDTD(String name, String publicId, String systemId) {
325: this .name = name;
326: this .publicId = publicId;
327: this .systemId = systemId;
328: }
329:
330: public void send(ContentHandler contentHandler)
331: throws SAXException {
332: if (contentHandler instanceof LexicalHandler)
333: ((LexicalHandler) contentHandler).startDTD(name,
334: publicId, systemId);
335: }
336:
337: public void dump(Writer writer) throws IOException {
338: writer.write("[StartDTD] name=" + name + ",publicId="
339: + publicId + ",systemId=" + systemId + "\n");
340: }
341: }
342:
343: public final static class EndDTD implements SaxBit, Serializable {
344: public static final EndDTD SINGLETON = new EndDTD();
345:
346: public void send(ContentHandler contentHandler)
347: throws SAXException {
348: if (contentHandler instanceof LexicalHandler)
349: ((LexicalHandler) contentHandler).endDTD();
350: }
351:
352: public void dump(Writer writer) throws IOException {
353: writer.write("[EndDTD]\n");
354: }
355: }
356:
357: public final static class StartEntity implements SaxBit,
358: Serializable {
359: public final String name;
360:
361: public StartEntity(String name) {
362: this .name = name;
363: }
364:
365: public void send(ContentHandler contentHandler)
366: throws SAXException {
367: if (contentHandler instanceof LexicalHandler)
368: ((LexicalHandler) contentHandler).startEntity(name);
369: }
370:
371: public void dump(Writer writer) throws IOException {
372: writer.write("[StartEntity] name=" + name + "\n");
373: }
374: }
375:
376: public final static class EndEntity implements SaxBit, Serializable {
377: public final String name;
378:
379: public EndEntity(String name) {
380: this .name = name;
381: }
382:
383: public void send(ContentHandler contentHandler)
384: throws SAXException {
385: if (contentHandler instanceof LexicalHandler)
386: ((LexicalHandler) contentHandler).endEntity(name);
387: }
388:
389: public void dump(Writer writer) throws IOException {
390: writer.write("[EndEntity] name=" + name + "\n");
391: }
392: }
393:
394: public final static class SkippedEntity implements SaxBit,
395: Serializable {
396: public final String name;
397:
398: public SkippedEntity(String name) {
399: this .name = name;
400: }
401:
402: public void send(ContentHandler contentHandler)
403: throws SAXException {
404: contentHandler.skippedEntity(name);
405: }
406:
407: public void dump(Writer writer) throws IOException {
408: writer.write("[SkippedEntity] name=" + name + "\n");
409: }
410: }
411:
412: public final static class StartPrefixMapping implements SaxBit,
413: Serializable {
414: public final String prefix;
415: public final String uri;
416:
417: public StartPrefixMapping(String prefix, String uri) {
418: this .prefix = prefix;
419: this .uri = uri;
420: }
421:
422: public void send(ContentHandler contentHandler)
423: throws SAXException {
424: contentHandler.startPrefixMapping(prefix, uri);
425: }
426:
427: public void dump(Writer writer) throws IOException {
428: writer.write("[StartPrefixMapping] prefix=" + prefix
429: + ",uri=" + uri + "\n");
430: }
431: }
432:
433: public final static class EndPrefixMapping implements SaxBit,
434: Serializable {
435: public final String prefix;
436:
437: public EndPrefixMapping(String prefix) {
438: this .prefix = prefix;
439: }
440:
441: public void send(ContentHandler contentHandler)
442: throws SAXException {
443: contentHandler.endPrefixMapping(prefix);
444: }
445:
446: public void dump(Writer writer) throws IOException {
447: writer.write("[EndPrefixMapping] prefix=" + prefix + "\n");
448: }
449: }
450:
451: public final static class StartElement implements SaxBit,
452: Serializable {
453: public final String namespaceURI;
454: public final String localName;
455: public final String qName;
456: public final Attributes attrs;
457:
458: public StartElement(String namespaceURI, String localName,
459: String qName, Attributes attrs) {
460: this .namespaceURI = namespaceURI;
461: this .localName = localName;
462: this .qName = qName;
463: this .attrs = new org.xml.sax.helpers.AttributesImpl(attrs);
464: }
465:
466: public void send(ContentHandler contentHandler)
467: throws SAXException {
468: contentHandler.startElement(namespaceURI, localName, qName,
469: attrs);
470: }
471:
472: public void dump(Writer writer) throws IOException {
473: writer.write("[StartElement] namespaceURI=" + namespaceURI
474: + ",localName=" + localName + ",qName=" + qName
475: + "\n");
476: for (int i = 0; i < attrs.getLength(); i++) {
477: writer.write(" [Attribute] namespaceURI="
478: + attrs.getURI(i) + ",localName="
479: + attrs.getLocalName(i) + ",qName="
480: + attrs.getQName(i) + ",type="
481: + attrs.getType(i) + ",value="
482: + attrs.getValue(i) + "\n");
483: }
484: }
485: }
486:
487: public final static class EndElement implements SaxBit,
488: Serializable {
489: public final String namespaceURI;
490: public final String localName;
491: public final String qName;
492:
493: public EndElement(String namespaceURI, String localName,
494: String qName) {
495: this .namespaceURI = namespaceURI;
496: this .localName = localName;
497: this .qName = qName;
498: }
499:
500: public void send(ContentHandler contentHandler)
501: throws SAXException {
502: contentHandler.endElement(namespaceURI, localName, qName);
503: }
504:
505: public void dump(Writer writer) throws IOException {
506: writer.write("[EndElement] namespaceURI=" + namespaceURI
507: + ",localName=" + localName + ",qName=" + qName
508: + "\n");
509: }
510: }
511:
512: public final static class Characters implements SaxBit,
513: Serializable {
514: public final char[] ch;
515:
516: public Characters(char[] ch, int start, int length) {
517: // make a copy so that we don't hold references to a potentially large array we don't control
518: this .ch = new char[length];
519: System.arraycopy(ch, start, this .ch, 0, length);
520: }
521:
522: public void send(ContentHandler contentHandler)
523: throws SAXException {
524: contentHandler.characters(ch, 0, ch.length);
525: }
526:
527: public void toString(StringBuffer value) {
528: value.append(ch);
529: }
530:
531: public void dump(Writer writer) throws IOException {
532: writer.write("[Characters] ch=" + new String(ch) + "\n");
533: }
534: }
535:
536: public final static class Comment implements SaxBit, Serializable {
537: public final char[] ch;
538:
539: public Comment(char[] ch, int start, int length) {
540: // make a copy so that we don't hold references to a potentially large array we don't control
541: this .ch = new char[length];
542: System.arraycopy(ch, start, this .ch, 0, length);
543: }
544:
545: public void send(ContentHandler contentHandler)
546: throws SAXException {
547: if (contentHandler instanceof LexicalHandler)
548: ((LexicalHandler) contentHandler).comment(ch, 0,
549: ch.length);
550: }
551:
552: public void dump(Writer writer) throws IOException {
553: writer.write("[Comment] ch=" + new String(ch) + "\n");
554: }
555: }
556:
557: public final static class StartCDATA implements SaxBit,
558: Serializable {
559: public static final StartCDATA SINGLETON = new StartCDATA();
560:
561: public void send(ContentHandler contentHandler)
562: throws SAXException {
563: if (contentHandler instanceof LexicalHandler)
564: ((LexicalHandler) contentHandler).startCDATA();
565: }
566:
567: public void dump(Writer writer) throws IOException {
568: writer.write("[StartCDATA]\n");
569: }
570: }
571:
572: public final static class EndCDATA implements SaxBit, Serializable {
573: public static final EndCDATA SINGLETON = new EndCDATA();
574:
575: public void send(ContentHandler contentHandler)
576: throws SAXException {
577: if (contentHandler instanceof LexicalHandler)
578: ((LexicalHandler) contentHandler).endCDATA();
579: }
580:
581: public void dump(Writer writer) throws IOException {
582: writer.write("[EndCDATA]\n");
583: }
584: }
585:
586: public final static class IgnorableWhitespace implements SaxBit,
587: Serializable {
588: public final char[] ch;
589:
590: public IgnorableWhitespace(char[] ch, int start, int length) {
591: // make a copy so that we don't hold references to a potentially large array we don't control
592: this .ch = new char[length];
593: System.arraycopy(ch, start, this .ch, 0, length);
594: }
595:
596: public void send(ContentHandler contentHandler)
597: throws SAXException {
598: contentHandler.ignorableWhitespace(ch, 0, ch.length);
599: }
600:
601: public void dump(Writer writer) throws IOException {
602: writer.write("[IgnorableWhitespace] ch=" + new String(ch)
603: + "\n");
604: }
605: }
606:
607: public final static class XMLizableBit implements SaxBit,
608: Serializable {
609: public final XMLizable xml;
610:
611: public XMLizableBit(XMLizable xml) {
612: this .xml = xml;
613: }
614:
615: public void send(ContentHandler contentHandler)
616: throws SAXException {
617: this .xml.toSAX(new EmbeddedXMLPipe(contentHandler));
618: }
619:
620: public void dump(Writer writer) throws IOException {
621: if (xml instanceof SaxBuffer) {
622: writer.write("[XMLizable] Begin nested SaxBuffer\n");
623: ((SaxBuffer) xml).dump(writer);
624: writer.write("[XMLizable] End nested SaxBuffer\n");
625: } else {
626: writer.write("[XMLizable] xml=" + xml + "\n");
627: }
628: }
629: }
630: }
|