001: /*
002: * The contents of this file are subject to the terms
003: * of the Common Development and Distribution License
004: * (the "License"). You may not use this file except
005: * in compliance with the License.
006: *
007: * You can obtain a copy of the license at
008: * https://jwsdp.dev.java.net/CDDLv1.0.html
009: * See the License for the specific language governing
010: * permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL
013: * HEADER in each file and include the License file at
014: * https://jwsdp.dev.java.net/CDDLv1.0.html If applicable,
015: * add the following below this CDDL HEADER, with the
016: * fields enclosed by brackets "[]" replaced with your
017: * own identifying information: Portions Copyright [yyyy]
018: * [name of copyright owner]
019: */
020: package com.sun.xml.stream.buffer.sax;
021:
022: import com.sun.xml.stream.buffer.AbstractProcessor;
023: import com.sun.xml.stream.buffer.AttributesHolder;
024: import com.sun.xml.stream.buffer.XMLStreamBuffer;
025: import org.xml.sax.ContentHandler;
026: import org.xml.sax.DTDHandler;
027: import org.xml.sax.EntityResolver;
028: import org.xml.sax.ErrorHandler;
029: import org.xml.sax.InputSource;
030: import org.xml.sax.SAXException;
031: import org.xml.sax.SAXNotRecognizedException;
032: import org.xml.sax.SAXNotSupportedException;
033: import org.xml.sax.SAXParseException;
034: import org.xml.sax.XMLReader;
035: import org.xml.sax.ext.LexicalHandler;
036: import org.xml.sax.helpers.LocatorImpl;
037:
038: import javax.xml.XMLConstants;
039: import java.io.IOException;
040:
041: /**
042: * A processor of a {@link XMLStreamBuffer} that that reads the XML infoset as
043: * {@link XMLReader}.
044: */
045: public class SAXBufferProcessor extends AbstractProcessor implements
046: XMLReader {
047: /**
048: * Reference to entity resolver.
049: */
050: protected EntityResolver _entityResolver = DEFAULT_LEXICAL_HANDLER;
051:
052: /**
053: * Reference to dtd handler.
054: */
055: protected DTDHandler _dtdHandler = DEFAULT_LEXICAL_HANDLER;
056:
057: /**
058: * Reference to content handler.
059: */
060: protected ContentHandler _contentHandler = DEFAULT_LEXICAL_HANDLER;
061:
062: /**
063: * Reference to error handler.
064: */
065: protected ErrorHandler _errorHandler = DEFAULT_LEXICAL_HANDLER;
066:
067: /**
068: * Reference to lexical handler.
069: */
070: protected LexicalHandler _lexicalHandler = DEFAULT_LEXICAL_HANDLER;
071:
072: /**
073: * SAX Namespace attributes features
074: */
075: protected boolean _namespacePrefixesFeature = false;
076:
077: protected AttributesHolder _attributes = new AttributesHolder();
078:
079: protected String[] _namespacePrefixes = new String[16];
080: protected int _namespacePrefixesIndex;
081:
082: protected int[] _namespaceAttributesStack = new int[16];
083: protected int _namespaceAttributesStackIndex;
084:
085: public SAXBufferProcessor() {
086: }
087:
088: /**
089: * @deprecated
090: * Use {@link #SAXBufferProcessor(XMLStreamBuffer, boolean)}
091: */
092: public SAXBufferProcessor(XMLStreamBuffer buffer) {
093: setXMLStreamBuffer(buffer);
094: }
095:
096: /**
097: * @param produceFragmentEvent
098: * True to generate fragment SAX events without start/endDocument.
099: * False to generate a full document SAX events.
100: */
101: public SAXBufferProcessor(XMLStreamBuffer buffer,
102: boolean produceFragmentEvent) {
103: setXMLStreamBuffer(buffer, produceFragmentEvent);
104: }
105:
106: public boolean getFeature(String name)
107: throws SAXNotRecognizedException, SAXNotSupportedException {
108: if (name.equals(Features.NAMESPACES_FEATURE)) {
109: return true;
110: } else if (name.equals(Features.NAMESPACE_PREFIXES_FEATURE)) {
111: return _namespacePrefixesFeature;
112: } else if (name.equals(Features.EXTERNAL_GENERAL_ENTITIES)) {
113: return true;
114: } else if (name.equals(Features.EXTERNAL_PARAMETER_ENTITIES)) {
115: return true;
116: } else if (name.equals(Features.STRING_INTERNING_FEATURE)) {
117: return _stringInterningFeature;
118: } else {
119: throw new SAXNotRecognizedException(
120: "Feature not supported: " + name);
121: }
122: }
123:
124: public void setFeature(String name, boolean value)
125: throws SAXNotRecognizedException, SAXNotSupportedException {
126: if (name.equals(Features.NAMESPACES_FEATURE)) {
127: if (!value) {
128: throw new SAXNotSupportedException(name + ":" + value);
129: }
130: } else if (name.equals(Features.NAMESPACE_PREFIXES_FEATURE)) {
131: _namespacePrefixesFeature = value;
132: } else if (name.equals(Features.EXTERNAL_GENERAL_ENTITIES)) {
133: // ignore
134: } else if (name.equals(Features.EXTERNAL_PARAMETER_ENTITIES)) {
135: // ignore
136: } else if (name.equals(Features.STRING_INTERNING_FEATURE)) {
137: if (value != _stringInterningFeature) {
138: throw new SAXNotSupportedException(name + ":" + value);
139: }
140: } else {
141: throw new SAXNotRecognizedException(
142: "Feature not supported: " + name);
143: }
144: }
145:
146: public Object getProperty(String name)
147: throws SAXNotRecognizedException, SAXNotSupportedException {
148: if (name.equals(Properties.LEXICAL_HANDLER_PROPERTY)) {
149: return getLexicalHandler();
150: } else {
151: throw new SAXNotRecognizedException(
152: "Property not recognized: " + name);
153: }
154: }
155:
156: public void setProperty(String name, Object value)
157: throws SAXNotRecognizedException, SAXNotSupportedException {
158: if (name.equals(Properties.LEXICAL_HANDLER_PROPERTY)) {
159: if (value instanceof LexicalHandler) {
160: setLexicalHandler((LexicalHandler) value);
161: } else {
162: throw new SAXNotSupportedException(
163: Properties.LEXICAL_HANDLER_PROPERTY);
164: }
165: } else {
166: throw new SAXNotRecognizedException(
167: "Property not recognized: " + name);
168: }
169: }
170:
171: public void setEntityResolver(EntityResolver resolver) {
172: _entityResolver = resolver;
173: }
174:
175: public EntityResolver getEntityResolver() {
176: return _entityResolver;
177: }
178:
179: public void setDTDHandler(DTDHandler handler) {
180: _dtdHandler = handler;
181: }
182:
183: public DTDHandler getDTDHandler() {
184: return _dtdHandler;
185: }
186:
187: public void setContentHandler(ContentHandler handler) {
188: _contentHandler = handler;
189: }
190:
191: public ContentHandler getContentHandler() {
192: return _contentHandler;
193: }
194:
195: public void setErrorHandler(ErrorHandler handler) {
196: _errorHandler = handler;
197: }
198:
199: public ErrorHandler getErrorHandler() {
200: return _errorHandler;
201: }
202:
203: public void setLexicalHandler(LexicalHandler handler) {
204: _lexicalHandler = handler;
205: }
206:
207: public LexicalHandler getLexicalHandler() {
208: return _lexicalHandler;
209: }
210:
211: public void parse(InputSource input) throws IOException,
212: SAXException {
213: // InputSource is ignored
214: process();
215: }
216:
217: public void parse(String systemId) throws IOException, SAXException {
218: // systemId is ignored
219: process();
220: }
221:
222: /**
223: * Short-hand for {@link #setXMLStreamBuffer(XMLStreamBuffer)} then {@link #process()}.
224: *
225: * @deprecated
226: * Use {@link #process(XMLStreamBuffer, boolean)}
227: */
228: public final void process(XMLStreamBuffer buffer)
229: throws SAXException {
230: setXMLStreamBuffer(buffer);
231: process();
232: }
233:
234: /**
235: * Short-hand for {@link #setXMLStreamBuffer(XMLStreamBuffer,boolean)} then {@link #process()}.
236: *
237: * @param produceFragmentEvent
238: * True to generate fragment SAX events without start/endDocument.
239: * False to generate a full document SAX events.
240: */
241: public final void process(XMLStreamBuffer buffer,
242: boolean produceFragmentEvent) throws SAXException {
243: setXMLStreamBuffer(buffer);
244: process();
245: }
246:
247: /**
248: * Resets the parser to read from the beginning of the given {@link XMLStreamBuffer}.
249: *
250: * @deprecated
251: * Use {@link #setXMLStreamBuffer(XMLStreamBuffer, boolean)}.
252: */
253: public void setXMLStreamBuffer(XMLStreamBuffer buffer) {
254: setBuffer(buffer);
255: }
256:
257: /**
258: * Resets the parser to read from the beginning of the given {@link XMLStreamBuffer}.
259: *
260: * @param produceFragmentEvent
261: * True to generate fragment SAX events without start/endDocument.
262: * False to generate a full document SAX events.
263: */
264: public void setXMLStreamBuffer(XMLStreamBuffer buffer,
265: boolean produceFragmentEvent) {
266: if (!produceFragmentEvent && _treeCount > 1)
267: throw new IllegalStateException(
268: "Can't write a forest to a full XML infoset");
269: setBuffer(buffer, produceFragmentEvent);
270: }
271:
272: /**
273: * Parse the sub-tree (or a whole document) that {@link XMLStreamBuffer}
274: * points to, and sends events to handlers.
275: *
276: * <p>
277: * TODO:
278: * We probably need two modes for a sub-tree event generation. One for
279: * firing a sub-tree as if it's a whole document (in which case start/endDocument
280: * and appropriate additional namespace bindings are necessary), and the other
281: * mode for firing a subtree as a subtree, like it does today.
282: * A stream buffer SAX feature could be used to specify this.
283: *
284: * @throws SAXException
285: * Follow the same semantics as {@link XMLReader#parse(InputSource)}.
286: */
287: public final void process() throws SAXException {
288: if (!_fragmentMode) {
289: LocatorImpl nullLocator = new LocatorImpl();
290: nullLocator.setSystemId(_buffer.getSystemId());
291: nullLocator.setLineNumber(-1);
292: nullLocator.setColumnNumber(-1);
293: _contentHandler.setDocumentLocator(nullLocator);
294:
295: _contentHandler.startDocument();
296: // TODO: if we are writing a fragment stream buffer as a full XML document,
297: // we need to declare in-scope namespaces as if they are on the root element.
298: }
299:
300: while (_treeCount > 0) {
301: final int item = readEiiState();
302: switch (item) {
303: case STATE_DOCUMENT:
304: processDocument();
305: _treeCount--;
306: break;
307: case STATE_END:
308: // Empty buffer
309: return;
310: case STATE_ELEMENT_U_LN_QN:
311: processElement(readStructureString(),
312: readStructureString(), readStructureString());
313: _treeCount--;
314: break;
315: case STATE_ELEMENT_P_U_LN: {
316: final String prefix = readStructureString();
317: final String uri = readStructureString();
318: final String localName = readStructureString();
319: processElement(uri, localName, getQName(prefix,
320: localName));
321: _treeCount--;
322: break;
323: }
324: case STATE_ELEMENT_U_LN: {
325: final String uri = readStructureString();
326: final String localName = readStructureString();
327: processElement(uri, localName, localName);
328: _treeCount--;
329: break;
330: }
331: case STATE_ELEMENT_LN: {
332: final String localName = readStructureString();
333: processElement("", localName, localName);
334: _treeCount--;
335: break;
336: }
337: case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
338: processCommentAsCharArraySmall();
339: break;
340: case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
341: processCommentAsCharArrayMedium();
342: break;
343: case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
344: processCommentAsCharArrayCopy();
345: break;
346: case STATE_COMMENT_AS_STRING:
347: processComment(readContentString());
348: break;
349: case STATE_PROCESSING_INSTRUCTION:
350: processProcessingInstruction(readStructureString(),
351: readStructureString());
352: break;
353: default:
354: throw reportFatalError("Illegal state for DIIs: "
355: + item);
356: }
357: }
358:
359: if (!_fragmentMode)
360: _contentHandler.endDocument();
361: }
362:
363: private void processCommentAsCharArraySmall() throws SAXException {
364: final int length = readStructure();
365: final int start = readContentCharactersBuffer(length);
366: processComment(_contentCharactersBuffer, start, length);
367: }
368:
369: /**
370: * Report a fatal error and abort.
371: *
372: * This is necessary to follow the SAX semantics of error handling.
373: */
374: private SAXParseException reportFatalError(String msg)
375: throws SAXException {
376: SAXParseException spe = new SAXParseException(msg, null);
377: if (_errorHandler != null)
378: _errorHandler.fatalError(spe);
379: return spe;
380: }
381:
382: private void processDocument() throws SAXException {
383: while (true) {
384: int item = readEiiState();
385: switch (item) {
386: case STATE_ELEMENT_U_LN_QN:
387: processElement(readStructureString(),
388: readStructureString(), readStructureString());
389: break;
390: case STATE_ELEMENT_P_U_LN: {
391: final String prefix = readStructureString();
392: final String uri = readStructureString();
393: final String localName = readStructureString();
394: processElement(uri, localName, getQName(prefix,
395: localName));
396: break;
397: }
398: case STATE_ELEMENT_U_LN: {
399: final String uri = readStructureString();
400: final String localName = readStructureString();
401: processElement(uri, localName, localName);
402: break;
403: }
404: case STATE_ELEMENT_LN: {
405: final String localName = readStructureString();
406: processElement("", localName, localName);
407: break;
408: }
409: case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
410: processCommentAsCharArraySmall();
411: break;
412: case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
413: processCommentAsCharArrayMedium();
414: break;
415: case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
416: processCommentAsCharArrayCopy();
417: break;
418: case STATE_COMMENT_AS_STRING:
419: processComment(readContentString());
420: break;
421: case STATE_PROCESSING_INSTRUCTION:
422: processProcessingInstruction(readStructureString(),
423: readStructureString());
424: break;
425: case STATE_END:
426: return;
427: default:
428: throw reportFatalError("Illegal state for child of DII: "
429: + item);
430: }
431: }
432: }
433:
434: protected void processElement(String uri, String localName,
435: String qName) throws SAXException {
436: boolean hasAttributes = false;
437: boolean hasNamespaceAttributes = false;
438: int item = peekStructure();
439: if ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) {
440: hasNamespaceAttributes = true;
441: item = processNamespaceAttributes(item);
442: }
443: if ((item & TYPE_MASK) == T_ATTRIBUTE) {
444: hasAttributes = true;
445: processAttributes(item);
446: }
447:
448: _contentHandler
449: .startElement(uri, localName, qName, _attributes);
450:
451: if (hasAttributes) {
452: _attributes.clear();
453: }
454:
455: do {
456: item = readEiiState();
457: switch (item) {
458: case STATE_ELEMENT_U_LN_QN:
459: processElement(readStructureString(),
460: readStructureString(), readStructureString());
461: break;
462: case STATE_ELEMENT_P_U_LN: {
463: final String p = readStructureString();
464: final String u = readStructureString();
465: final String ln = readStructureString();
466: processElement(u, ln, getQName(p, ln));
467: break;
468: }
469: case STATE_ELEMENT_U_LN: {
470: final String u = readStructureString();
471: final String ln = readStructureString();
472: processElement(u, ln, ln);
473: break;
474: }
475: case STATE_ELEMENT_LN: {
476: final String ln = readStructureString();
477: processElement("", ln, ln);
478: break;
479: }
480: case STATE_TEXT_AS_CHAR_ARRAY_SMALL: {
481: final int length = readStructure();
482: int start = readContentCharactersBuffer(length);
483: _contentHandler.characters(_contentCharactersBuffer,
484: start, length);
485: break;
486: }
487: case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM: {
488: final int length = readStructure16();
489: int start = readContentCharactersBuffer(length);
490: _contentHandler.characters(_contentCharactersBuffer,
491: start, length);
492: break;
493: }
494: case STATE_TEXT_AS_CHAR_ARRAY_COPY: {
495: final char[] ch = readContentCharactersCopy();
496:
497: _contentHandler.characters(ch, 0, ch.length);
498: break;
499: }
500: case STATE_TEXT_AS_STRING: {
501: final String s = readContentString();
502: _contentHandler.characters(s.toCharArray(), 0, s
503: .length());
504: break;
505: }
506: case STATE_TEXT_AS_OBJECT: {
507: final CharSequence c = (CharSequence) readContentObject();
508: final String s = c.toString();
509: _contentHandler.characters(s.toCharArray(), 0, s
510: .length());
511: break;
512: }
513: case STATE_COMMENT_AS_CHAR_ARRAY_SMALL:
514: processCommentAsCharArraySmall();
515: break;
516: case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM:
517: processCommentAsCharArrayMedium();
518: break;
519: case STATE_COMMENT_AS_CHAR_ARRAY_COPY:
520: processCommentAsCharArrayCopy();
521: break;
522: case T_COMMENT_AS_STRING:
523: processComment(readContentString());
524: break;
525: case STATE_PROCESSING_INSTRUCTION:
526: processProcessingInstruction(readStructureString(),
527: readStructureString());
528: break;
529: case STATE_END:
530: break;
531: default:
532: throw reportFatalError("Illegal state for child of EII: "
533: + item);
534: }
535: } while (item != STATE_END);
536:
537: _contentHandler.endElement(uri, localName, qName);
538:
539: if (hasNamespaceAttributes) {
540: processEndPrefixMapping();
541: }
542: }
543:
544: private void processCommentAsCharArrayCopy() throws SAXException {
545: final char[] ch = readContentCharactersCopy();
546: processComment(ch, 0, ch.length);
547: }
548:
549: private void processCommentAsCharArrayMedium() throws SAXException {
550: final int length = readStructure16();
551: final int start = readContentCharactersBuffer(length);
552: processComment(_contentCharactersBuffer, start, length);
553: }
554:
555: private void processEndPrefixMapping() throws SAXException {
556: final int end = _namespaceAttributesStack[--_namespaceAttributesStackIndex];
557: final int start = (_namespaceAttributesStackIndex > 0) ? _namespaceAttributesStack[_namespaceAttributesStackIndex]
558: : 0;
559:
560: for (int i = end - 1; i >= start; i--) {
561: _contentHandler.endPrefixMapping(_namespacePrefixes[i]);
562: }
563: _namespacePrefixesIndex = start;
564: }
565:
566: private int processNamespaceAttributes(int item)
567: throws SAXException {
568: do {
569: switch (_niiStateTable[item]) {
570: case STATE_NAMESPACE_ATTRIBUTE:
571: // Undeclaration of default namespace
572: processNamespaceAttribute("", "");
573: break;
574: case STATE_NAMESPACE_ATTRIBUTE_P:
575: // Undeclaration of namespace
576: processNamespaceAttribute(readStructureString(), "");
577: break;
578: case STATE_NAMESPACE_ATTRIBUTE_P_U:
579: // Declaration with prefix
580: processNamespaceAttribute(readStructureString(),
581: readStructureString());
582: break;
583: case STATE_NAMESPACE_ATTRIBUTE_U:
584: // Default declaration
585: processNamespaceAttribute("", readStructureString());
586: break;
587: default:
588: throw reportFatalError("Illegal state: " + item);
589: }
590: readStructure();
591:
592: item = peekStructure();
593: } while ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE);
594:
595: cacheNamespacePrefixIndex();
596:
597: return item;
598: }
599:
600: private void processAttributes(int item) throws SAXException {
601: do {
602: switch (_aiiStateTable[item]) {
603: case STATE_ATTRIBUTE_U_LN_QN:
604: _attributes.addAttributeWithQName(
605: readStructureString(), readStructureString(),
606: readStructureString(), readStructureString(),
607: readContentString());
608: break;
609: case STATE_ATTRIBUTE_P_U_LN: {
610: final String p = readStructureString();
611: final String u = readStructureString();
612: final String ln = readStructureString();
613: _attributes.addAttributeWithQName(u, ln,
614: getQName(p, ln), readStructureString(),
615: readContentString());
616: break;
617: }
618: case STATE_ATTRIBUTE_U_LN: {
619: final String u = readStructureString();
620: final String ln = readStructureString();
621: _attributes.addAttributeWithQName(u, ln, ln,
622: readStructureString(), readContentString());
623: break;
624: }
625: case STATE_ATTRIBUTE_LN: {
626: final String ln = readStructureString();
627: _attributes.addAttributeWithQName("", ln, ln,
628: readStructureString(), readContentString());
629: break;
630: }
631: default:
632: throw reportFatalError("Illegal state: " + item);
633: }
634: readStructure();
635:
636: item = peekStructure();
637: } while ((item & TYPE_MASK) == T_ATTRIBUTE);
638: }
639:
640: private void processNamespaceAttribute(String prefix, String uri)
641: throws SAXException {
642: _contentHandler.startPrefixMapping(prefix, uri);
643:
644: if (_namespacePrefixesFeature) {
645: // Add the namespace delcaration as an attribute
646: if (prefix != "") {
647: _attributes.addAttributeWithQName(
648: XMLConstants.XMLNS_ATTRIBUTE_NS_URI, prefix,
649: getQName(XMLConstants.XMLNS_ATTRIBUTE, prefix),
650: "CDATA", uri);
651: } else {
652: _attributes.addAttributeWithQName(
653: XMLConstants.XMLNS_ATTRIBUTE_NS_URI,
654: XMLConstants.XMLNS_ATTRIBUTE,
655: XMLConstants.XMLNS_ATTRIBUTE, "CDATA", uri);
656: }
657: }
658:
659: cacheNamespacePrefix(prefix);
660: }
661:
662: private void cacheNamespacePrefix(String prefix) {
663: if (_namespacePrefixesIndex == _namespacePrefixes.length) {
664: final String[] namespaceAttributes = new String[_namespacePrefixesIndex * 3 / 2 + 1];
665: System.arraycopy(_namespacePrefixes, 0,
666: namespaceAttributes, 0, _namespacePrefixesIndex);
667: _namespacePrefixes = namespaceAttributes;
668: }
669:
670: _namespacePrefixes[_namespacePrefixesIndex++] = prefix;
671: }
672:
673: private void cacheNamespacePrefixIndex() {
674: if (_namespaceAttributesStackIndex == _namespaceAttributesStack.length) {
675: final int[] namespaceAttributesStack = new int[_namespaceAttributesStackIndex * 3 / 2 + 1];
676: System.arraycopy(_namespaceAttributesStack, 0,
677: namespaceAttributesStack, 0,
678: _namespaceAttributesStackIndex);
679: _namespaceAttributesStack = namespaceAttributesStack;
680: }
681:
682: _namespaceAttributesStack[_namespaceAttributesStackIndex++] = _namespacePrefixesIndex;
683: }
684:
685: private void processComment(String s) throws SAXException {
686: processComment(s.toCharArray(), 0, s.length());
687: }
688:
689: private void processComment(char[] ch, int start, int length)
690: throws SAXException {
691: _lexicalHandler.comment(ch, start, length);
692: }
693:
694: private void processProcessingInstruction(String target, String data)
695: throws SAXException {
696: _contentHandler.processingInstruction(target, data);
697: }
698:
699: private static final DefaultWithLexicalHandler DEFAULT_LEXICAL_HANDLER = new DefaultWithLexicalHandler();
700: }
|