001: /*
002: * $Id: XMLEventReaderImpl.java,v 1.3 2006/11/03 18:29:18 spericas Exp $
003: */
004:
005: /*
006: * The contents of this file are subject to the terms
007: * of the Common Development and Distribution License
008: * (the License). You may not use this file except in
009: * compliance with the License.
010: *
011: * You can obtain a copy of the license at
012: * https://glassfish.dev.java.net/public/CDDLv1.0.html.
013: * See the License for the specific language governing
014: * permissions and limitations under the License.
015: *
016: * When distributing Covered Code, include this CDDL
017: * Header Notice in each file and include the License file
018: * at https://glassfish.dev.java.net/public/CDDLv1.0.html.
019: * If applicable, add the following below the CDDL Header,
020: * with the fields enclosed by brackets [] replaced by
021: * you own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * [Name of File] [ver.__] [Date]
025: *
026: * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
027: */
028:
029: package com.sun.xml.stream;
030:
031: import com.sun.xml.stream.events.XMLEventAllocatorImpl;
032: import java.util.NoSuchElementException;
033: import javax.xml.stream.XMLInputFactory;
034: import javax.xml.stream.XMLStreamConstants;
035: import javax.xml.stream.XMLStreamException;
036: import javax.xml.stream.XMLStreamReader;
037: import javax.xml.stream.events.EntityReference;
038: import javax.xml.stream.events.XMLEvent;
039: import javax.xml.stream.util.XMLEventAllocator;
040:
041: /**
042: * @author Neeraj Bajaj Sun Microsystems
043: * @author Santiago.PericasGeertsen@sun.com
044: */
045:
046: public class XMLEventReaderImpl implements
047: javax.xml.stream.XMLEventReader {
048:
049: protected XMLStreamReader fXMLReader;
050: protected XMLEventAllocator fXMLEventAllocator;
051:
052: //only constructor will do because we delegate everything to underlying XMLStreamReader
053: public XMLEventReaderImpl(XMLStreamReader reader)
054: throws XMLStreamException {
055: fXMLReader = reader;
056: fXMLEventAllocator = (XMLEventAllocator) reader
057: .getProperty(XMLInputFactory.ALLOCATOR);
058: if (fXMLEventAllocator == null) {
059: fXMLEventAllocator = new XMLEventAllocatorImpl();
060: }
061: fPeekedEvent = fXMLEventAllocator.allocate(fXMLReader);
062: }
063:
064: public boolean hasNext() {
065: //if we have the peeked event return 'true'
066: if (fPeekedEvent != null)
067: return true;
068: //this is strange XMLStreamReader throws XMLStreamException
069: //XMLEventReader doesn't throw XMLStreamException
070: boolean next = false;
071: try {
072: next = fXMLReader.hasNext();
073: } catch (XMLStreamException ex) {
074: return false;
075: }
076: return next;
077: }
078:
079: public XMLEvent nextEvent() throws XMLStreamException {
080: //if application peeked return the peeked event
081: if (fPeekedEvent != null) {
082: fLastEvent = fPeekedEvent;
083: fPeekedEvent = null;
084: return fLastEvent;
085: } else if (fXMLReader.hasNext()) {
086: //advance the reader to next state.
087: fXMLReader.next();
088: return fLastEvent = fXMLEventAllocator.allocate(fXMLReader);
089: } else {
090: fLastEvent = null;
091: throw new NoSuchElementException();
092: }
093: }
094:
095: public void remove() {
096: //remove of the event is not supported.
097: throw new java.lang.UnsupportedOperationException();
098: }
099:
100: public void close() throws XMLStreamException {
101: fXMLReader.close();
102: }
103:
104: /** Reads the content of a text-only element. Precondition:
105: * the current event is START_ELEMENT. Postcondition:
106: * The current event is the corresponding END_ELEMENT.
107: * @throws XMLStreamException if the current event is not a START_ELEMENT
108: * or if a non text element is encountered
109: */
110: public String getElementText() throws XMLStreamException {
111: //we have to keep reference to the 'last event' of the stream to be able
112: //to make this check - is there another way ? - nb.
113: if (fLastEvent.getEventType() != XMLEvent.START_ELEMENT) {
114: throw new XMLStreamException(
115: "parser must be on START_ELEMENT to read next text",
116: fLastEvent.getLocation());
117: }
118:
119: // STag content ETag
120: //[43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)*
121:
122: //<foo>....some long text say in KB and underlying parser reports multiple character
123: // but getElementText() events....</foo>
124:
125: //having a peeked event makes things really worse -- we have to test the first event
126: if (fPeekedEvent != null) {
127: XMLEvent event = fPeekedEvent;
128: fPeekedEvent = null;
129: int type = event.getEventType();
130:
131: String data = null;
132: if (type == XMLEvent.CHARACTERS || type == XMLEvent.SPACE
133: || type == XMLEvent.CDATA) {
134: data = event.asCharacters().getData();
135: } else if (type == XMLEvent.ENTITY_REFERENCE) {
136: data = ((EntityReference) event).getDeclaration()
137: .getReplacementText();
138: } else if (type == XMLEvent.COMMENT
139: || type == XMLEvent.PROCESSING_INSTRUCTION) {
140: //ignore
141: } else if (type == XMLEvent.START_ELEMENT) {
142: throw new XMLStreamException(
143: "elementGetText() function expects text only elment but START_ELEMENT was encountered.",
144: event.getLocation());
145: } else if (type == XMLEvent.END_ELEMENT) {
146: return "";
147: }
148:
149: //create the string buffer and add initial data
150: StringBuffer buffer = new StringBuffer();
151: if (data != null && data.length() > 0) {
152: buffer.append(data);
153: }
154: //get the next event -- we should stop at END_ELEMENT but it can be any thing
155: //things are worse when implementing this function in XMLEventReader because
156: //there isn't any function called getText() which can get values for
157: //space, cdata, characters and entity reference
158: //nextEvent() would also set the last event.
159: event = nextEvent();
160: while (event.getEventType() != XMLEvent.END_ELEMENT) {
161: if (type == XMLEvent.CHARACTERS
162: || type == XMLEvent.SPACE
163: || type == XMLEvent.CDATA) {
164: data = event.asCharacters().getData();
165: } else if (type == XMLEvent.ENTITY_REFERENCE) {
166: data = ((EntityReference) event).getDeclaration()
167: .getReplacementText();
168: } else if (type == XMLEvent.COMMENT
169: || type == XMLEvent.PROCESSING_INSTRUCTION) {
170: //ignore
171: } else if (type == XMLEvent.END_DOCUMENT) {
172: throw new XMLStreamException(
173: "unexpected end of document when reading element text content");
174: } else if (type == XMLEvent.START_ELEMENT) {
175: throw new XMLStreamException(
176: "elementGetText() function expects text only elment but START_ELEMENT was encountered.",
177: event.getLocation());
178: } else {
179: throw new XMLStreamException(
180: "Unexpected event type " + type, event
181: .getLocation());
182: }
183: //add the data to the buffer
184: if (data != null && data.length() > 0) {
185: buffer.append(data);
186: }
187: }
188: return buffer.toString();
189: }//if (fPeekedEvent != null)
190:
191: //if there was no peeked event simply delegate everything to fXMLReader
192: return fXMLReader.getElementText();
193: }
194:
195: /** Get the value of a feature/property from the underlying implementation
196: * @param name The name of the property
197: * @return The value of the property
198: * @throws IllegalArgumentException if the property is not supported
199: */
200: public Object getProperty(java.lang.String name)
201: throws java.lang.IllegalArgumentException {
202: return fXMLReader.getProperty(name);
203: }
204:
205: /** Skips any insignificant space events until a START_ELEMENT or
206: * END_ELEMENT is reached. If anything other than space characters are
207: * encountered, an exception is thrown. This method should
208: * be used when processing element-only content because
209: * the parser is not able to recognize ignorable whitespace if
210: * the DTD is missing or not interpreted.
211: * @throws XMLStreamException if anything other than space characters are encountered
212: */
213: public XMLEvent nextTag() throws XMLStreamException {
214: //its really a pain if there is peeked event before calling nextTag()
215: if (fPeekedEvent != null) {
216: //check the peeked event first.
217: XMLEvent event = fPeekedEvent;
218: fPeekedEvent = null;
219: int eventType = event.getEventType();
220: //if peeked event is whitespace move to the next event
221: //if peeked event is PI or COMMENT move to the next event
222: if ((event.isCharacters() && event.asCharacters()
223: .isWhiteSpace())
224: || eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
225: || eventType == XMLStreamConstants.COMMENT) {
226: event = nextEvent();
227: eventType = event.getEventType();
228: }
229:
230: //we have to have the while loop because there can be many PI or comment event in sucession
231: while ((event.isCharacters() && event.asCharacters()
232: .isWhiteSpace())
233: || eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
234: || eventType == XMLStreamConstants.COMMENT) {
235:
236: event = nextEvent();
237: eventType = event.getEventType();
238: }
239:
240: if (eventType != XMLStreamConstants.START_ELEMENT
241: && eventType != XMLStreamConstants.END_ELEMENT) {
242: throw new XMLStreamException(
243: "expected start or end tag", event
244: .getLocation());
245: }
246: return event;
247: }
248:
249: //if there is no peeked event -- delegate the work of getting next event to fXMLReader
250: fXMLReader.nextTag();
251: return fXMLEventAllocator.allocate(fXMLReader);
252: }
253:
254: public Object next() {
255: Object object = null;
256: try {
257: object = nextEvent();
258: } catch (XMLStreamException streamException) {
259: fLastEvent = null;
260: //xxx: what should be done in this case ?
261: throw new NoSuchElementException();
262: }
263: return object;
264: }
265:
266: public XMLEvent peek() throws XMLStreamException {
267: //if someone call peek() two times we should just return the peeked event
268: //this is reset if we call next() or nextEvent()
269: if (fPeekedEvent != null)
270: return fPeekedEvent;
271:
272: if (hasNext()) {
273: //revisit: we can implement peek() by calling underlying reader to advance
274: // the stream and returning the event without the knowledge of the user
275: // that the stream was advanced but the point is we are advancing the stream
276: //here. -- nb.
277:
278: // Is there any application that relies on this behavior ?
279: //Can it be an application knows that there is particularly very large 'comment' section
280: //or character data which it doesn't want to read or to be returned as event
281: //But as of now we are creating every event but it can be optimized not to create
282: // the event.
283: fXMLReader.next();
284: fPeekedEvent = fXMLEventAllocator.allocate(fXMLReader);
285: return fPeekedEvent;
286: } else {
287: return null;
288: }
289: }//peek()
290:
291: private XMLEvent fPeekedEvent;
292: private XMLEvent fLastEvent;
293:
294: }//XMLEventReaderImpl
|