/* * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.ukit.xml; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.NoSuchElementException; import javax.xml.stream.Location; import javax.xml.stream.XMLResolver; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.xml.sax.InputSource; import org.xml.sax.ext.Attributes2; /** * XML non-validating pull parser. * * @see javax.xml.stream.XMLStreamReader */ /* pkg */ final class ParserStAX extends Parser implements XMLStreamReader, Location { private Pair mEQhead; // internal event queue private Pair mEQtail; private int mNsNum; // number of namespaces defined on current element private String mEnc; // encoding if provided by factory /* pkg */ DTDStreamReaderImp mDTD; // DTD reader or null /* pkg */ XMLResolver mResolver; /** * Constructor. * * @param stream Document input stream. * @param encoding Document encoding or <code>null</code>. */ /* pkg */ ParserStAX(InputStream stream, String encoding) throws XMLStreamException { super(); // Set up the document if (stream == null) throw new NullPointerException(); mInp = new Input(BUFFSIZE_READER); mPh = PH_BEFORE_DOC; // before parsing try { mEnc = encoding; InputSource is = new InputSource(stream); is.setEncoding(encoding); setinp(is); } catch(XMLStreamException xmlse) { throw xmlse; } catch(IOException ioe) { throw new XMLStreamException(ioe); } catch(RuntimeException rte) { throw rte; } catch(Exception e) { panic(e.toString()); } init(); mIsNSAware = true; // Set initial event Pair evt = pair(null); evt.id = XMLStreamReader.START_DOCUMENT; eqAdd(evt); } /** * Constructor. * * @param stream Document reader. */ /* pkg */ ParserStAX(Reader reader) throws XMLStreamException { super(); // Set up the document if (reader == null) throw new NullPointerException(); mInp = new Input(BUFFSIZE_READER); mPh = PH_BEFORE_DOC; // before parsing try { setinp(new InputSource(reader)); } catch(XMLStreamException xmlse) { throw xmlse; } catch(IOException ioe) { throw new XMLStreamException(ioe); } catch(RuntimeException rte) { throw rte; } catch(Exception e) { panic(e.toString()); } init(); mIsNSAware = true; // Set initial event Pair evt = pair(null); evt.id = XMLStreamReader.START_DOCUMENT; eqAdd(evt); } /** * Get the value of a feature/property from the underlying implementation * * @param name The name of the property, may not be <code>null</code> * @return The value of the property * @throws IllegalArgumentException if name is <code>null</code> */ public Object getProperty(String name) throws IllegalArgumentException { if (name == null) throw new IllegalArgumentException(name); if ("javax.xml.stream.isValidating".equals(name)) { return Boolean.FALSE; } else if ("javax.xml.stream.isNamespaceAware".equals(name)) { return (mIsNSAware)? Boolean.TRUE: Boolean.FALSE; } else if ("javax.xml.stream.isCoalescing".equals(name)) { return Boolean.FALSE; } else if ("javax.xml.stream.isReplacingEntityReferences".equals(name)) { return Boolean.TRUE; } else if ("javax.xml.stream.isSupportingExternalEntities".equals(name)) { return (mResolver != null)? Boolean.TRUE: Boolean.FALSE; } else if ("javax.xml.stream.supportDTD".equals(name)) { return Boolean.TRUE; } else if ("javax.xml.stream.resolver".equals(name)) { return mResolver; } else if ("javax.xml.stream.DTDStreamReader".equals(name)) { if (getEventType() == XMLStreamReader.DTD) { if (mDTD == null) { Pair evt = pair(null); evt.name = mEQhead.name; evt.chars = null; evt.list = pair(null); evt.list.name = mEQhead.list.name; evt.list.value = mEQhead.list.value; mDTD = new DTDStreamReaderImp(this, evt); } return mDTD; } return null; } return null; } /** * Get next parsing event - a processor may return all contiguous * character data in a single chunk, or it may split it into several chunks. * If the property javax.xml.stream.isCoalescing is set to <code>true</code> * element content must be coalesced and only one CHARACTERS event * must be returned for contiguous element content or * CDATA Sections. * * By default entity references must be * expanded and reported transparently to the application. * An exception will be thrown if an entity reference cannot be expanded. * If element content is empty (i.e. content is "") then no CHARACTERS event * will be reported. * * <p>Given the following XML:<br> * <foo><!--description-->content text<![CDATA[<greeting>Hello * </greeting>]]>other content</foo><br> * The behavior of calling next() when being on foo will be:<br> * 1- the comment (COMMENT)<br> * 2- then the characters section (CHARACTERS)<br> * 3- then the CDATA section (another CHARACTERS)<br> * 4- then the next characters section (another CHARACTERS)<br> * 5- then the END_ELEMENT<br> * * <p><b>NOTE:</b> empty element (such as <tag/>) will be reported * with two separate events: START_ELEMENT, END_ELEMENT - This preserves * parsing equivalency of empty element to <tag></tag>. * * @return the integer code corresponding to the current parse event * @throws NoSuchElementException if this is called when hasNext() returns * <code>false</code> * @throws XMLStreamException if there is an error processing the underlying * XML source */ public int next() throws XMLStreamException { try { if (mEQhead != null) { if (mEQhead.id == XMLStreamReader.END_ELEMENT) { // Remove all element's namespace mappings while(mPref.list == mElm) mPref = del(mPref); // Remove the top element tag mElm = del(mElm); } // Remove the head of the queue del(eqGet()); if (mEQhead != null) return mEQhead.id; } // Event queue is empty. Get more events. mDTD = null; // make sure new events go into local queue Pair evt = null; int eid = (mPh < PH_AFTER_DOC)? step(): 0; switch(mPh) { case PH_DOC_START: // document start mPh = PH_MISC_DTD; case PH_MISC_DTD: // misc before DTD case PH_DTD_MISC: // misc after DTD switch (eid) { case EV_ELM: mNsNum = -1; // recalculate the number if needed evt = pair(null); evt.id = XMLStreamReader.START_ELEMENT; evt.list = mElm; eqAdd(evt); if (mAttrs.getLength() != 0) { evt = pair(null); evt.id = XMLStreamReader.ATTRIBUTE; eqAdd(evt); } evt = pair(null); evt.id = XMLStreamReader.END_ELEMENT; evt.list = mElm; eqAdd(evt); // There is only one element in this document. mPh = (wsskip() != EOS)? PH_DOCELM_MISC: PH_AFTER_DOC; if (mPh == PH_AFTER_DOC) { evt = pair(null); evt.id = XMLStreamReader.END_DOCUMENT; eqAdd(evt); } break; case EV_ELMS: mNsNum = -1; // recalculate the number if needed evt = pair(null); evt.id = XMLStreamReader.START_ELEMENT; evt.list = mElm; eqAdd(evt); if (mAttrs.getLength() != 0) { evt = pair(null); evt.id = XMLStreamReader.ATTRIBUTE; eqAdd(evt); } mPh = PH_DOCELM; // document's element break; case EV_DTDS: if (mPh >= PH_DTD) panic(FAULT); mPh = PH_DTD; // DTD break; case EV_COMM: case EV_PI: break; default: panic(FAULT); } break; case PH_DTD: // DTD switch (eid) { case EV_COMM: case EV_PI: case EV_PENT: case EV_UENT: case EV_NOT: do { if (mEQhead != null) del(eqGet()); } while (step() != EV_DTDE); case EV_DTDE: mPh = PH_DTD_MISC; // misc after DTD next(); break; default: panic(FAULT); } break; case PH_DOCELM: // document's element switch (eid) { case EV_ELM: mNsNum = -1; // recalculate the number if needed evt = pair(null); evt.id = XMLStreamReader.START_ELEMENT; evt.list = mElm; eqAdd(evt); if (mAttrs.getLength() != 0) { evt = pair(null); evt.id = XMLStreamReader.ATTRIBUTE; eqAdd(evt); } evt = pair(null); evt.id = XMLStreamReader.END_ELEMENT; evt.list = mElm; eqAdd(evt); break; case EV_ELMS: mNsNum = -1; // recalculate the number if needed evt = pair(null); evt.id = XMLStreamReader.START_ELEMENT; evt.list = mElm; eqAdd(evt); if (mAttrs.getLength() != 0) { evt = pair(null); evt.id = XMLStreamReader.ATTRIBUTE; eqAdd(evt); } break; case EV_ELME: mNsNum = -1; // recalculate the number if needed evt = pair(null); evt.id = XMLStreamReader.END_ELEMENT; evt.list = mElm; eqAdd(evt); if (mElm.next == null) { mPh = (wsskip() != EOS)? PH_DOCELM_MISC: PH_AFTER_DOC; if (mPh == PH_AFTER_DOC) { evt = pair(null); evt.id = XMLStreamReader.END_DOCUMENT; eqAdd(evt); } } break; case EV_CDAT: mEQtail.id = XMLStreamReader.CDATA; break; case EV_TEXT: case EV_WSPC: case EV_COMM: case EV_PI: case EV_ENT: break; default: panic(FAULT); } break; case PH_DOCELM_MISC: // misc after element switch (eid) { case EV_COMM: case EV_PI: if (wsskip() == EOS) { mPh = PH_AFTER_DOC; evt = pair(null); evt.id = XMLStreamReader.END_DOCUMENT; eqAdd(evt); } break; default: panic(FAULT); } break; case PH_BEFORE_DOC: // before parsing case PH_AFTER_DOC: // after parsing if (mDoc != null) throw new NoSuchElementException(); default: throw new XMLStreamException(); } } catch(XMLStreamException xmlse) { throw xmlse; } catch(IOException ioe) { throw new XMLStreamException(ioe); } catch(RuntimeException rte) { throw rte; } catch(Exception e) { panic(e.toString()); } return mEQhead.id; } /** * Test if the current event is of the given type and if the namespace and * name match the current namespace and name of the current event. If the * namespaceURI is <code>null</code> it is not checked for equality, if the * localName is <code>null</code> it is not checked for equality. * * @param type the event type * @param namespaceURI the uri of the event, may be <code>null</code> * @param localName the localName of the event, may be <code>null</code> * @throws XMLStreamException if the required values are not matched. */ public void require(int type, String namespaceURI, String localName) throws XMLStreamException { try { if (type != getEventType()) throw new XMLStreamException(FAULT); if (namespaceURI != null && !namespaceURI.equals(getNamespaceURI())) throw new XMLStreamException(FAULT); if (localName != null && !localName.equals(getLocalName())) throw new XMLStreamException(FAULT); } catch (IllegalStateException ise) { throw new XMLStreamException(ise); } } /** * Reads the content of a text-only element, an exception is thrown if this * is not a text-only element. * Regardless of value of javax.xml.stream.isCoalescing this method always * returns coalesced content. * <br /> Precondition: the current event is START_ELEMENT. * <br /> Postcondition: the current event is the corresponding END_ELEMENT. * * <br />The method does the following (implementations are free to optimize * but must do equivalent processing): * <pre> * if(getEventType() != XMLStreamConstants.START_ELEMENT) { * throw new XMLStreamException( * "parser must be on START_ELEMENT to read next text", * getLocation()); * } * int eventType = next(); * StringBuffer content = new StringBuffer(); * while(eventType != XMLStreamConstants.END_ELEMENT ) { * if(eventType == XMLStreamConstants.CHARACTERS * || eventType == XMLStreamConstants.CDATA * || eventType == XMLStreamConstants.SPACE * || eventType == XMLStreamConstants.ENTITY_REFERENCE) { * content.append(getText()); * } else if(eventType == XMLStreamConstants.PROCESSING_INSTRUCTION * || eventType == XMLStreamConstants.COMMENT) { * // skipping * } else if(eventType == XMLStreamConstants.END_DOCUMENT) { * throw new XMLStreamException( * "unexpected end of document when reading element text content", * this); * } else if(eventType == XMLStreamConstants.START_ELEMENT) { * throw new XMLStreamException( * "element text content may not contain START_ELEMENT", * getLocation()); * } else { * throw new XMLStreamException( * "Unexpected event type "+eventType, getLocation()); * } * eventType = next(); * } * return content.toString(); * </pre> * * @throws XMLStreamException if the current event is not a START_ELEMENT * or if a non text element is encountered */ public String getElementText() throws XMLStreamException { int evt = getEventType(); if (evt != XMLStreamReader.START_ELEMENT) throw new XMLStreamException(FAULT); StringBuffer content = new StringBuffer(); evt = next(); while (evt != XMLStreamReader.END_ELEMENT) { switch (evt) { case XMLStreamReader.CHARACTERS: case XMLStreamReader.SPACE: case XMLStreamReader.CDATA: content.append(getText()); break; case XMLStreamReader.ENTITY_REFERENCE: content.append((getText() != null)? getText(): ""); break; case XMLStreamReader.PROCESSING_INSTRUCTION: case XMLStreamReader.COMMENT: case XMLStreamReader.ATTRIBUTE: case XMLStreamReader.NAMESPACE: break; default: throw new XMLStreamException(FAULT); } evt = next(); } return content.toString(); } /** * Skips any white space (isWhiteSpace() returns <code>true</code>), COMMENT, * or PROCESSING_INSTRUCTION, until a START_ELEMENT or END_ELEMENT is * reached. * If other than white space characters, COMMENT, PROCESSING_INSTRUCTION, * START_ELEMENT, END_ELEMENT are encountered, an exception is thrown. This * method should be used when processing element-only content separated by * white space. * * <br /> Precondition: none * <br /> Postcondition: the current event is START_ELEMENT or END_ELEMENT * and cursor may have moved over any whitespace event. * * <br />Essentially it does the following (implementations are free to * optimize but must do equivalent processing): * <pre> * int eventType = next(); * // skip whitespace * while((eventType == XMLStreamConstants.CHARACTERS * && isWhiteSpace()) * || (eventType == XMLStreamConstants.CDATA && isWhiteSpace()) * // skip whitespace * || eventType == XMLStreamConstants.SPACE * || eventType == XMLStreamConstants.PROCESSING_INSTRUCTION * || eventType == XMLStreamConstants.COMMENT) { * eventType = next(); * } * if (eventType != XMLStreamConstants.START_ELEMENT * && eventType != XMLStreamConstants.END_ELEMENT) { * throw new String XMLStreamException( * "expected start or end tag", getLocation()); * } * return eventType; * </pre> * * @return the event type of the element read (START_ELEMENT or END_ELEMENT) * @throws XMLStreamException if the current event is not white space, * PROCESSING_INSTRUCTION, START_ELEMENT or END_ELEMENT * @throws NoSuchElementException if this is called when hasNext() returns * <code>false</code> */ public int nextTag() throws XMLStreamException { int evt = 0; do { evt = next(); switch (evt) { case XMLStreamReader.SPACE: case XMLStreamReader.ATTRIBUTE: case XMLStreamReader.NAMESPACE: break; case XMLStreamReader.PROCESSING_INSTRUCTION: case XMLStreamReader.COMMENT: break; case XMLStreamReader.START_ELEMENT: case XMLStreamReader.END_ELEMENT: evt = -1; break; case XMLStreamReader.CHARACTERS: case XMLStreamReader.CDATA: if (isWhiteSpace()) break; default: throw new XMLStreamException(FAULT); } } while (evt > 0); return getEventType(); } /** * Returns <code>true</code> if there are more parsing events and * <code>false</code> if there are no more events. This method will return * <code>false</code> if the current state of the XMLStreamReader is * END_DOCUMENT * * @return <code>true</code> if there are more events, <code>false</code> * otherwise * @throws XMLStreamException if there is a fatal error detecting the next * state */ public boolean hasNext() throws XMLStreamException { mDTD = null; return (mEQhead != null && mEQhead.next != null) || (mPh >= PH_DOC_START && mPh < PH_AFTER_DOC); } /** * Frees any resources associated with this Reader. This method does not * close the underlying input source. * * @throws XMLStreamException if there are errors freeing associated resources */ public void close() throws XMLStreamException { mDTD = null; cleanup(); } /** * Returns <code>true</code> if the cursor points to a character data event * that consists of all whitespace * * @return <code>true</code> if the cursor points to all whitespace, * <code>false</code> otherwise */ public boolean isWhiteSpace() { switch (getEventType()) { case XMLStreamReader.CHARACTERS: return (mEQhead.name == null); // hint from bflash and bflash_ws case XMLStreamReader.SPACE: return true; case XMLStreamReader.CDATA: default: return false; } } /** * Returns the normalized attribute value of the attribute with the namespace * and localName. If the namespaceURI is <code>null</code> the namespace * is not checked for equality. A value of "" (empty String) is interpreted * to mean 'no namespace'. * * @param namespaceURI the namespace of the attribute * @param localName the local name of the attribute, cannot be * <code>null</code> * @return returns the value of the attribute , returns <code>null</code> if * not found * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE */ public String getAttributeValue(String namespaceURI, String localName) { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.ATTRIBUTE: if (namespaceURI != null && namespaceURI.length() == 0) return mAttrs.getValue(localName); if (mIsNSAware) { return mAttrs.getValue( mAttrs.getIndexNullNS(namespaceURI, localName)); } else { return (namespaceURI == null)? mAttrs.getValue(localName): null; } default: throw new IllegalStateException(FAULT); } } /** * Returns the count of attributes on this START_ELEMENT, * this method is only valid on a START_ELEMENT or ATTRIBUTE. This * count excludes namespace definitions. Attribute indices are * zero-based. * * @return returns the number of attributes * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE */ public int getAttributeCount() { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.ATTRIBUTE: return mAttrs.getLength(); default: throw new IllegalStateException(FAULT); } } /** * Returns the namespace of the attribute at the provided index. * * @param index the position of the attribute * @return the namespace URI (can be <code>null</code>) * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE */ public String getAttributeNamespace(int index) { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.ATTRIBUTE: String ns = mAttrs.getURI(index); if (ns == null) throw new IndexOutOfBoundsException(); return (ns.length() != 0)? ns: null; default: throw new IllegalStateException(FAULT); } } /** * Returns the localName of the attribute at the provided index. * * @param index the position of the attribute * @return the localName of the attribute * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE */ public String getAttributeLocalName(int index) { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.ATTRIBUTE: String name = (mIsNSAware != false)? mAttrs.getLocalName(index): mAttrs.getQName(index); if (name == null) throw new IndexOutOfBoundsException(); return name; default: throw new IllegalStateException(FAULT); } } /** * Returns the prefix of this attribute at the provided index. * * @param index the position of the attribute * @return the prefix of the attribute * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE */ public String getAttributePrefix(int index) { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.ATTRIBUTE: // See Attrs.java and Parser.attr() for details. String qname = mAttrs.getQName(index); // qName of the attribute if (qname == null) throw new IndexOutOfBoundsException(); int offset = qname.indexOf(':'); return (offset >= 0)? qname.substring(0, offset): null; default: throw new IllegalStateException(FAULT); } } /** * Returns the XML type of the attribute at the provided index. * * @param index the position of the attribute * @return the XML type of the attribute * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE */ public String getAttributeType(int index) { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.ATTRIBUTE: String type = mAttrs.getType(index); if (type == null) throw new IndexOutOfBoundsException(); return type; default: throw new IllegalStateException(FAULT); } } /** * Returns the value of the attribute at the index. * * @param index the position of the attribute * @return the attribute value * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE */ public String getAttributeValue(int index) { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.ATTRIBUTE: String value = mAttrs.getValue(index); if (value == null) throw new IndexOutOfBoundsException(); return value; default: throw new IllegalStateException(FAULT); } } /** * Returns a boolean which indicates if this attribute was created by default. * * @param index the position of the attribute * @return <code>true</code> if this is a default attribute * @throws IllegalStateException if this is not a START_ELEMENT or ATTRIBUTE */ public boolean isAttributeSpecified(int index) { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.ATTRIBUTE: if (index < 0 || index >= mAttrs.getLength()) throw new IndexOutOfBoundsException(); return !((Attributes2)mAttrs).isSpecified(index); default: throw new IllegalStateException(FAULT); } } /** * Returns the count of namespaces declared on this START_ELEMENT or * END_ELEMENT, this method is only valid on a START_ELEMENT, END_ELEMENT or * NAMESPACE. On an END_ELEMENT the count is of the namespaces that are about * to go out of scope. This is the equivalent of the information reported * by SAX callback for an end element event. * * @return returns the number of namespace declarations on this specific * element * @throws IllegalStateException if this is not a START_ELEMENT, END_ELEMENT * or NAMESPACE */ public int getNamespaceCount() { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.END_ELEMENT: case XMLStreamReader.NAMESPACE: return nsNum(); default: throw new IllegalStateException(FAULT); } } /** * Returns the prefix for the namespace declared at the index. Returns * <code>null</code> if this is the default namespace declaration * * @param index the position of the namespace declaration * @return returns the namespace prefix * @throws IllegalStateException if this is not a START_ELEMENT, END_ELEMENT * or NAMESPACE */ public String getNamespacePrefix(int index) { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.END_ELEMENT: case XMLStreamReader.NAMESPACE: break; default: throw new IllegalStateException(FAULT); } if (index < 0 || index >= nsNum()) throw new IndexOutOfBoundsException(); Pair pref = mPref; for (int idx = 0; idx < index; idx++) { pref = pref.next; } return (pref.name.length() != 0)? pref.name: null; } /** * Returns the uri for the namespace declared at the index. * * @param index the position of the namespace declaration * @return returns the namespace uri * @throws IllegalStateException if this is not a START_ELEMENT, END_ELEMENT * or NAMESPACE */ public String getNamespaceURI(int index) { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.END_ELEMENT: case XMLStreamReader.NAMESPACE: break; default: throw new IllegalStateException(FAULT); } if (nsNum() == 0) return null; if (index < 0 || index >= nsNum()) throw new IndexOutOfBoundsException(); Pair pref = mPref; for (int idx = 0; idx < index; idx++) { pref = pref.next; } return pref.value; } /** * Return the uri for the given prefix. * The uri returned depends on the current state of the processor. * * <p><strong>NOTE:</strong>The 'xml' prefix is bound as defined in * <a href="http://www.w3.org/TR/REC-xml-names/#ns-using"> * Namespaces in XML</a> specification to * "http://www.w3.org/XML/1998/namespace". * * <p><strong>NOTE:</strong> The 'xmlns' prefix must be resolved to following * namespace * <a href="http://www.w3.org/2000/xmlns/">http://www.w3.org/2000/xmlns/</a> * * @param prefix The prefix to lookup, may not be <code>null</code> * @return the uri bound to the given prefix or <code>null</code> if it is * not bound * @throws IllegalArgumentException if the prefix is <code>null</code> */ public String getNamespaceURI(String prefix) { if (prefix == null) throw new IllegalArgumentException(""); Pair pref = mPref; while (pref != null) { if (prefix.equals(pref.name)) return pref.value; pref = pref.next; } if (prefix.equals("xml")) return "http://www.w3.org/XML/1998/namespace"; if (prefix.equals("xmlns")) return "http://www.w3.org/2000/xmlns/"; return null; } /** * Returns an integer code that indicates the type of the event the cursor * is pointing to. */ public int getEventType() { return mEQhead.id; } /** * Returns the current value of the parse event as a string, this returns the * string value of a CHARACTERS event, returns the value of a COMMENT, the * replacement value for an ENTITY_REFERENCE, the string value of a CDATA * section, the string value for a SPACE event, or the String value of the * internal subset of the DTD. If an ENTITY_REFERENCE has been resolved, any * character data will be reported as CHARACTERS events. * * @return the current text or <code>null</code> * @throws java.lang.IllegalStateException if this state is not a valid * text state. */ public String getText() { switch (getEventType()) { case XMLStreamReader.COMMENT: case XMLStreamReader.CHARACTERS: case XMLStreamReader.SPACE: case XMLStreamReader.CDATA: if (mEQhead.value == null) mEQhead.value = new String(mEQhead.chars, 0, mEQhead.num); return mEQhead.value; case XMLStreamReader.DTD: if (mDTD != null) throw new IllegalStateException(FAULT); return (mEQhead.chars != null)? String.valueOf(mEQhead.chars): null; case XMLStreamReader.ENTITY_REFERENCE: return mEQhead.value; default: throw new IllegalStateException(FAULT); } } /** * Returns an array which contains the characters from this event. * This array should be treated as read-only and transient. I.e. the array * will contain the text characters until the XMLStreamReader moves on to the * next event. Attempts to hold onto the character array beyond that time or * modify the contents of the array are breaches of the contract for this * interface. * * @return the current text or an empty array * @throws java.lang.IllegalStateException if this state is not a valid * text state. */ public char[] getTextCharacters() { switch (getEventType()) { case XMLStreamReader.COMMENT: case XMLStreamReader.CHARACTERS: case XMLStreamReader.SPACE: case XMLStreamReader.CDATA: return mEQhead.chars; default: throw new IllegalStateException(FAULT); } } /** * Returns the offset into the text character array where the first character * (of this text event) is stored. * * @throws java.lang.IllegalStateException if this state is not a valid text * state. */ public int getTextStart() { switch (getEventType()) { case XMLStreamReader.COMMENT: case XMLStreamReader.CHARACTERS: case XMLStreamReader.SPACE: case XMLStreamReader.CDATA: return 0; default: throw new IllegalStateException(FAULT); } } /** * Returns the length of the sequence of characters for this Text event * within the text character array. * * @throws java.lang.IllegalStateException if this state is not a valid text * state. */ public int getTextLength() { switch (getEventType()) { case XMLStreamReader.COMMENT: case XMLStreamReader.CHARACTERS: case XMLStreamReader.SPACE: case XMLStreamReader.CDATA: return mEQhead.num; default: throw new IllegalStateException(FAULT); } } /** * Return input encoding if known or <code>null</code> if unknown. * * @return the encoding of this instance or <code>null</code> */ public String getEncoding() { return mEnc; } /** * Return the current location of the processor. * If the Location is unknown the processor should return an implementation * of Location that returns -1 for the location and <code>null</code> for the * publicId and systemId. The location information is only valid until next() * is called. */ public Location getLocation() { return this; } /** * Returns the (local) name of the current event. * For START_ELEMENT or END_ELEMENT returns the (local) name of the current * element. For ENTITY_REFERENCE it returns entity name. The current event * must be START_ELEMENT or END_ELEMENT or ENTITY_REFERENCE. * * @return the localName * @throws IllegalStateException if this not a START_ELEMENT, END_ELEMENT * or ENTITY_REFERENCE */ public String getLocalName() { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.END_ELEMENT: return mEQhead.list.name; case XMLStreamReader.ENTITY_REFERENCE: return mEQhead.name; default: throw new IllegalStateException(FAULT); } } /** * If the current event is a START_ELEMENT or END_ELEMENT this method * returns the URI of the prefix or the default namespace. * Returns <code>null</code> if the event does not have a prefix. * * @return the URI bound to this elements prefix, the default namespace, or * <code>null</code> */ public String getNamespaceURI() { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.END_ELEMENT: Pair elm = mEQhead.list; return (elm.ns != null && elm.ns.length() != 0)? elm.ns: null; case XMLStreamReader.NAMESPACE: return mEQhead.list.value; default: return null; } } /** * Returns the prefix of the current event or <code>null</code> if the event * does not have a prefix. * * @return the prefix or <code>null</code> */ public String getPrefix() { switch (getEventType()) { case XMLStreamReader.START_ELEMENT: case XMLStreamReader.END_ELEMENT: Pair elm = mEQhead.list; return (elm.chars[0] > 0)? new String(elm.chars, 1, elm.chars[0] - 1): null; default: throw new IllegalStateException(FAULT); } } /** * Get the xml version declared on the xml declaration. Returns * <code>null</code> if none was declared. * * @return the XML version or <code>null</code> */ public String getVersion() { // Current version of the parser does not support XML versions // other then 1.0. switch (mInp.xmlver) { case 0x0100: return "1.0"; default: return null; } } /** * Get the standalone declaration from the xml declaration. * * @return <code>true</code> if this is standalone, or <code>false</code> * otherwise */ public boolean isStandalone() { return mIsSAlone; } /** * Checks if standalone was set in the document. * * @return <code>true</code> if standalone was set in the document, or * <code>false</code> otherwise */ public boolean standaloneSet() { return mIsSAloneSet; } /** * Returns the character encoding declared on the xml declaration. * Returns <code>null</code> if none was declared. * * @return the encoding declared in the document or <code>null</code> */ public String getCharacterEncodingScheme() { // Returns encoding from XML declaration of current entity. // Therefore it might be encoding of the document entity or an // external parsed entity. return mInp.xmlenc; } /** * Get the target of a processing instruction. * * @return the target or <code>null</code> */ public String getPITarget() { if (getEventType() != XMLStreamReader.PROCESSING_INSTRUCTION) throw new IllegalStateException(FAULT); return mEQhead.name; } /** * Get the data section of a processing instruction. * * @return the data or <code>null</code> */ public String getPIData() { if (getEventType() != XMLStreamReader.PROCESSING_INSTRUCTION) throw new IllegalStateException(FAULT); return mEQhead.value; } /** * Return the line number where the current event ends, * returns -1 if none is available. * * @return the current line number */ public int getLineNumber() { return -1; } /** * Return the column number where the current event ends, * returns -1 if none is available. * * @return the current column number */ public int getColumnNumber() { return -1; } /** * Return the byte or character offset into the input source this location * is pointing to. If the input source is a file or a byte stream then * this is the byte offset into that stream, but if the input source is * a character media then the offset is the character offset. * Returns -1 if there is no offset available. * * @return the current offset */ public int getCharacterOffset() { return -1; } /** * Returns the public ID of the XML * * @return the public ID, or null if not available */ public String getPublicId() { return (mInp != null)? mInp.pubid: null; } /** * Returns the system ID of the XML * * @return the system ID, or null if not available */ public String getSystemId() { return (mInp != null)? mInp.sysid: null; } /** * Reports document type. * * @param name The name of the entity. * @param pubid The public identifier of the DTD or <code>null</code>. * @param sysid The system identifier of the DTD or <code>null</code>. * @param dtdint The DTD internal subset or <code>null</code>. */ protected void docType( String name, String pubid, String sysid, char[] dtdint) { Pair evt = pair(null); evt.id = XMLStreamReader.DTD; evt.name = name; evt.chars = dtdint; evt.list = pair(null); evt.list.name = pubid; evt.list.value = sysid; eqAdd(evt); } /** * Reports a comment. * * @param text The comment text starting from first character. * @param length The number of characters in comment. */ protected void comm(char[] text, int length) { Pair evt = pair(null); evt.id = XMLStreamReader.COMMENT; evt.chars = text; evt.num = length; if (mDTD != null) mDTD.eqAdd(evt); else eqAdd(evt); } /** * Reports a processing instruction. * * @param target The processing instruction target name. * @param body The processing instruction body text. */ protected void pi(String target, String body) throws Exception { Pair evt = pair(null); evt.id = XMLStreamReader.PROCESSING_INSTRUCTION; evt.name = target; evt.value = (body.length() != 0)? body: null; if (mDTD != null) mDTD.eqAdd(evt); else eqAdd(evt); } /** * Reports new namespace prefix. * The Namespace prefix (<code>mPref.name</code>) being declared and * the Namespace URI (<code>mPref.value</code>) the prefix is mapped * to. An empty string is used for the default element namespace, * which has no prefix. */ protected void newPrefix() throws Exception { Pair evt = pair(null); evt.id = XMLStreamReader.NAMESPACE; evt.list = mPref; eqAdd(evt); } /** * Reports skipped entity name. * * @param name The entity name. */ protected void skippedEnt(String name) throws Exception { if (name.charAt(0) != '%' && !name.equals("[dtd]")) panic(name); } /** * Returns an <code>InputSource</code> for specified entity or * <code>null</code>. * * @param name The name of the entity. * @param pubid The public identifier of the entity. * @param sysid The system identifier of the entity. */ protected InputSource resolveEnt(String name, String pubid, String sysid) throws XMLStreamException, IOException { if (mResolver != null) { InputStream is = (InputStream)mResolver.resolveEntity( pubid, sysid, null, null); return new InputSource(is); } return null; } /** * Reports internal parsed entity. * * @param name The entity name. * @param value The entity replacement text. */ protected void intparsedEntDecl(String name, char[] value) throws Exception { if (mDTD != null) { Pair evt = pair(null); evt.id = XMLStreamReader.ENTITY_DECLARATION; evt.name = name; evt.num = value.length; evt.chars = value; mDTD.eqAdd(evt); } } /** * Reports external parsed entity. * * @param name The entity name. * @param pubid The entity public identifier, may be null. * @param name The entity system identifier, may be null. */ protected void extparsedEntDecl(String name, String pubid, String sysid) throws Exception { if (mDTD != null) { Pair evt = pair(null); evt.id = XMLStreamReader.ENTITY_DECLARATION; evt.name = name; evt.num = -1; evt.chars = new char[0]; evt.list = pair(null); evt.list.name = pubid; evt.list.value = sysid; mDTD.eqAdd(evt); } } /** * Reports notation declaration. * * @param name The notation's name. * @param pubid The notation's public identifier, or <code>null</code> * if none was given. * @param sysid The notation's system identifier, or <code>null</code> * if none was given. */ protected void notDecl(String name, String pubid, String sysid) throws Exception { if (mDTD != null) { Pair evt = pair(null); evt.id = XMLStreamReader.NOTATION_DECLARATION; evt.name = name; evt.list = pair(null); evt.list.name = pubid; evt.list.value = sysid; mDTD.eqAdd(evt); } } /** * Reports unparsed entity name. * * @param name The unparsed entity's name. * @param pubid The entity's public identifier, or null if none was given. * @param sysid The entity's system identifier. * @param notation The name of the associated notation. */ protected void unparsedEntDecl( String name, String pubid, String sysid, String notation) throws Exception { if (mDTD != null) { Pair evt = pair(null); evt.id = XMLStreamReader.ENTITY_DECLARATION; evt.name = name; evt.value = notation; evt.list = pair(null); evt.list.name = pubid; evt.list.value = sysid; mDTD.eqAdd(evt); } } /** * Notifies the handler about fatal parsing error. * * @param msg The problem description message. */ protected void panic(String msg) throws XMLStreamException { throw new XMLStreamException(msg); } /** * Reports characters and empties the parser's buffer. * This method is called only if parser is going to return control to * the main loop. This means that this method may use parser buffer * to report white space without copying characters to temporary * buffer. */ protected void bflash() throws Exception { if (mBuffIdx < 0) // if the parser buffer is empty return; Pair evt = pair(null); evt.id = XMLStreamReader.CHARACTERS; evt.chars = mBuff; // character data evt.num = mBuffIdx + 1; // number of characters evt.name = ""; // non white space hint for isWhiteSpace eqAdd(evt); // Empty parser's buffer. mBuffIdx = -1; } /** * Reports white space characters and empties the parser's buffer. * This method is called only if parser is going to return control to * the main loop. This means that this method may use parser buffer * to report white space without copying characters to temporary * buffer. */ protected void bflash_ws() throws Exception { if (mBuffIdx < 0) // if the parser buffer is empty return; Pair evt = pair(null); evt.id = ((mElm.id & FLAG_XMLSPC_PRESERVE) != 0) ? XMLStreamReader.CHARACTERS : XMLStreamReader.SPACE; evt.chars = mBuff; // white space characters evt.num = mBuffIdx + 1; // number of characters evt.name = null; // white space hint for isWhiteSpace eqAdd(evt); // Empty parser's buffer. mBuffIdx = -1; } /** * Adds an event to the head of internal event queue. * * @param evt An event to add to the tail of the queue. */ private void eqAdd(Pair evt) { evt.next = null; if (mEQtail != null) mEQtail.next = evt; else mEQhead = evt; mEQtail = evt; } /** * Retrieves an event from internal event queue. * * @return An event or <code>null</code> if queue is empty. */ private Pair eqGet() { Pair evt = mEQhead; if (evt != null) { mEQhead = evt.next; evt.next = null; } if (mEQhead == null) mEQtail = null; return evt; } /** * Calculates number of namespaces defined on current element. * * @return A number of namespaces defined on current element. */ private int nsNum() { if (mNsNum < 0) { int count = 0; for(Pair nsdec = mPref; nsdec != null; nsdec = nsdec.next) { if (nsdec.list == mElm) count++; else break; } mNsNum = count; } return mNsNum; } }