001: /* Copyright 2001 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.utils;
007:
008: import java.util.Enumeration;
009: import java.util.Vector;
010:
011: import org.jasig.portal.properties.PropertiesManager;
012: import org.xml.sax.Attributes;
013: import org.xml.sax.ContentHandler;
014: import org.xml.sax.DTDHandler;
015: import org.xml.sax.EntityResolver;
016: import org.xml.sax.ErrorHandler;
017: import org.xml.sax.Locator;
018: import org.xml.sax.SAXException;
019: import org.xml.sax.SAXParseException;
020: import org.xml.sax.XMLReader;
021: import org.xml.sax.ext.LexicalHandler;
022:
023: /**
024: * A basic XML buffer implementation.
025: *
026: * @author Peter Kharchenko {@link <a href="mailto:pkharchenko@interactivebusiness.com"">pkharchenko@interactivebusiness.com"</a>}
027: */
028:
029: public class SAX2BufferImpl extends SAX2FilterImpl {
030: // holding vectors
031: protected Vector eventTypes;
032: protected Vector eventArguments;
033:
034: // control flags
035: protected boolean buffering;
036: protected boolean outputAtDocumentEnd;
037:
038: // types of SAX events
039: private static final int ISTARTDOCUMENT = 0;
040: private static final int IENDDOCUMENT = 1;
041: private static final int ISTARTELEMENT = 2;
042: private static final int IENDELEMENT = 3;
043: private static final int ICHARACTERS = 4;
044: private static final int IIGNORABLEWHITESPACE = 5;
045: private static final int IPROCESSINGINSTRUCTION = 6;
046: private static final int INOTATIONDECL = 7;
047: private static final int IUNPARSEDENTITYDECL = 8;
048: private static final int ISTARTPREFIXMAPPING = 9;
049: private static final int IENDPREFIXMAPPING = 10;
050: private static final int ISKIPPEDENTITY = 11;
051: private static final int IWARNING = 12;
052: private static final int IERROR = 13;
053: private static final int IFATALERROR = 14;
054: private static final int ICOMMENT = 15;
055: private static final int ISTARTCDATA = 16;
056: private static final int IENDCDATA = 17;
057: private static final int ISTARTDTD = 18;
058: private static final int IENDDTD = 19;
059: private static final int ISTARTENTITY = 20;
060: private static final int IENDENTITY = 21;
061:
062: public static final Integer STARTDOCUMENT = new Integer(
063: ISTARTDOCUMENT);
064: public static final Integer ENDDOCUMENT = new Integer(IENDDOCUMENT);
065: public static final Integer STARTELEMENT = new Integer(
066: ISTARTELEMENT);
067: public static final Integer ENDELEMENT = new Integer(IENDELEMENT);
068: public static final Integer CHARACTERS = new Integer(ICHARACTERS);
069: public static final Integer IGNORABLEWHITESPACE = new Integer(
070: IIGNORABLEWHITESPACE);
071: public static final Integer PROCESSINGINSTRUCTION = new Integer(
072: IPROCESSINGINSTRUCTION);
073: public static final Integer NOTATIONDECL = new Integer(
074: INOTATIONDECL);
075: public static final Integer UNPARSEDENTITYDECL = new Integer(
076: IUNPARSEDENTITYDECL);
077: public static final Integer STARTPREFIXMAPPING = new Integer(
078: ISTARTPREFIXMAPPING);
079: public static final Integer ENDPREFIXMAPPING = new Integer(
080: IENDPREFIXMAPPING);
081: public static final Integer SKIPPEDENTITY = new Integer(
082: ISKIPPEDENTITY);
083: public static final Integer WARNING = new Integer(IWARNING);
084: public static final Integer ERROR = new Integer(IERROR);
085: public static final Integer FATALERROR = new Integer(IFATALERROR);
086: public static final Integer COMMENT = new Integer(ICOMMENT);
087: public static final Integer STARTCDATA = new Integer(ISTARTCDATA);
088: public static final Integer ENDCDATA = new Integer(IENDCDATA);
089: public static final Integer STARTDTD = new Integer(ISTARTDTD);
090: public static final Integer ENDDTD = new Integer(IENDDTD);
091: public static final Integer STARTENTITY = new Integer(ISTARTENTITY);
092: public static final Integer ENDENTITY = new Integer(IENDENTITY);
093:
094: protected boolean copyCharBlock = PropertiesManager
095: .getPropertyAsBoolean(SAX2BufferImpl.class.getName()
096: + ".copyCharBlock", true);
097:
098: // constructors
099:
100: /**
101: * Construct an empty XML buffer, with no parent.
102: *
103: * <p>This filter will have no parent: you must assign a parent
104: * before you start a parse or do any configuration with
105: * setFeature or setProperty.</p>
106: * <p>By default, buffering is on, outputAtDocumentEnd is off.</p>
107: *
108: * @see org.xml.sax.XMLReader#setFeature
109: * @see org.xml.sax.XMLReader#setProperty
110: */
111: public SAX2BufferImpl() {
112: super ();
113: buffering = true;
114: outputAtDocumentEnd = false;
115: eventTypes = new Vector();
116: eventArguments = new Vector();
117: }
118:
119: /**
120: * Construct an XML filter with the specified parent.
121: * <p> Same default flag settings as with empty constructor</p>
122: * @see #setParent(XMLReader)
123: * @see #getParent()
124: */
125: public SAX2BufferImpl(XMLReader parent) {
126: this ();
127: setParent(parent);
128: }
129:
130: /**
131: * Construct an XML filter with the specified children.
132: * <p> Same default flag settings as with empty constructor</p>
133: * @see #setContentHandler(ContentHandler)
134: * @see #setDTDHandler(DTDHandler)
135: * @see #setErrorHandler(ErrorHandler)
136: * @see #setEntityResolver(EntityResolver)
137: * @see #setLexicalHandler(LexicalHandler)
138: */
139: public SAX2BufferImpl(ContentHandler ch, EntityResolver er,
140: ErrorHandler eh, LexicalHandler lh, DTDHandler dh) {
141: super (ch, er, eh, lh, dh);
142: buffering = true;
143: outputAtDocumentEnd = false;
144: eventTypes = new Vector();
145: eventArguments = new Vector();
146: }
147:
148: /**
149: * Construct an XML filter based on a unified handler.
150: * <p>
151: * Same default flag settings as with empty constructor.
152: */
153: public SAX2BufferImpl(ContentHandler ch) {
154: super (ch);
155: buffering = true;
156: outputAtDocumentEnd = false;
157: eventTypes = new Vector();
158: eventArguments = new Vector();
159: }
160:
161: /**
162: * Tells buffer to automatically output itself once
163: * an end of a document is reached.
164: * @param setting a <code>boolean</code> value
165: */
166: public void setOutputAtDocumentEnd(boolean setting) {
167: this .outputAtDocumentEnd = setting;
168: }
169:
170: public synchronized void clearBuffer() {
171: // clean out the vectors
172: eventTypes.clear();
173: eventArguments.clear();
174: }
175:
176: public synchronized void stopBuffering() throws SAXException {
177: buffering = false;
178: }
179:
180: public synchronized void startBuffering() {
181: buffering = true;
182: }
183:
184: public synchronized boolean isEmpty() {
185: return eventTypes.isEmpty();
186: }
187:
188: /**
189: * Outputs buffer's content to a current set of handlers.
190: * Please note that this method is not thread-safe if
191: * handlers are being reset by different threads.
192: * Use the other outputBuffer(ContentHandler) method in such cases.
193: *
194: * @exception SAXException if an error occurs
195: */
196: public void outputBuffer() throws SAXException {
197: // for speed purposes, we don't allow contentHandler to be null
198: if (contentHandler != null) {
199: outputBuffer(contentHandler, dtdHandler, errorHandler,
200: lexicalHandler);
201: } else {
202: // Logger.log (Logger.ERROR, "SAX2BufferImpl:stopBuffering() : trying to ouput buffer to a null ContentHandler.");
203: }
204: }
205:
206: /**
207: * Outputs buffer's content to a specified content handler.
208: * Please note that the content handler can also represent
209: * lexical handler, dtd handler and error handler.
210: * This method is thread-safe.
211: *
212: * @param ch a <code>ContenteHandler</code> value
213: * @exception SAXException if an error occurs
214: */
215: public void outputBuffer(ContentHandler ch) throws SAXException {
216: // unqueue all of the buffered events
217:
218: // for speed purposes, we don't allow contentHandler to be null
219: if (ch != null) {
220: // determine what a given content handler represents
221: DTDHandler dtdh = null;
222: if (ch instanceof DTDHandler) {
223: dtdh = (DTDHandler) ch;
224: }
225:
226: ErrorHandler erh = null;
227: if (ch instanceof ErrorHandler) {
228: erh = (ErrorHandler) ch;
229: }
230:
231: LexicalHandler lh = null;
232: if (ch instanceof LexicalHandler) {
233: lh = (LexicalHandler) ch;
234: }
235:
236: outputBuffer(ch, dtdh, erh, lh);
237: } else {
238: // Logger.log (Logger.ERROR, "SAX2BufferImpl:stopBuffering() : trying to ouput buffer to a null ContentHandler.");
239: }
240: }
241:
242: private void outputBuffer(final ContentHandler ch,
243: final DTDHandler dtdh, final ErrorHandler erh,
244: final LexicalHandler lh) throws SAXException {
245:
246: final Enumeration args = eventArguments.elements();
247:
248: for (final Enumeration types = eventTypes.elements(); types
249: .hasMoreElements();) {
250: final int type = ((Integer) types.nextElement()).intValue();
251:
252: // ContentHandler events
253: if (ISTARTELEMENT == type) {
254: final StartElementData sed = (StartElementData) args
255: .nextElement();
256: ch.startElement(sed.getURI(), sed.getLocalName(), sed
257: .getQName(), sed.getAtts());
258: } else if (IENDELEMENT == type) {
259: final ThreeString ths = (ThreeString) args
260: .nextElement();
261: ch.endElement(ths.first, ths.second, ths.third);
262: } else if (ISTARTPREFIXMAPPING == type) {
263: final TwoString ts = (TwoString) args.nextElement();
264: ch.startPrefixMapping(ts.first, ts.second);
265: } else if (ICHARACTERS == type) {
266: final CharBlock cd = (CharBlock) args.nextElement();
267: ch
268: .characters(cd.getCh(), cd.getStart(), cd
269: .getLength());
270: } else if (ISTARTDOCUMENT == type) {
271: ch.startDocument();
272: } else if (IENDDOCUMENT == type) {
273: ch.endDocument();
274:
275: } else if (IENDPREFIXMAPPING == type) {
276: ch.endPrefixMapping((String) args.nextElement());
277: } else if (IIGNORABLEWHITESPACE == type) {
278: final CharBlock cd = (CharBlock) args.nextElement();
279: ch.ignorableWhitespace(cd.getCh(), cd.getStart(), cd
280: .getLength());
281: } else if (IPROCESSINGINSTRUCTION == type) {
282: final TwoString ts = (TwoString) args.nextElement();
283: ch.processingInstruction(ts.first, ts.second);
284: } else {
285: switch (type) {
286: case INOTATIONDECL:
287: case IUNPARSEDENTITYDECL:
288: // DTDHandler events
289: if (dtdh != null) {
290: if (INOTATIONDECL == type) {
291: final ThreeString ths = (ThreeString) args
292: .nextElement();
293: dtdh.notationDecl(ths.first, ths.second,
294: ths.third);
295: } else if (IUNPARSEDENTITYDECL == type) {
296: final FourString fs = (FourString) args
297: .nextElement();
298: dtdh.unparsedEntityDecl(fs.first,
299: fs.second, fs.third, fs.fourth);
300: }
301: }
302: break;
303:
304: case IWARNING:
305: case IERROR:
306: case IFATALERROR:
307: // ErrorHandler events
308: if (erh != null) {
309: final SAXParseException e = (SAXParseException) args
310: .nextElement();
311: ;
312: if (IWARNING == type) {
313: erh.warning(e);
314: } else if (IERROR == type) {
315: erh.error(e);
316: } else if (IFATALERROR == type) {
317: erh.fatalError(e);
318: }
319: }
320: break;
321:
322: case ISTARTDTD:
323: case IENDDTD:
324: case ISTARTENTITY:
325: case IENDENTITY:
326: case ISTARTCDATA:
327: case IENDCDATA:
328: case ICOMMENT:
329: // LexicalHandler events
330: if (lh != null) {
331: if (ISTARTDTD == type) {
332: final ThreeString ths = (ThreeString) args
333: .nextElement();
334: lh.startDTD(ths.first, ths.second,
335: ths.third);
336: } else if (IENDDTD == type) {
337: lh.endDTD();
338: } else if (ISTARTENTITY == type) {
339: final String n = (String) args
340: .nextElement();
341: lh.startEntity(n);
342: } else if (IENDENTITY == type) {
343: final String n = (String) args
344: .nextElement();
345: lh.endEntity(n);
346: } else if (ISTARTCDATA == type) {
347: lh.startCDATA();
348: } else if (IENDCDATA == type) {
349: lh.endCDATA();
350: } else if (ICOMMENT == type) {
351: final CharBlock ccd = (CharBlock) args
352: .nextElement();
353: lh.comment(ccd.getCh(), ccd.getStart(), ccd
354: .getLength());
355: }
356: }
357: break;
358:
359: default:
360: throw new RuntimeException(
361: "org.jasig.portal.utils.SAX2BufferImpl::outputBuffer: unexpected element type "
362: + type
363: + " encountered in SAX stream");
364: }
365: }
366: }
367: }
368:
369: // Implementation of org.xml.sax.ext.LexicalHandler
370:
371: public void startDTD(String name, String publicId, String systemId)
372: throws SAXException {
373: if (buffering) {
374: eventTypes.add(STARTDTD);
375: eventArguments
376: .add(new ThreeString(name, publicId, systemId));
377: } else if (lexicalHandler != null) {
378: lexicalHandler.startDTD(name, publicId, systemId);
379: }
380: }
381:
382: public void endDTD() throws SAXException {
383: if (buffering) {
384: eventTypes.add(ENDDTD);
385: } else if (lexicalHandler != null) {
386: lexicalHandler.endDTD();
387: }
388: }
389:
390: public void startEntity(String name) throws SAXException {
391: if (buffering) {
392: eventTypes.add(STARTENTITY);
393: eventArguments.add(name);
394: } else if (lexicalHandler != null) {
395: lexicalHandler.startEntity(name);
396: }
397: }
398:
399: public void endEntity(String name) throws SAXException {
400: if (buffering) {
401: eventTypes.add(ENDENTITY);
402: eventArguments.add(name);
403: } else if (lexicalHandler != null) {
404: lexicalHandler.endEntity(name);
405: }
406: }
407:
408: public void startCDATA() throws SAXException {
409: if (buffering) {
410: eventTypes.add(STARTCDATA);
411: } else if (lexicalHandler != null) {
412: lexicalHandler.startCDATA();
413: }
414: }
415:
416: public void endCDATA() throws SAXException {
417: if (buffering) {
418: eventTypes.add(ENDCDATA);
419: } else if (lexicalHandler != null) {
420: lexicalHandler.endCDATA();
421: }
422: }
423:
424: public void comment(char ch[], int start, int length)
425: throws SAXException {
426: if (buffering) {
427: eventTypes.add(COMMENT);
428: eventArguments.add(new CharBlock(ch, start, length));
429: } else if (lexicalHandler != null) {
430: lexicalHandler.comment(ch, start, length);
431: }
432: }
433:
434: // Implementation of org.xml.sax.DTDHandler
435:
436: public void notationDecl(String name, String publicId,
437: String systemId) throws SAXException {
438: if (buffering) {
439: eventTypes.add(NOTATIONDECL);
440: eventArguments
441: .add(new ThreeString(name, publicId, systemId));
442: } else if (dtdHandler != null) {
443: dtdHandler.notationDecl(name, publicId, systemId);
444: }
445: }
446:
447: public void unparsedEntityDecl(String name, String publicId,
448: String systemId, String notationName) throws SAXException {
449: if (buffering) {
450: eventTypes.add(UNPARSEDENTITYDECL);
451: eventArguments.add(new FourString(name, publicId, systemId,
452: notationName));
453: } else if (dtdHandler != null) {
454: dtdHandler.unparsedEntityDecl(name, publicId, systemId,
455: notationName);
456: }
457: }
458:
459: // Implementation of org.xml.sax.ContentHandler.
460:
461: public void setDocumentLocator(Locator locator) {
462: this .locator = locator;
463: if (!buffering) {
464: if (contentHandler != null) {
465: contentHandler.setDocumentLocator(locator);
466: }
467: }
468: }
469:
470: public void startDocument() throws SAXException {
471: if (buffering) {
472: eventTypes.add(STARTDOCUMENT);
473: } else if (contentHandler != null) {
474: contentHandler.startDocument();
475: }
476: }
477:
478: public void endDocument() throws SAXException {
479: if (buffering) {
480: eventTypes.add(ENDDOCUMENT);
481: if (outputAtDocumentEnd) {
482: this .stopBuffering();
483: this .outputBuffer();
484: }
485: } else if (contentHandler != null) {
486: contentHandler.endDocument();
487: }
488: }
489:
490: public void startPrefixMapping(String prefix, String uri)
491: throws SAXException {
492: if (buffering) {
493: eventTypes.add(STARTPREFIXMAPPING);
494: eventArguments.add(new TwoString(prefix, uri));
495: } else if (contentHandler != null) {
496: contentHandler.startPrefixMapping(prefix, uri);
497: }
498: }
499:
500: public void endPrefixMapping(String prefix) throws SAXException {
501: if (buffering) {
502: eventTypes.add(ENDPREFIXMAPPING);
503: eventArguments.add(prefix);
504: } else if (contentHandler != null) {
505: contentHandler.endPrefixMapping(prefix);
506: }
507: }
508:
509: public void startElement(String uri, String localName,
510: String qName, Attributes atts) throws SAXException {
511: if (buffering) {
512: eventTypes.add(STARTELEMENT);
513: eventArguments.add(new StartElementData(uri, localName,
514: qName, atts));
515: } else if (contentHandler != null) {
516: contentHandler.startElement(uri, localName, qName, atts);
517: }
518: }
519:
520: public void endElement(String uri, String localName, String qName)
521: throws SAXException {
522: if (buffering) {
523: eventTypes.add(ENDELEMENT);
524: eventArguments.add(new ThreeString(uri, localName, qName));
525: } else if (contentHandler != null) {
526: contentHandler.endElement(uri, localName, qName);
527: }
528: }
529:
530: public void characters(char ch[], int start, int length)
531: throws SAXException {
532: if (buffering) {
533: eventTypes.add(CHARACTERS);
534: eventArguments.add(new CharBlock(ch, start, length));
535: } else if (contentHandler != null) {
536: contentHandler.characters(ch, start, length);
537: }
538: }
539:
540: public void ignorableWhitespace(char ch[], int start, int length)
541: throws SAXException {
542: if (buffering) {
543: eventTypes.add(IGNORABLEWHITESPACE);
544: eventArguments.add(new CharBlock(ch, start, length));
545: } else if (contentHandler != null) {
546: contentHandler.ignorableWhitespace(ch, start, length);
547: }
548: }
549:
550: public void processingInstruction(String target, String data)
551: throws SAXException {
552: if (buffering) {
553: eventTypes.add(PROCESSINGINSTRUCTION);
554: eventArguments.add(new TwoString(target, data));
555: } else if (contentHandler != null) {
556: contentHandler.processingInstruction(target, data);
557: }
558: }
559:
560: public void skippedEntity(String name) throws SAXException {
561: if (buffering) {
562: eventTypes.add(SKIPPEDENTITY);
563: eventArguments.add(name);
564: } else if (contentHandler != null) {
565: contentHandler.skippedEntity(name);
566: }
567: }
568:
569: // Implementation of org.xml.sax.ErrorHandler.
570:
571: public void warning(SAXParseException e) throws SAXException {
572: if (buffering) {
573: eventTypes.add(WARNING);
574: eventArguments.add(e);
575: } else if (errorHandler != null) {
576: errorHandler.warning(e);
577: }
578: }
579:
580: public void error(SAXParseException e) throws SAXException {
581: if (buffering) {
582: eventTypes.add(ERROR);
583: eventArguments.add(e);
584: } else if (errorHandler != null) {
585: errorHandler.error(e);
586: }
587: }
588:
589: public void fatalError(SAXParseException e) throws SAXException {
590: if (buffering) {
591: eventTypes.add(FATALERROR);
592: eventArguments.add(e);
593: } else if (errorHandler != null) {
594: errorHandler.fatalError(e);
595: }
596: }
597:
598: // supporting utility classes
599:
600: private class TwoString {
601: public String first;
602: public String second;
603:
604: TwoString(String first, String second) {
605: this .first = first;
606: this .second = second;
607: }
608:
609: public String getFirst() {
610: return this .first;
611: }
612:
613: public String getSecond() {
614: return this .second;
615: }
616: }
617:
618: private class ThreeString {
619: public String first;
620: public String second;
621: public String third;
622:
623: ThreeString(String first, String second, String third) {
624: this .first = first;
625: this .second = second;
626: this .third = third;
627: }
628:
629: public String getFirst() {
630: return this .first;
631: }
632:
633: public String getSecond() {
634: return this .second;
635: }
636:
637: public String getThird() {
638: return this .third;
639: }
640: }
641:
642: private class FourString {
643: public String first;
644: public String second;
645: public String third;
646: public String fourth;
647:
648: FourString(String first, String second, String third,
649: String fourth) {
650: this .first = first;
651: this .second = second;
652: this .third = third;
653: this .fourth = fourth;
654: }
655:
656: public String getFirst() {
657: return this .first;
658: }
659:
660: public String getSecond() {
661: return this .second;
662: }
663:
664: public String getThird() {
665: return this .third;
666: }
667:
668: public String getFourth() {
669: return this .fourth;
670: }
671: }
672:
673: private class CharBlock {
674: public char[] ca_ch;
675: public int i_start;
676: public int i_length;
677:
678: CharBlock(char ch[], int start, int length) {
679: if (copyCharBlock) {
680: this .ca_ch = new char[length + start];
681: System.arraycopy(ch, 0, this .ca_ch, 0, length + start);
682: } else {
683: this .ca_ch = ch;
684: }
685: this .i_start = start;
686: this .i_length = length;
687: }
688:
689: public char[] getCh() {
690: return ca_ch;
691: }
692:
693: public int getStart() {
694: return i_start;
695: }
696:
697: public int getLength() {
698: return i_length;
699: }
700: }
701:
702: private class StartElementData extends ThreeString {
703: public org.xml.sax.helpers.AttributesImpl ai;
704:
705: StartElementData(String uri, String localName, String qName,
706: Attributes atts) {
707: super (uri, localName, qName);
708: ai = new org.xml.sax.helpers.AttributesImpl(atts);
709: }
710:
711: public String getURI() {
712: return super .getFirst();
713: }
714:
715: public String getLocalName() {
716: return super .getSecond();
717: }
718:
719: public String getQName() {
720: return super .getThird();
721: }
722:
723: public Attributes getAtts() {
724: return ai;
725: }
726: }
727: }
|