001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Emil Ong
028: */
029:
030: package com.caucho.xml.stream;
031:
032: import com.caucho.util.CharBuffer;
033: import com.caucho.util.L10N;
034: import com.caucho.vfs.*;
035:
036: import static javax.xml.XMLConstants.*;
037: import javax.xml.namespace.NamespaceContext;
038: import javax.xml.namespace.QName;
039: import javax.xml.stream.Location;
040: import javax.xml.stream.XMLStreamException;
041: import javax.xml.stream.XMLStreamReader;
042: import javax.xml.transform.dom.DOMSource;
043: import java.io.IOException;
044: import java.io.InputStream;
045: import java.io.Reader;
046: import java.util.NoSuchElementException;
047: import java.util.logging.Logger;
048:
049: import org.w3c.dom.*;
050: import static org.w3c.dom.Node.*;
051:
052: /**
053: * XML pull-parser interface.
054: */
055: public class DOMSourceXMLStreamReaderImpl implements XMLStreamReader {
056: private static final Logger log = Logger
057: .getLogger(DOMSourceXMLStreamReaderImpl.class.getName());
058: private static final L10N L = new L10N(
059: DOMSourceXMLStreamReaderImpl.class);
060:
061: private NamespaceReaderContext _namespaceTracker;
062: private Node _current;
063: private boolean _ending = false;
064: private String _systemId;
065: private String _publicId;
066:
067: private String _version;
068: private String _encoding;
069: private boolean _standalone = false;
070:
071: private boolean _first = true;
072:
073: public DOMSourceXMLStreamReaderImpl(DOMSource source)
074: throws XMLStreamException {
075: this (source.getNode());
076: }
077:
078: public DOMSourceXMLStreamReaderImpl(Node node)
079: throws XMLStreamException {
080: _current = node;
081:
082: if (node.getNodeType() == Node.ELEMENT_NODE)
083: _first = false;
084:
085: init();
086: }
087:
088: private void init() throws XMLStreamException {
089: _namespaceTracker = new NamespaceReaderContext();
090: declareNamespaces();
091:
092: if (_current instanceof Document) {
093: Document document = (Document) _current;
094:
095: _encoding = document.getXmlEncoding();
096: _version = document.getXmlVersion();
097: _standalone = document.getXmlStandalone();
098: }
099: }
100:
101: private void declareNamespaces() {
102: if (isStartElement()) {
103: _namespaceTracker.push();
104:
105: Element element = (Element) _current;
106:
107: NamedNodeMap map = element.getAttributes();
108:
109: if (map == null)
110: return;
111:
112: for (int i = 0; i < map.getLength(); i++) {
113: Attr attr = (Attr) map.item(i);
114:
115: if (attr.getName().startsWith(XMLNS_ATTRIBUTE)) {
116: int colon = attr.getName().indexOf(':');
117:
118: if (colon < 0)
119: _namespaceTracker.declare(DEFAULT_NS_PREFIX,
120: attr.getNodeValue());
121: else {
122: String prefix = attr.getName().substring(0,
123: colon);
124: _namespaceTracker.declare(prefix, attr
125: .getNodeValue());
126: }
127: }
128: }
129: }
130: }
131:
132: public int getAttributeCount() {
133: if (isStartElement()) {
134: Element element = (Element) _current;
135:
136: NamedNodeMap map = element.getAttributes();
137:
138: if (map == null)
139: return 0;
140:
141: int attributeCount = 0;
142:
143: // count the non-namespace prefix attributes
144: for (int i = 0; i < map.getLength(); i++) {
145: Attr attr = (Attr) map.item(i);
146:
147: if (!attr.getName().startsWith(XMLNS_ATTRIBUTE))
148: attributeCount++;
149: }
150:
151: return attributeCount;
152: } else {
153: throw new IllegalStateException(L
154: .l("Current event not a start or end element"));
155: }
156: }
157:
158: private Attr getAttribute(int index) {
159: if (isStartElement()) {
160: Element element = (Element) _current;
161:
162: NamedNodeMap map = element.getAttributes();
163:
164: if (map == null)
165: throw new NoSuchElementException();
166:
167: int attributeCount = 0;
168:
169: for (int i = 0; i < map.getLength(); i++) {
170: Attr attr = (Attr) map.item(i);
171:
172: if (!attr.getName().startsWith(XMLNS_ATTRIBUTE)) {
173: if (attributeCount == index)
174: return attr;
175:
176: attributeCount++;
177: }
178: }
179:
180: throw new NoSuchElementException();
181: } else {
182: throw new IllegalStateException(L
183: .l("Current event not a start or end element"));
184: }
185: }
186:
187: public String getAttributeLocalName(int index) {
188: Attr attr = getAttribute(index);
189:
190: return attr.getLocalName();
191: }
192:
193: public QName getAttributeName(int index) {
194: Attr attr = getAttribute(index);
195:
196: int colon = attr.getName().indexOf(':');
197:
198: if (colon < 0) {
199: if (attr.getNamespaceURI() == null
200: || "".equals(attr.getNamespaceURI()))
201: return new QName(attr.getNamespaceURI(), attr
202: .getLocalName());
203: else
204: return new QName(attr.getLocalName());
205: }
206:
207: String prefix = attr.getName().substring(0, colon);
208:
209: return new QName(attr.getNamespaceURI(), attr.getLocalName(),
210: prefix);
211: }
212:
213: public String getAttributeNamespace(int index) {
214: Attr attr = getAttribute(index);
215:
216: return attr.getNamespaceURI();
217: }
218:
219: public String getAttributePrefix(int index) {
220: Attr attr = getAttribute(index);
221:
222: int colon = attr.getName().indexOf(':');
223:
224: if (colon < 0)
225: return DEFAULT_NS_PREFIX;
226:
227: String prefix = attr.getName().substring(0, colon);
228:
229: return prefix;
230: }
231:
232: public String getAttributeType(int index) {
233: return "CDATA";
234: }
235:
236: public String getAttributeValue(int index) {
237: Attr attr = getAttribute(index);
238:
239: return attr.getNodeValue();
240: }
241:
242: public boolean isAttributeSpecified(int index) {
243: return index < getAttributeCount();
244: }
245:
246: public String getAttributeValue(String namespaceURI,
247: String localName) {
248: if (isStartElement()) {
249: Element element = (Element) _current;
250:
251: NamedNodeMap map = element.getAttributes();
252:
253: Attr attr = (Attr) map.getNamedItemNS(namespaceURI,
254: localName);
255:
256: if (attr == null)
257: return null;
258:
259: return attr.getNodeValue();
260: } else {
261: throw new IllegalStateException(L
262: .l("Current event not a start or end element"));
263: }
264: }
265:
266: public String getCharacterEncodingScheme() {
267: return _encoding;
268: }
269:
270: public String getElementText() throws XMLStreamException {
271: return _current.getTextContent();
272: }
273:
274: public String getEncoding() {
275: return _encoding;
276: }
277:
278: public int getEventType() {
279: if (_current == null)
280: return END_DOCUMENT;
281:
282: switch (_current.getNodeType()) {
283: case ATTRIBUTE_NODE:
284: return ATTRIBUTE;
285: case CDATA_SECTION_NODE:
286: return CDATA;
287: case COMMENT_NODE:
288: return COMMENT;
289: case DOCUMENT_FRAGMENT_NODE:
290: throw new IllegalStateException();
291: case DOCUMENT_NODE:
292: return _ending ? END_DOCUMENT : START_DOCUMENT;
293: case DOCUMENT_TYPE_NODE:
294: return DTD;
295: case ELEMENT_NODE:
296: return _ending ? END_ELEMENT : START_ELEMENT;
297: case ENTITY_NODE:
298: return ENTITY_DECLARATION;
299: case ENTITY_REFERENCE_NODE:
300: return ENTITY_REFERENCE;
301: case NOTATION_NODE:
302: return NOTATION_DECLARATION;
303: case PROCESSING_INSTRUCTION_NODE:
304: return PROCESSING_INSTRUCTION;
305: case TEXT_NODE:
306: return CHARACTERS;
307: }
308:
309: return -1;
310: }
311:
312: public Location getLocation() {
313: return new UnknownLocation();
314: }
315:
316: public String getLocalName() {
317: if (_current instanceof Element) {
318: Element element = (Element) _current;
319:
320: return element.getLocalName();
321: } else {
322: throw new IllegalStateException(L
323: .l("Current event not a start or end element"));
324: }
325: }
326:
327: public String getNamespaceURI() {
328: if (isStartElement()) {
329: Element element = (Element) _current;
330:
331: return element.getNamespaceURI();
332: } else {
333: throw new IllegalStateException(L
334: .l("Current event not a start or end element"));
335: }
336: }
337:
338: public QName getName() {
339: if (_current.getNodeType() == ELEMENT_NODE) {
340: Element element = (Element) _current;
341:
342: String name = element.getNodeName();
343:
344: int colon = name.indexOf(':');
345:
346: if (colon < 0) {
347: if (element.getNamespaceURI() == null
348: || "".equals(element.getNamespaceURI()))
349: return new QName(element.getLocalName());
350: else
351: return new QName(element.getNamespaceURI(), element
352: .getLocalName());
353: }
354:
355: String prefix = name.substring(0, colon);
356:
357: return new QName(element.getNamespaceURI(), element
358: .getLocalName(), prefix);
359: } else {
360: throw new IllegalStateException(L
361: .l("Current event not a start or end element"));
362: }
363: }
364:
365: public NamespaceContext getNamespaceContext() {
366: return _namespaceTracker;
367: }
368:
369: public int getNamespaceCount() {
370: return _namespaceTracker.getNumDecls();
371: }
372:
373: public String getNamespacePrefix(int index) {
374: String prefix = _namespaceTracker.getPrefix(index);
375:
376: // The API specifies that this function return a different value for
377: // the default namespace, null, than any other function, which all return
378: // the constant defined in XMLConstants.
379: if (DEFAULT_NS_PREFIX.equals(prefix))
380: return null;
381: else
382: return prefix;
383: }
384:
385: public String getNamespaceURI(int index) {
386: return _namespaceTracker.getUri(index);
387: }
388:
389: public String getNamespaceURI(String prefix) {
390: return _namespaceTracker.getUri(prefix);
391: }
392:
393: public String getPIData() {
394: if (_current.getNodeType() != PROCESSING_INSTRUCTION_NODE)
395: return null;
396:
397: ProcessingInstruction pi = (ProcessingInstruction) _current;
398:
399: return pi.getData();
400: }
401:
402: public String getPITarget() {
403: if (_current.getNodeType() != PROCESSING_INSTRUCTION_NODE)
404: return null;
405:
406: ProcessingInstruction pi = (ProcessingInstruction) _current;
407:
408: return pi.getTarget();
409: }
410:
411: public String getPrefix() {
412: if (isStartElement()) {
413: Element element = (Element) _current;
414:
415: String name = element.getNodeName();
416:
417: int colon = name.indexOf(':');
418:
419: if (colon < 0)
420: return DEFAULT_NS_PREFIX;
421:
422: return name.substring(0, colon);
423: } else {
424: throw new IllegalStateException(L
425: .l("Current event not a start or end element"));
426: }
427: }
428:
429: public Object getProperty(String name)
430: throws IllegalArgumentException {
431: if ("javax.xml.stream.notations".equals(name)) {
432: throw new UnsupportedOperationException(getClass()
433: .getName());
434: } else if ("javax.xml.stream.entities".equals(name)) {
435: throw new UnsupportedOperationException(getClass()
436: .getName());
437: } else {
438: throw new IllegalArgumentException("property \"" + name
439: + "+\" not supported");
440: }
441: }
442:
443: /**
444: * Returns the current text string.
445: */
446: public String getText() {
447: if (_current instanceof CharacterData) {
448: CharacterData data = (CharacterData) _current;
449:
450: return data.getData();
451: }
452:
453: throw new IllegalStateException("Not a text node");
454: }
455:
456: /**
457: * Returns a character buffer for the current text.
458: */
459: public char[] getTextCharacters() {
460: return getText().toCharArray();
461: }
462:
463: /**
464: * Reads the current text into a buffer.
465: */
466: public int getTextCharacters(int sourceStart, char[] target,
467: int targetStart, int length) throws XMLStreamException {
468: char[] source = getTextCharacters();
469:
470: int ret = Math.min(source.length - sourceStart, length);
471:
472: System.arraycopy(source, sourceStart, target, targetStart, ret);
473:
474: return ret;
475: }
476:
477: /**
478: * Returns the length of the current text.
479: */
480: public int getTextLength() {
481: return getText().length();
482: }
483:
484: /**
485: * Returns the offset of the current text.
486: */
487: public int getTextStart() {
488: return 0;
489: }
490:
491: public String getVersion() {
492: return _version;
493: }
494:
495: public boolean hasName() {
496: return _current.getNodeType() == ELEMENT_NODE;
497: }
498:
499: public boolean hasText() {
500: switch (_current.getNodeType()) {
501: case TEXT_NODE:
502: case DOCUMENT_TYPE_NODE:
503: case ENTITY_REFERENCE_NODE:
504: case COMMENT_NODE:
505: return true;
506: default:
507: return false;
508: }
509: }
510:
511: public boolean isCharacters() {
512: return _current.getNodeType() == TEXT_NODE;
513: }
514:
515: public boolean isEndElement() {
516: return _ending;
517: }
518:
519: public boolean isStandalone() {
520: return _standalone;
521: }
522:
523: public boolean isStartElement() {
524: return _current != null
525: && _current.getNodeType() == ELEMENT_NODE && !_ending;
526: }
527:
528: public boolean isWhiteSpace() {
529: if (!isCharacters())
530: return false;
531:
532: String text = getText();
533:
534: for (int i = 0; i < text.length(); i++) {
535: if (!Character.isWhitespace(text.charAt(i)))
536: return false;
537: }
538:
539: return true;
540: }
541:
542: /**
543: * Skips until the next START_ELEMENT or END_ELEMENT
544: */
545: public int nextTag() throws XMLStreamException {
546: while (true) {
547: int tag = next();
548:
549: if (tag < 0 || tag == START_ELEMENT || tag == END_ELEMENT) {
550: return tag;
551: }
552: }
553: }
554:
555: public void require(int type, String namespaceURI, String localName)
556: throws XMLStreamException {
557: if (type != getEventType())
558: throw new XMLStreamException("expected "
559: + constantToString(type) + ", " + "found "
560: + constantToString(getEventType()) + " at "
561: + getLocation());
562:
563: if (localName != null && !localName.equals(getLocalName()))
564: throw new XMLStreamException("expected <" + localName
565: + ">, found " + "<" + getLocalName() + "> at "
566: + getLocation());
567:
568: if (namespaceURI != null
569: && !namespaceURI.equals(getNamespaceURI()))
570: throw new XMLStreamException("expected xmlns="
571: + namespaceURI + ", found xmlns="
572: + getNamespaceURI() + " at " + getLocation());
573: }
574:
575: public boolean standaloneSet() {
576: return isStandalone();
577: }
578:
579: public boolean hasNext() throws XMLStreamException {
580: return (_current != null && getEventType() != END_DOCUMENT);
581: }
582:
583: public int next() throws XMLStreamException {
584: if (_current == null)
585: throw new NoSuchElementException();
586:
587: if (_first) {
588: _first = false;
589: return getEventType();
590: }
591:
592: if (_ending) {
593: if (_current.getNodeType() == ELEMENT_NODE)
594: _namespaceTracker.pop();
595:
596: if (_current.getNextSibling() != null) {
597: _current = _current.getNextSibling();
598: _ending = false;
599: } else
600: _current = _current.getParentNode();
601: } else if (_current.getFirstChild() != null) {
602: _current = _current.getFirstChild();
603: } else if (isStartElement()) {
604: _ending = true;
605: } else if (_current.getNextSibling() != null) {
606: _current = _current.getNextSibling();
607: } else {
608: _current = _current.getParentNode();
609: _ending = true;
610: }
611:
612: declareNamespaces();
613:
614: return getEventType();
615: }
616:
617: public void close() throws XMLStreamException {
618: }
619:
620: private class UnknownLocation implements Location {
621: public int getCharacterOffset() {
622: return -1;
623: }
624:
625: public int getColumnNumber() {
626: return -1;
627: }
628:
629: public int getLineNumber() {
630: return -1;
631: }
632:
633: public String getPublicId() {
634: return null;
635: }
636:
637: public String getSystemId() {
638: return null;
639: }
640: }
641:
642: private static String constantToString(int constant) {
643: switch (constant) {
644: case ATTRIBUTE:
645: return "ATTRIBUTE";
646: case CDATA:
647: return "CDATA";
648: case CHARACTERS:
649: return "CHARACTERS";
650: case COMMENT:
651: return "COMMENT";
652: case DTD:
653: return "DTD";
654: case END_DOCUMENT:
655: return "END_DOCUMENT";
656: case END_ELEMENT:
657: return "END_ELEMENT";
658: case ENTITY_DECLARATION:
659: return "ENTITY_DECLARATION";
660: case ENTITY_REFERENCE:
661: return "ENTITY_REFERENCE";
662: case NAMESPACE:
663: return "NAMESPACE";
664: case NOTATION_DECLARATION:
665: return "NOTATION_DECLARATION";
666: case PROCESSING_INSTRUCTION:
667: return "PROCESSING_INSTRUCTION";
668: case SPACE:
669: return "SPACE";
670: case START_DOCUMENT:
671: return "START_DOCUMENT";
672: case START_ELEMENT:
673: return "START_ELEMENT";
674: default:
675: throw new RuntimeException("constantToString(" + constant
676: + ") unknown");
677: }
678: }
679: }
|