diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:28:47 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:28:47 -0800 |
commit | adc854b798c1cfe3bfd4c27d68d5cee38ca617da (patch) | |
tree | 6aed8b4923ca428942cbaa7e848d50237a3d31e0 /xml/src/main/java/org/xmlpull | |
parent | 1c0fed63c71ddb230f3b304aac12caffbedf2f21 (diff) | |
download | libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.zip libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.tar.gz libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'xml/src/main/java/org/xmlpull')
-rw-r--r-- | xml/src/main/java/org/xmlpull/v1/XmlPullParser.java | 1116 | ||||
-rw-r--r-- | xml/src/main/java/org/xmlpull/v1/XmlPullParserException.java | 76 | ||||
-rw-r--r-- | xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java | 349 | ||||
-rw-r--r-- | xml/src/main/java/org/xmlpull/v1/XmlSerializer.java | 326 | ||||
-rw-r--r-- | xml/src/main/java/org/xmlpull/v1/sax2/Driver.java | 469 |
5 files changed, 2336 insertions, 0 deletions
diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java new file mode 100644 index 0000000..2c2946f --- /dev/null +++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java @@ -0,0 +1,1116 @@ +/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/ +// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/) + +package org.xmlpull.v1; + +import java.io.InputStream; +import java.io.IOException; +import java.io.Reader; + +/** + * XML Pull Parser is an interface that defines parsing functionlity provided + * in <a href="http://www.xmlpull.org/">XMLPULL V1 API</a> (visit this website to + * learn more about API and its implementations). + * + * <p>There are following different + * kinds of parser depending on which features are set:<ul> + * <li><b>non-validating</b> parser as defined in XML 1.0 spec when + * FEATURE_PROCESS_DOCDECL is set to true + * <li><b>validating parser</b> as defined in XML 1.0 spec when + * FEATURE_VALIDATION is true (and that implies that FEATURE_PROCESS_DOCDECL is true) + * <li>when FEATURE_PROCESS_DOCDECL is false (this is default and + * if different value is required necessary must be changed before parsing is started) + * then parser behaves like XML 1.0 compliant non-validating parser under condition that + * <em>no DOCDECL is present</em> in XML documents + * (internal entites can still be defined with defineEntityReplacementText()). + * This mode of operation is intened <b>for operation in constrained environments</b> such as J2ME. + * </ul> + * + * + * <p>There are two key methods: next() and nextToken(). While next() provides + * access to high level parsing events, nextToken() allows access to lower + * level tokens. + * + * <p>The current event state of the parser + * can be determined by calling the + * <a href="#getEventType()">getEventType()</a> method. + * Initially, the parser is in the <a href="#START_DOCUMENT">START_DOCUMENT</a> + * state. + * + * <p>The method <a href="#next()">next()</a> advances the parser to the + * next event. The int value returned from next determines the current parser + * state and is identical to the value returned from following calls to + * getEventType (). + * + * <p>Th following event types are seen by next()<dl> + * <dt><a href="#START_TAG">START_TAG</a><dd> An XML start tag was read. + * <dt><a href="#TEXT">TEXT</a><dd> Text content was read; + * the text content can be retreived using the getText() method. + * (when in validating mode next() will not report ignorable whitespaces, use nextToken() instead) + * <dt><a href="#END_TAG">END_TAG</a><dd> An end tag was read + * <dt><a href="#END_DOCUMENT">END_DOCUMENT</a><dd> No more events are available + * </dl> + * + * <p>after first next() or nextToken() (or any other next*() method) + * is called user application can obtain + * XML version, standalone and encoding from XML declaration + * in following ways:<ul> + * <li><b>version</b>: + * getProperty("<a href="http://xmlpull.org/v1/doc/properties.html#xmldecl-version">http://xmlpull.org/v1/doc/properties.html#xmldecl-version</a>") + * returns String ("1.0") or null if XMLDecl was not read or if property is not supported + * <li><b>standalone</b>: + * getProperty("<a href="http://xmlpull.org/v1/doc/features.html#xmldecl-standalone">http://xmlpull.org/v1/doc/features.html#xmldecl-standalone</a>") + * returns Boolean: null if there was no standalone declaration + * or if property is not supported + * otherwise returns Boolean(true) if standalon="yes" and Boolean(false) when standalone="no" + * <li><b>encoding</b>: obtained from getInputEncoding() + * null if stream had unknown encoding (not set in setInputStream) + * and it was not declared in XMLDecl + * </ul> + * + * A minimal example for using this API may look as follows: + * <pre> + * import java.io.IOException; + * import java.io.StringReader; + * + * import org.xmlpull.v1.XmlPullParser; + * import org.xmlpull.v1.<a href="XmlPullParserException.html">XmlPullParserException.html</a>; + * import org.xmlpull.v1.<a href="XmlPullParserFactory.html">XmlPullParserFactory</a>; + * + * public class SimpleXmlPullApp + * { + * + * public static void main (String args[]) + * throws XmlPullParserException, IOException + * { + * XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + * factory.setNamespaceAware(true); + * XmlPullParser xpp = factory.newPullParser(); + * + * xpp.<a href="#setInput">setInput</a>( new StringReader ( "<foo>Hello World!</foo>" ) ); + * int eventType = xpp.getEventType(); + * while (eventType != XmlPullParser.END_DOCUMENT) { + * if(eventType == XmlPullParser.START_DOCUMENT) { + * System.out.println("Start document"); + * } else if(eventType == XmlPullParser.END_DOCUMENT) { + * System.out.println("End document"); + * } else if(eventType == XmlPullParser.START_TAG) { + * System.out.println("Start tag "+xpp.<a href="#getName()">getName()</a>); + * } else if(eventType == XmlPullParser.END_TAG) { + * System.out.println("End tag "+xpp.getName()); + * } else if(eventType == XmlPullParser.TEXT) { + * System.out.println("Text "+xpp.<a href="#getText()">getText()</a>); + * } + * eventType = xpp.next(); + * } + * } + * } + * </pre> + * + * <p>The above example will generate the following output: + * <pre> + * Start document + * Start tag foo + * Text Hello World! + * End tag foo + * </pre> + * + * <p>For more details on API usage, please refer to the + * quick Introduction available at <a href="http://www.xmlpull.org">http://www.xmlpull.org</a> + * + * @see XmlPullParserFactory + * @see #defineEntityReplacementText + * @see #getName + * @see #getNamespace + * @see #getText + * @see #next + * @see #nextToken + * @see #setInput + * @see #FEATURE_PROCESS_DOCDECL + * @see #FEATURE_VALIDATION + * @see #START_DOCUMENT + * @see #START_TAG + * @see #TEXT + * @see #END_TAG + * @see #END_DOCUMENT + * + * @author <a href="http://www-ai.cs.uni-dortmund.de/PERSONAL/haustein.html">Stefan Haustein</a> + * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a> + */ + +public interface XmlPullParser { + + /** This constant represents the default namespace (empty string "") */ + String NO_NAMESPACE = ""; + + // ---------------------------------------------------------------------------- + // EVENT TYPES as reported by next() + + /** + * Signalize that parser is at the very beginning of the document + * and nothing was read yet. + * This event type can only be observed by calling getEvent() + * before the first call to next(), nextToken, or nextTag()</a>). + * + * @see #next + * @see #nextToken + */ + int START_DOCUMENT = 0; + + /** + * Logical end of the xml document. Returned from getEventType, next() + * and nextToken() + * when the end of the input document has been reached. + * <p><strong>NOTE:</strong> calling again + * <a href="#next()">next()</a> or <a href="#nextToken()">nextToken()</a> + * will result in exception being thrown. + * + * @see #next + * @see #nextToken + */ + int END_DOCUMENT = 1; + + /** + * Returned from getEventType(), + * <a href="#next()">next()</a>, <a href="#nextToken()">nextToken()</a> when + * a start tag was read. + * The name of start tag is available from getName(), its namespace and prefix are + * available from getNamespace() and getPrefix() + * if <a href='#FEATURE_PROCESS_NAMESPACES'>namespaces are enabled</a>. + * See getAttribute* methods to retrieve element attributes. + * See getNamespace* methods to retrieve newly declared namespaces. + * + * @see #next + * @see #nextToken + * @see #getName + * @see #getPrefix + * @see #getNamespace + * @see #getAttributeCount + * @see #getDepth + * @see #getNamespaceCount + * @see #getNamespace + * @see #FEATURE_PROCESS_NAMESPACES + */ + int START_TAG = 2; + + /** + * Returned from getEventType(), <a href="#next()">next()</a>, or + * <a href="#nextToken()">nextToken()</a> when an end tag was read. + * The name of start tag is available from getName(), its + * namespace and prefix are + * available from getNamespace() and getPrefix(). + * + * @see #next + * @see #nextToken + * @see #getName + * @see #getPrefix + * @see #getNamespace + * @see #FEATURE_PROCESS_NAMESPACES + */ + int END_TAG = 3; + + + /** + * Character data was read and will is available by calling getText(). + * <p><strong>Please note:</strong> <a href="#next()">next()</a> will + * accumulate multiple + * events into one TEXT event, skipping IGNORABLE_WHITESPACE, + * PROCESSING_INSTRUCTION and COMMENT events, + * In contrast, <a href="#nextToken()">nextToken()</a> will stop reading + * text when any other event is observed. + * Also, when the state was reached by calling next(), the text value will + * be normalized, whereas getText() will + * return unnormalized content in the case of nextToken(). This allows + * an exact roundtrip without chnanging line ends when examining low + * level events, whereas for high level applications the text is + * normalized apropriately. + * + * @see #next + * @see #nextToken + * @see #getText + */ + int TEXT = 4; + + // ---------------------------------------------------------------------------- + // additional events exposed by lower level nextToken() + + /** + * A CDATA sections was just read; + * this token is available only from calls to <a href="#nextToken()">nextToken()</a>. + * A call to next() will accumulate various text events into a single event + * of type TEXT. The text contained in the CDATA section is available + * by callling getText(). + * + * @see #nextToken + * @see #getText + */ + int CDSECT = 5; + + /** + * An entity reference was just read; + * this token is available from <a href="#nextToken()">nextToken()</a> + * only. The entity name is available by calling getName(). If available, + * the replacement text can be obtained by calling getTextt(); otherwise, + * the user is responsibile for resolving the entity reference. + * This event type is never returned from next(); next() will + * accumulate the replacement text and other text + * events to a single TEXT event. + * + * @see #nextToken + * @see #getText + */ + int ENTITY_REF = 6; + + /** + * Ignorable whitespace was just read. + * This token is available only from <a href="#nextToken()">nextToken()</a>). + * For non-validating + * parsers, this event is only reported by nextToken() when outside + * the root element. + * Validating parsers may be able to detect ignorable whitespace at + * other locations. + * The ignorable whitespace string is available by calling getText() + * + * <p><strong>NOTE:</strong> this is different from calling the + * isWhitespace() method, since text content + * may be whitespace but not ignorable. + * + * Ignorable whitespace is skipped by next() automatically; this event + * type is never returned from next(). + * + * @see #nextToken + * @see #getText + */ + int IGNORABLE_WHITESPACE = 7; + + /** + * An XML processing instruction declaration was just read. This + * event type is available only via <a href="#nextToken()">nextToken()</a>. + * getText() will return text that is inside the processing instruction. + * Calls to next() will skip processing instructions automatically. + * @see #nextToken + * @see #getText + */ + int PROCESSING_INSTRUCTION = 8; + + /** + * An XML comment was just read. This event type is this token is + * available via <a href="#nextToken()">nextToken()</a> only; + * calls to next() will skip comments automatically. + * The content of the comment can be accessed using the getText() + * method. + * + * @see #nextToken + * @see #getText + */ + int COMMENT = 9; + + /** + * An XML document type declaration was just read. This token is + * available from <a href="#nextToken()">nextToken()</a> only. + * The unparsed text inside the doctype is available via + * the getText() method. + * + * @see #nextToken + * @see #getText + */ + int DOCDECL = 10; + + /** + * This array can be used to convert the event type integer constants + * such as START_TAG or TEXT to + * to a string. For example, the value of TYPES[START_TAG] is + * the string "START_TAG". + * + * This array is intended for diagnostic output only. Relying + * on the contents of the array may be dangerous since malicous + * applications may alter the array, although it is final, due + * to limitations of the Java language. + */ + String [] TYPES = { + "START_DOCUMENT", + "END_DOCUMENT", + "START_TAG", + "END_TAG", + "TEXT", + "CDSECT", + "ENTITY_REF", + "IGNORABLE_WHITESPACE", + "PROCESSING_INSTRUCTION", + "COMMENT", + "DOCDECL" + }; + + + // ---------------------------------------------------------------------------- + // namespace related features + + /** + * This feature determines whether the parser processes + * namespaces. As for all features, the default value is false. + * <p><strong>NOTE:</strong> The value can not be changed during + * parsing an must be set before parsing. + * + * @see #getFeature + * @see #setFeature + */ + String FEATURE_PROCESS_NAMESPACES = + "http://xmlpull.org/v1/doc/features.html#process-namespaces"; + + /** + * This feature determines whether namespace attributes are + * exposed via the attribute access methods. Like all features, + * the default value is false. This feature cannot be changed + * during parsing. + * + * @see #getFeature + * @see #setFeature + */ + String FEATURE_REPORT_NAMESPACE_ATTRIBUTES = + "http://xmlpull.org/v1/doc/features.html#report-namespace-prefixes"; + + /** + * This feature determines whether the document declaration + * is processed. If set to false, + * the DOCDECL event type is reported by nextToken() + * and ignored by next(). + * + * If this featue is activated, then the document declaration + * must be processed by the parser. + * + * <p><strong>Please note:</strong> If the document type declaration + * was ignored, entity references may cause exceptions + * later in the parsing process. + * The default value of this feature is false. It cannot be changed + * during parsing. + * + * @see #getFeature + * @see #setFeature + */ + String FEATURE_PROCESS_DOCDECL = + "http://xmlpull.org/v1/doc/features.html#process-docdecl"; + + /** + * If this feature is activated, all validation errors as + * defined in the XML 1.0 sepcification are reported. + * This implies that FEATURE_PROCESS_DOCDECL is true and both, the + * internal and external document type declaration will be processed. + * <p><strong>Please Note:</strong> This feature can not be changed + * during parsing. The default value is false. + * + * @see #getFeature + * @see #setFeature + */ + String FEATURE_VALIDATION = + "http://xmlpull.org/v1/doc/features.html#validation"; + + /** + * Use this call to change the general behaviour of the parser, + * such as namespace processing or doctype declaration handling. + * This method must be called before the first call to next or + * nextToken. Otherwise, an exception is thrown. + * <p>Example: call setFeature(FEATURE_PROCESS_NAMESPACES, true) in order + * to switch on namespace processing. The initial settings correspond + * to the properties requested from the XML Pull Parser factory. + * If none were requested, all feautures are deactivated by default. + * + * @exception XmlPullParserException If the feature is not supported or can not be set + * @exception IllegalArgumentException If string with the feature name is null + */ + void setFeature(String name, + boolean state) throws XmlPullParserException; + + /** + * Returns the current value of the given feature. + * <p><strong>Please note:</strong> unknown features are + * <strong>always</strong> returned as false. + * + * @param name The name of feature to be retrieved. + * @return The value of the feature. + * @exception IllegalArgumentException if string the feature name is null + */ + + boolean getFeature(String name); + + /** + * Set the value of a property. + * + * The property name is any fully-qualified URI. + * + * @exception XmlPullParserException If the property is not supported or can not be set + * @exception IllegalArgumentException If string with the property name is null + */ + void setProperty(String name, + Object value) throws XmlPullParserException; + + /** + * Look up the value of a property. + * + * The property name is any fully-qualified URI. + * <p><strong>NOTE:</strong> unknown properties are <strong>always</strong> + * returned as null. + * + * @param name The name of property to be retrieved. + * @return The value of named property. + */ + Object getProperty(String name); + + + /** + * Set the input source for parser to the given reader and + * resets the parser. The event type is set to the initial value + * START_DOCUMENT. + * Setting the reader to null will just stop parsing and + * reset parser state, + * allowing the parser to free internal resources + * such as parsing buffers. + */ + void setInput(Reader in) throws XmlPullParserException; + + + /** + * Sets the input stream the parser is going to process. + * This call resets the parser state and sets the event type + * to the initial value START_DOCUMENT. + * + * <p><strong>NOTE:</strong> If an input encoding string is passed, + * it MUST be used. Otherwise, + * if inputEncoding is null, the parser SHOULD try to determine + * input encoding following XML 1.0 specification (see below). + * If encoding detection is supported then following feature + * <a href="http://xmlpull.org/v1/doc/features.html#detect-encoding">http://xmlpull.org/v1/doc/features.html#detect-encoding</a> + * MUST be true amd otherwise it must be false + * + * @param inputStream contains a raw byte input stream of possibly + * unknown encoding (when inputEncoding is null). + * + * @param inputEncoding if not null it MUST be used as encoding for inputStream + */ + void setInput(InputStream inputStream, String inputEncoding) + throws XmlPullParserException; + + /** + * Returns the input encoding if known, null otherwise. + * If setInput(InputStream, inputEncoding) was called with an inputEncoding + * value other than null, this value must be returned + * from this method. Otherwise, if inputEncoding is null and + * the parser suppports the encoding detection feature + * (http://xmlpull.org/v1/doc/features.html#detect-encoding), + * it must return the detected encoding. + * If setInput(Reader) was called, null is returned. + * After first call to next if XML declaration was present this method + * will return encoding declared. + */ + String getInputEncoding(); + + /** + * Set new value for entity replacement text as defined in + * <a href="http://www.w3.org/TR/REC-xml#intern-replacement">XML 1.0 Section 4.5 + * Construction of Internal Entity Replacement Text</a>. + * If FEATURE_PROCESS_DOCDECL or FEATURE_VALIDATION are set, calling this + * function will result in an exception -- when processing of DOCDECL is + * enabled, there is no need to the entity replacement text manually. + * + * <p>The motivation for this function is to allow very small + * implementations of XMLPULL that will work in J2ME environments. + * Though these implementations may not be able to process the document type + * declaration, they still can work with known DTDs by using this function. + * + * <p><b>Please notes:</b> The given value is used literally as replacement text + * and it corresponds to declaring entity in DTD that has all special characters + * escaped: left angle bracket is replaced with &lt;, ampersnad with &amp; + * and so on. + * + * <p><b>Note:</b> The given value is the literal replacement text and must not + * contain any other entity reference (if it contains any entity reference + * there will be no further replacement). + * + * <p><b>Note:</b> The list of pre-defined entity names will + * always contain standard XML entities such as + * amp (&amp;), lt (&lt;), gt (&gt;), quot (&quot;), and apos (&apos;). + * Those cannot be redefined by this method! + * + * @see #setInput + * @see #FEATURE_PROCESS_DOCDECL + * @see #FEATURE_VALIDATION + */ + void defineEntityReplacementText( String entityName, + String replacementText ) throws XmlPullParserException; + + /** + * Returns the numbers of elements in the namespace stack for the given + * depth. + * If namespaces are not enabled, 0 is returned. + * + * <p><b>NOTE:</b> when parser is on END_TAG then it is allowed to call + * this function with getDepth()+1 argument to retrieve position of namespace + * prefixes and URIs that were declared on corresponding START_TAG. + * <p><b>NOTE:</b> to retrieve lsit of namespaces declared in current element:<pre> + * XmlPullParser pp = ... + * int nsStart = pp.getNamespaceCount(pp.getDepth()-1); + * int nsEnd = pp.getNamespaceCount(pp.getDepth()); + * for (int i = nsStart; i < nsEnd; i++) { + * String prefix = pp.getNamespacePrefix(i); + * String ns = pp.getNamespaceUri(i); + * // ... + * } + * </pre> + * + * @see #getNamespacePrefix + * @see #getNamespaceUri + * @see #getNamespace() + * @see #getNamespace(String) + */ + int getNamespaceCount(int depth) throws XmlPullParserException; + + /** + * Returns the namespace prefixe for the given position + * in the namespace stack. + * Default namespace declaration (xmlns='...') will have null as prefix. + * If the given index is out of range, an exception is thrown. + * <p><b>Please note:</b> when the parser is on an END_TAG, + * namespace prefixes that were declared + * in the corresponding START_TAG are still accessible + * although they are no longer in scope. + */ + String getNamespacePrefix(int pos) throws XmlPullParserException; + + /** + * Returns the namespace URI for the given position in the + * namespace stack + * If the position is out of range, an exception is thrown. + * <p><b>NOTE:</b> when parser is on END_TAG then namespace prefixes that were declared + * in corresponding START_TAG are still accessible even though they are not in scope + */ + String getNamespaceUri(int pos) throws XmlPullParserException; + + /** + * Returns the URI corresponding to the given prefix, + * depending on current state of the parser. + * + * <p>If the prefix was not declared in the current scope, + * null is returned. The default namespace is included + * in the namespace table and is available via + * getNamespace (null). + * + * <p>This method is a convenience method for + * + * <pre> + * for (int i = getNamespaceCount(getDepth ())-1; i >= 0; i--) { + * if (getNamespacePrefix(i).equals( prefix )) { + * return getNamespaceUri(i); + * } + * } + * return null; + * </pre> + * + * <p><strong>Please note:</strong> parser implementations + * may provide more efifcient lookup, e.g. using a Hashtable. + * The 'xml' prefix is bound to "http://www.w3.org/XML/1998/namespace", as + * defined in the + * <a href="http://www.w3.org/TR/REC-xml-names/#ns-using">Namespaces in XML</a> + * specification. Analogous, the 'xmlns' prefix is resolved to + * <a href="http://www.w3.org/2000/xmlns/">http://www.w3.org/2000/xmlns/</a> + * + * @see #getNamespaceCount + * @see #getNamespacePrefix + * @see #getNamespaceUri + */ + String getNamespace (String prefix); + + + // -------------------------------------------------------------------------- + // miscellaneous reporting methods + + /** + * Returns the current depth of the element. + * Outside the root element, the depth is 0. The + * depth is incremented by 1 when a start tag is reached. + * The depth is decremented AFTER the end tag + * event was observed. + * + * <pre> + * <!-- outside --> 0 + * <root> 1 + * sometext 1 + * <foobar> 2 + * </foobar> 2 + * </root> 1 + * <!-- outside --> 0 + * </pre> + */ + int getDepth(); + + /** + * Returns a short text describing the current parser state, including + * the position, a + * description of the current event and the data source if known. + * This method is especially useful to provide meaningful + * error messages and for debugging purposes. + */ + String getPositionDescription (); + + + /** + * Returns the current line number, starting from 1. + * When the parser does not know the current line number + * or can not determine it, -1 is returned (e.g. for WBXML). + * + * @return current line number or -1 if unknown. + */ + int getLineNumber(); + + /** + * Returns the current column number, starting from 0. + * When the parser does not know the current column number + * or can not determine it, -1 is returned (e.g. for WBXML). + * + * @return current column number or -1 if unknown. + */ + int getColumnNumber(); + + + // -------------------------------------------------------------------------- + // TEXT related methods + + /** + * Checks whether the current TEXT event contains only whitespace + * characters. + * For IGNORABLE_WHITESPACE, this is always true. + * For TEXT and CDSECT, false is returned when the current event text + * contains at least one non-white space character. For any other + * event type an exception is thrown. + * + * <p><b>Please note:</b> non-validating parsers are not + * able to distinguish whitespace and ignorable whitespace, + * except from whitespace outside the root element. Ignorable + * whitespace is reported as separate event, which is exposed + * via nextToken only. + * + */ + boolean isWhitespace() throws XmlPullParserException; + + /** + * Returns the text content of the current event as String. + * The value returned depends on current event type, + * for example for TEXT event it is element content + * (this is typical case when next() is used). + * + * See description of nextToken() for detailed description of + * possible returned values for different types of events. + * + * <p><strong>NOTE:</strong> in case of ENTITY_REF, this method returns + * the entity replacement text (or null if not available). This is + * the only case where + * getText() and getTextCharacters() return different values. + * + * @see #getEventType + * @see #next + * @see #nextToken + */ + String getText (); + + + /** + * Returns the buffer that contains the text of the current event, + * as well as the start offset and length relevant for the current + * event. See getText(), next() and nextToken() for description of possible returned values. + * + * <p><strong>Please note:</strong> this buffer must not + * be modified and its content MAY change after a call to + * next() or nextToken(). This method will always return the + * same value as getText(), except for ENTITY_REF. In the case + * of ENTITY ref, getText() returns the replacement text and + * this method returns the actual input buffer containing the + * entity name. + * If getText() returns null, this method returns null as well and + * the values returned in the holder array MUST be -1 (both start + * and length). + * + * @see #getText + * @see #next + * @see #nextToken + * + * @param holderForStartAndLength Must hold an 2-element int array + * into which the start offset and length values will be written. + * @return char buffer that contains the text of the current event + * (null if the current event has no text associated). + */ + char[] getTextCharacters(int [] holderForStartAndLength); + + // -------------------------------------------------------------------------- + // START_TAG / END_TAG shared methods + + /** + * Returns the namespace URI of the current element. + * The default namespace is represented + * as empty string. + * If namespaces are not enabled, an empty String ("") is always returned. + * The current event must be START_TAG or END_TAG; otherwise, + * null is returned. + */ + String getNamespace (); + + /** + * For START_TAG or END_TAG events, the (local) name of the current + * element is returned when namespaces are enabled. When namespace + * processing is disabled, the raw name is returned. + * For ENTITY_REF events, the entity name is returned. + * If the current event is not START_TAG, END_TAG, or ENTITY_REF, + * null is returned. + * <p><b>Please note:</b> To reconstruct the raw element name + * when namespaces are enabled and the prefix is not null, + * you will need to add the prefix and a colon to localName.. + * + */ + String getName(); + + /** + * Returns the prefix of the current element. + * If the element is in the default namespace (has no prefix), + * null is returned. + * If namespaces are not enabled, or the current event + * is not START_TAG or END_TAG, null is returned. + */ + String getPrefix(); + + /** + * Returns true if the current event is START_TAG and the tag + * is degenerated + * (e.g. <foobar/>). + * <p><b>NOTE:</b> if the parser is not on START_TAG, an exception + * will be thrown. + */ + boolean isEmptyElementTag() throws XmlPullParserException; + + // -------------------------------------------------------------------------- + // START_TAG Attributes retrieval methods + + /** + * Returns the number of attributes of the current start tag, or + * -1 if the current event type is not START_TAG + * + * @see #getAttributeNamespace + * @see #getAttributeName + * @see #getAttributePrefix + * @see #getAttributeValue + */ + int getAttributeCount(); + + /** + * Returns the namespace URI of the attribute + * with the given index (starts from 0). + * Returns an empty string ("") if namespaces are not enabled + * or the attribute has no namespace. + * Throws an IndexOutOfBoundsException if the index is out of range + * or the current event type is not START_TAG. + * + * <p><strong>NOTE:</strong> if FEATURE_REPORT_NAMESPACE_ATTRIBUTES is set + * then namespace attributes (xmlns:ns='...') must be reported + * with namespace + * <a href="http://www.w3.org/2000/xmlns/">http://www.w3.org/2000/xmlns/</a> + * (visit this URL for description!). + * The default namespace attribute (xmlns="...") will be reported with empty namespace. + * <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". + * + * @param index zero-based index of attribute + * @return attribute namespace, + * empty string ("") is returned if namesapces processing is not enabled or + * namespaces processing is enabled but attribute has no namespace (it has no prefix). + */ + String getAttributeNamespace (int index); + + /** + * Returns the local name of the specified attribute + * if namespaces are enabled or just attribute name if namespaces are disabled. + * Throws an IndexOutOfBoundsException if the index is out of range + * or current event type is not START_TAG. + * + * @param index zero-based index of attribute + * @return attribute name (null is never returned) + */ + String getAttributeName (int index); + + /** + * Returns the prefix of the specified attribute + * Returns null if the element has no prefix. + * If namespaces are disabled it will always return null. + * Throws an IndexOutOfBoundsException if the index is out of range + * or current event type is not START_TAG. + * + * @param index zero-based index of attribute + * @return attribute prefix or null if namespaces processing is not enabled. + */ + String getAttributePrefix(int index); + + /** + * Returns the type of the specified attribute + * If parser is non-validating it MUST return CDATA. + * + * @param index zero-based index of attribute + * @return attribute type (null is never returned) + */ + String getAttributeType(int index); + + /** + * Returns if the specified attribute was not in input was declared in XML. + * If parser is non-validating it MUST always return false. + * This information is part of XML infoset: + * + * @param index zero-based index of attribute + * @return false if attribute was in input + */ + boolean isAttributeDefault(int index); + + /** + * Returns the given attributes value. + * Throws an IndexOutOfBoundsException if the index is out of range + * or current event type is not START_TAG. + * + * <p><strong>NOTE:</strong> attribute value must be normalized + * (including entity replacement text if PROCESS_DOCDECL is false) as described in + * <a href="http://www.w3.org/TR/REC-xml#AVNormalize">XML 1.0 section + * 3.3.3 Attribute-Value Normalization</a> + * + * @see #defineEntityReplacementText + * + * @param index zero-based index of attribute + * @return value of attribute (null is never returned) + */ + String getAttributeValue(int index); + + /** + * Returns the attributes value identified by namespace URI and namespace localName. + * If namespaces are disabled namespace must be null. + * If current event type is not START_TAG then IndexOutOfBoundsException will be thrown. + * + * <p><strong>NOTE:</strong> attribute value must be normalized + * (including entity replacement text if PROCESS_DOCDECL is false) as described in + * <a href="http://www.w3.org/TR/REC-xml#AVNormalize">XML 1.0 section + * 3.3.3 Attribute-Value Normalization</a> + * + * @see #defineEntityReplacementText + * + * @param namespace Namespace of the attribute if namespaces are enabled otherwise must be null + * @param name If namespaces enabled local name of attribute otherwise just attribute name + * @return value of attribute or null if attribute with given name does not exist + */ + String getAttributeValue(String namespace, + String name); + + // -------------------------------------------------------------------------- + // actual parsing methods + + /** + * Returns the type of the current event (START_TAG, END_TAG, TEXT, etc.) + * + * @see #next() + * @see #nextToken() + */ + int getEventType() + throws XmlPullParserException; + + /** + * Get next parsing event - element content wil be coalesced and only one + * TEXT event must be returned for whole element content + * (comments and processing instructions will be ignored and emtity references + * must be expanded or exception mus be thrown if entity reerence can not be exapnded). + * If element content is empty (content is "") then no TEXT event will be reported. + * + * <p><b>NOTE:</b> empty element (such as <tag/>) will be reported + * with two separate events: START_TAG, END_TAG - it must be so to preserve + * parsing equivalency of empty element to <tag></tag>. + * (see isEmptyElementTag ()) + * + * @see #isEmptyElementTag + * @see #START_TAG + * @see #TEXT + * @see #END_TAG + * @see #END_DOCUMENT + */ + + int next() + throws XmlPullParserException, IOException; + + + /** + * This method works similarly to next() but will expose + * additional event types (COMMENT, CDSECT, DOCDECL, ENTITY_REF, PROCESSING_INSTRUCTION, or + * IGNORABLE_WHITESPACE) if they are available in input. + * + * <p>If special feature + * <a href="http://xmlpull.org/v1/doc/features.html#xml-roundtrip">FEATURE_XML_ROUNDTRIP</a> + * (identified by URI: http://xmlpull.org/v1/doc/features.html#xml-roundtrip) + * is enabled it is possible to do XML document round trip ie. reproduce + * exectly on output the XML input using getText(): + * returned content is always unnormalized (exactly as in input). + * Otherwise returned content is end-of-line normalized as described + * <a href="http://www.w3.org/TR/REC-xml#sec-line-ends">XML 1.0 End-of-Line Handling</a> + * and. Also when this feature is enabled exact content of START_TAG, END_TAG, + * DOCDECL and PROCESSING_INSTRUCTION is available. + * + * <p>Here is the list of tokens that can be returned from nextToken() + * and what getText() and getTextCharacters() returns:<dl> + * <dt>START_DOCUMENT<dd>null + * <dt>END_DOCUMENT<dd>null + * <dt>START_TAG<dd>null unless FEATURE_XML_ROUNDTRIP + * enabled and then returns XML tag, ex: <tag attr='val'> + * <dt>END_TAG<dd>null unless FEATURE_XML_ROUNDTRIP + * id enabled and then returns XML tag, ex: </tag> + * <dt>TEXT<dd>return element content. + * <br>Note: that element content may be delivered in multiple consecutive TEXT events. + * <dt>IGNORABLE_WHITESPACE<dd>return characters that are determined to be ignorable white + * space. If the FEATURE_XML_ROUNDTRIP is enabled all whitespace content outside root + * element will always reported as IGNORABLE_WHITESPACE otherise rteporting is optional. + * <br>Note: that element content may be delevered in multiple consecutive IGNORABLE_WHITESPACE events. + * <dt>CDSECT<dd> + * return text <em>inside</em> CDATA + * (ex. 'fo<o' from <!CDATA[fo<o]]>) + * <dt>PROCESSING_INSTRUCTION<dd> + * if FEATURE_XML_ROUNDTRIP is true + * return exact PI content ex: 'pi foo' from <?pi foo?> + * otherwise it may be exact PI content or concatenation of PI target, + * space and data so for example for + * <?target data?> string "target data" may + * be returned if FEATURE_XML_ROUNDTRIP is false. + * <dt>COMMENT<dd>return comment content ex. 'foo bar' from <!--foo bar--> + * <dt>ENTITY_REF<dd>getText() MUST return entity replacement text if PROCESS_DOCDECL is false + * otherwise getText() MAY return null, + * additionally getTextCharacters() MUST return entity name + * (for example 'entity_name' for &entity_name;). + * <br><b>NOTE:</b> this is the only place where value returned from getText() and + * getTextCharacters() <b>are different</b> + * <br><b>NOTE:</b> it is user responsibility to resolve entity reference + * if PROCESS_DOCDECL is false and there is no entity replacement text set in + * defineEntityReplacementText() method (getText() will be null) + * <br><b>NOTE:</b> character entities (ex. &#32;) and standard entities such as + * &amp; &lt; &gt; &quot; &apos; are reported as well + * and are <b>not</b> reported as TEXT tokens but as ENTITY_REF tokens! + * This requirement is added to allow to do roundtrip of XML documents! + * <dt>DOCDECL<dd> + * if FEATURE_XML_ROUNDTRIP is true or PROCESS_DOCDECL is false + * then return what is inside of DOCDECL for example it returns:<pre> + * " titlepage SYSTEM "http://www.foo.bar/dtds/typo.dtd" + * [<!ENTITY % active.links "INCLUDE">]"</pre> + * <p>for input document that contained:<pre> + * <!DOCTYPE titlepage SYSTEM "http://www.foo.bar/dtds/typo.dtd" + * [<!ENTITY % active.links "INCLUDE">]></pre> + * otherwise if FEATURE_XML_ROUNDTRIP is false and PROCESS_DOCDECL is true + * then what is returned is undefined (it may be even null) + * </dd> + * </dl> + * + * <p><strong>NOTE:</strong> there is no gurantee that there will only one TEXT or + * IGNORABLE_WHITESPACE event from nextToken() as parser may chose to deliver element content in + * multiple tokens (dividing element content into chunks) + * + * <p><strong>NOTE:</strong> whether returned text of token is end-of-line normalized + * is depending on FEATURE_XML_ROUNDTRIP. + * + * <p><strong>NOTE:</strong> XMLDecl (<?xml ...?>) is not reported but its content + * is available through optional properties (see class description above). + * + * @see #next + * @see #START_TAG + * @see #TEXT + * @see #END_TAG + * @see #END_DOCUMENT + * @see #COMMENT + * @see #DOCDECL + * @see #PROCESSING_INSTRUCTION + * @see #ENTITY_REF + * @see #IGNORABLE_WHITESPACE + */ + int nextToken() + throws XmlPullParserException, IOException; + + //----------------------------------------------------------------------------- + // utility methods to mak XML parsing easier ... + + /** + * Test if the current event is of the given type and if the + * namespace and name do match. null will match any namespace + * and any name. If the test is not passed, an exception is + * thrown. The exception text indicates the parser position, + * the expected event and the current event that is not meeting the + * requirement. + * + * <p>Essentially it does this + * <pre> + * if (type != getEventType() + * || (namespace != null && !namespace.equals( getNamespace () ) ) + * || (name != null && !name.equals( getName() ) ) ) + * throw new XmlPullParserException( "expected "+ TYPES[ type ]+getPositionDescription()); + * </pre> + */ + void require(int type, String namespace, String name) + throws XmlPullParserException, IOException; + + /** + * If current event is START_TAG then if next element is TEXT then element content is returned + * or if next event is END_TAG then empty string is returned, otherwise exception is thrown. + * After calling this function successfully parser will be positioned on END_TAG. + * + * <p>The motivation for this function is to allow to parse consistently both + * empty elements and elements that has non empty content, for example for input: <ol> + * <li><tag>foo</tag> + * <li><tag></tag> (which is equivalent to <tag/> + * both input can be parsed with the same code: + * <pre> + * p.nextTag() + * p.requireEvent(p.START_TAG, "", "tag"); + * String content = p.nextText(); + * p.requireEvent(p.END_TAG, "", "tag"); + * </pre> + * This function together with nextTag make it very easy to parse XML that has + * no mixed content. + * + * + * <p>Essentially it does this + * <pre> + * if(getEventType() != START_TAG) { + * throw new XmlPullParserException( + * "parser must be on START_TAG to read next text", this, null); + * } + * int eventType = next(); + * if(eventType == TEXT) { + * String result = getText(); + * eventType = next(); + * if(eventType != END_TAG) { + * throw new XmlPullParserException( + * "event TEXT it must be immediately followed by END_TAG", this, null); + * } + * return result; + * } else if(eventType == END_TAG) { + * return ""; + * } else { + * throw new XmlPullParserException( + * "parser must be on START_TAG or TEXT to read text", this, null); + * } + * </pre> + */ + String nextText() throws XmlPullParserException, IOException; + + /** + * Call next() and return event if it is START_TAG or END_TAG + * otherwise throw an exception. + * It will skip whitespace TEXT before actual tag if any. + * + * <p>essentially it does this + * <pre> + * int eventType = next(); + * if(eventType == TEXT && isWhitespace()) { // skip whitespace + * eventType = next(); + * } + * if (eventType != START_TAG && eventType != END_TAG) { + * throw new XmlPullParserException("expected start or end tag", this, null); + * } + * return eventType; + * </pre> + */ + int nextTag() throws XmlPullParserException, IOException; + +} + diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParserException.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParserException.java new file mode 100644 index 0000000..b4b4b71 --- /dev/null +++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParserException.java @@ -0,0 +1,76 @@ +/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/ +// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/) + +package org.xmlpull.v1; + +/** + * This exception is thrown to signal XML Pull Parser related faults. + * + * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a> + */ +public class XmlPullParserException extends Exception { + protected Throwable detail; + protected int row = -1; + protected int column = -1; + + /* public XmlPullParserException() { + }*/ + + public XmlPullParserException(String s) { + super(s); + } + + /* + public XmlPullParserException(String s, Throwable thrwble) { + super(s); + this.detail = thrwble; + } + + public XmlPullParserException(String s, int row, int column) { + super(s); + this.row = row; + this.column = column; + } + */ + + public XmlPullParserException(String msg, XmlPullParser parser, Throwable chain) { + super ((msg == null ? "" : msg+" ") + + (parser == null ? "" : "(position:"+parser.getPositionDescription()+") ") + + (chain == null ? "" : "caused by: "+chain)); + + if (parser != null) { + this.row = parser.getLineNumber(); + this.column = parser.getColumnNumber(); + } + this.detail = chain; + } + + public Throwable getDetail() { return detail; } + // public void setDetail(Throwable cause) { this.detail = cause; } + public int getLineNumber() { return row; } + public int getColumnNumber() { return column; } + + /* + public String getMessage() { + if(detail == null) + return super.getMessage(); + else + return super.getMessage() + "; nested exception is: \n\t" + + detail.getMessage(); + } + */ + + //NOTE: code that prints this and detail is difficult in J2ME + public void printStackTrace() { + if (detail == null) { + super.printStackTrace(); + } else { + synchronized(System.err) { + System.err.println(super.getMessage() + "; nested exception is:"); + detail.printStackTrace(); + } + } + } + +} + diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java new file mode 100644 index 0000000..7b786f6 --- /dev/null +++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParserFactory.java @@ -0,0 +1,349 @@ +/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/ +// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/) + +package org.xmlpull.v1; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +/** + * This class is used to create implementations of XML Pull Parser defined in XMPULL V1 API. + * The name of actual factory class will be determined based on several parameters. + * It works similar to JAXP but tailored to work in J2ME environments + * (no access to system properties or file system) so name of parser class factory to use + * and its class used for loading (no class loader - on J2ME no access to context class loaders) + * must be passed explicitly. If no name of parser factory was passed (or is null) + * it will try to find name by searching in CLASSPATH for + * META-INF/services/org.xmlpull.v1.XmlPullParserFactory resource that should contain + * a comma separated list of class names of factories or parsers to try (in order from + * left to the right). If none found, it will throw an exception. + * + * <br /><strong>NOTE:</strong>In J2SE or J2EE environments, you may want to use + * <code>newInstance(property, classLoaderCtx)</code> + * where first argument is + * <code>System.getProperty(XmlPullParserFactory.PROPERTY_NAME)</code> + * and second is <code>Thread.getContextClassLoader().getClass()</code> . + * + * @see XmlPullParser + * + * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a> + * @author Stefan Haustein + */ + +public class XmlPullParserFactory { + /** used as default class to server as context class in newInstance() */ + final static Class referenceContextClass; + + static { + XmlPullParserFactory f = new XmlPullParserFactory(); + referenceContextClass = f.getClass(); + } + + /** Name of the system or midlet property that should be used for + a system property containing a comma separated list of factory + or parser class names (value: + org.xmlpull.v1.XmlPullParserFactory). */ + + + public static final String PROPERTY_NAME = + "org.xmlpull.v1.XmlPullParserFactory"; + + private static final String RESOURCE_NAME = + "/META-INF/services/" + PROPERTY_NAME; + + + // public static final String DEFAULT_PROPERTY = + // "org.xmlpull.xpp3.XmlPullParser,org.kxml2.io.KXmlParser"; + + + protected ArrayList parserClasses; + protected String classNamesLocation; + + protected ArrayList serializerClasses; + + + // features are kept there + protected HashMap features = new HashMap(); + + + /** + * Protected constructor to be called by factory implementations. + */ + + protected XmlPullParserFactory() { + } + + + + /** + * Set the features to be set when XML Pull Parser is created by this factory. + * <p><b>NOTE:</b> factory features are not used for XML Serializer. + * + * @param name string with URI identifying feature + * @param state if true feature will be set; if false will be ignored + */ + + public void setFeature(String name, + boolean state) throws XmlPullParserException { + + features.put(name, new Boolean(state)); + } + + + /** + * Return the current value of the feature with given name. + * <p><b>NOTE:</b> factory features are not used for XML Serializer. + * + * @param name The name of feature to be retrieved. + * @return The value of named feature. + * Unknown features are <string>always</strong> returned as false + */ + + public boolean getFeature (String name) { + Boolean value = (Boolean) features.get(name); + return value != null ? value.booleanValue() : false; + } + + /** + * Specifies that the parser produced by this factory will provide + * support for XML namespaces. + * By default the value of this is set to false. + * + * @param awareness true if the parser produced by this code + * will provide support for XML namespaces; false otherwise. + */ + + public void setNamespaceAware(boolean awareness) { + features.put (XmlPullParser.FEATURE_PROCESS_NAMESPACES, new Boolean (awareness)); + } + + /** + * Indicates whether or not the factory is configured to produce + * parsers which are namespace aware + * (it simply set feature XmlPullParser.FEATURE_PROCESS_NAMESPACES to true or false). + * + * @return true if the factory is configured to produce parsers + * which are namespace aware; false otherwise. + */ + + public boolean isNamespaceAware() { + return getFeature (XmlPullParser.FEATURE_PROCESS_NAMESPACES); + } + + + /** + * Specifies that the parser produced by this factory will be validating + * (it simply set feature XmlPullParser.FEATURE_VALIDATION to true or false). + * + * By default the value of this is set to false. + * + * @param validating - if true the parsers created by this factory must be validating. + */ + + public void setValidating(boolean validating) { + features.put (XmlPullParser.FEATURE_VALIDATION, new Boolean (validating)); + } + + /** + * Indicates whether or not the factory is configured to produce parsers + * which validate the XML content during parse. + * + * @return true if the factory is configured to produce parsers + * which validate the XML content during parse; false otherwise. + */ + + public boolean isValidating() { + return getFeature (XmlPullParser.FEATURE_VALIDATION); + } + + /** + * Creates a new instance of a XML Pull Parser + * using the currently configured factory features. + * + * @return A new instance of a XML Pull Parser. + * @throws XmlPullParserException if a parser cannot be created which satisfies the + * requested configuration. + */ + + public XmlPullParser newPullParser() throws XmlPullParserException { + + if (parserClasses == null) throw new XmlPullParserException + ("Factory initialization was incomplete - has not tried "+classNamesLocation); + + if (parserClasses.size() == 0) throw new XmlPullParserException + ("No valid parser classes found in "+classNamesLocation); + + final StringBuffer issues = new StringBuffer (); + + for (int i = 0; i < parserClasses.size(); i++) { + final Class ppClass = (Class) parserClasses.get(i); + try { + final XmlPullParser pp = (XmlPullParser) ppClass.newInstance(); + + for (Iterator iter = features.keySet().iterator(); iter.hasNext(); ) { + final String key = (String) iter.next(); + final Boolean value = (Boolean) features.get(key); + if(value != null && value.booleanValue()) { + pp.setFeature(key, true); + } + } + return pp; + + } catch(Exception ex) { + issues.append (ppClass.getName () + ": "+ ex.toString ()+"; "); + } + } + + throw new XmlPullParserException ("could not create parser: "+issues); + } + + + /** + * Creates a new instance of a XML Serializer. + * + * <p><b>NOTE:</b> factory features are not used for XML Serializer. + * + * @return A new instance of a XML Serializer. + * @throws XmlPullParserException if a parser cannot be created which satisfies the + * requested configuration. + */ + + public XmlSerializer newSerializer() throws XmlPullParserException { + + if (serializerClasses == null) { + throw new XmlPullParserException + ("Factory initialization incomplete - has not tried "+classNamesLocation); + } + if(serializerClasses.size() == 0) { + throw new XmlPullParserException + ("No valid serializer classes found in "+classNamesLocation); + } + + final StringBuffer issues = new StringBuffer (); + + for (int i = 0; i < serializerClasses.size (); i++) { + final Class ppClass = (Class) serializerClasses.get(i); + try { + final XmlSerializer ser = (XmlSerializer) ppClass.newInstance(); + + return ser; + + } catch(Exception ex) { + issues.append (ppClass.getName () + ": "+ ex.toString ()+"; "); + } + } + + throw new XmlPullParserException ("could not create serializer: "+issues); + } + + /** + * Create a new instance of a PullParserFactory that can be used + * to create XML pull parsers (see class description for more + * details). + * + * @return a new instance of a PullParserFactory, as returned by newInstance (null, null); + */ + public static XmlPullParserFactory newInstance () throws XmlPullParserException { + return newInstance(null, null); + } + + public static XmlPullParserFactory newInstance (String classNames, Class context) + throws XmlPullParserException { + + if (context == null) { + //NOTE: make sure context uses the same class loader as API classes + // this is the best we can do without having access to context classloader in J2ME + // if API is in the same classloader as implementation then this will work + context = referenceContextClass; + } + + /* + String classNamesLocation = null; + + if (classNames == null || classNames.length() == 0 || "DEFAULT".equals(classNames)) { + try { + InputStream is = context.getResourceAsStream (RESOURCE_NAME); + + if (is == null) throw new XmlPullParserException + ("resource not found: "+RESOURCE_NAME + +" make sure that parser implementing XmlPull API is available"); + final StringBuffer sb = new StringBuffer(); + + while (true) { + final int ch = is.read(); + if (ch < 0) break; + else if (ch > ' ') + sb.append((char) ch); + } + is.close (); + + classNames = sb.toString (); + } + catch (Exception e) { + throw new XmlPullParserException (null, null, e); + } + classNamesLocation = "resource "+RESOURCE_NAME+" that contained '"+classNames+"'"; + } else { + classNamesLocation = + "parameter classNames to newInstance() that contained '"+classNames+"'"; + } + */ + classNames = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer"; + + XmlPullParserFactory factory = null; + final ArrayList parserClasses = new ArrayList(); + final ArrayList serializerClasses = new ArrayList(); + int pos = 0; + + while (pos < classNames.length ()) { + int cut = classNames.indexOf (',', pos); + + if (cut == -1) cut = classNames.length (); + final String name = classNames.substring (pos, cut); + + Class candidate = null; + Object instance = null; + + try { + candidate = Class.forName (name); + // necessary because of J2ME .class issue + instance = candidate.newInstance (); + } + catch (Exception e) {} + + if (candidate != null) { + boolean recognized = false; + if (instance instanceof XmlPullParser) { + parserClasses.add(candidate); + recognized = true; + } + if (instance instanceof XmlSerializer) { + serializerClasses.add(candidate); + recognized = true; + } + if (instance instanceof XmlPullParserFactory) { + if (factory == null) { + factory = (XmlPullParserFactory) instance; + } + recognized = true; + } + if (!recognized) { + throw new XmlPullParserException ("incompatible class: "+name); + } + } + pos = cut + 1; + } + + if (factory == null) { + factory = new XmlPullParserFactory (); + } + factory.parserClasses = parserClasses; + factory.serializerClasses = serializerClasses; + factory.classNamesLocation = "org.kxml2.io.kXmlParser,org.kxml2.io.KXmlSerializer"; + return factory; + } +} + + diff --git a/xml/src/main/java/org/xmlpull/v1/XmlSerializer.java b/xml/src/main/java/org/xmlpull/v1/XmlSerializer.java new file mode 100644 index 0000000..8e85e2f --- /dev/null +++ b/xml/src/main/java/org/xmlpull/v1/XmlSerializer.java @@ -0,0 +1,326 @@ +package org.xmlpull.v1; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; + +/** + * Define an interface to serialziation of XML Infoset. + * This interface abstracts away if serialized XML is XML 1.0 comaptible text or + * other formats of XML 1.0 serializations (such as binary XML for example with WBXML). + * + * <p><b>PLEASE NOTE:</b> This interface will be part of XmlPull 1.2 API. + * It is included as basis for discussion. It may change in any way. + * + * <p>Exceptions that may be thrown are: IOException or runtime exception + * (more runtime exceptions can be thrown but are not declared and as such + * have no semantics defined for this interface): + * <ul> + * <li><em>IllegalArgumentException</em> - for almost all methods to signal that + * argument is illegal + * <li><em>IllegalStateException</em> - to signal that call has good arguments but + * is not expected here (violation of contract) and for features/properties + * when requesting setting unimplemented feature/property + * (UnsupportedOperationException would be better but it is not in MIDP) + * </ul> + * + * <p><b>NOTE:</b> writing CDSECT, ENTITY_REF, IGNORABLE_WHITESPACE, + * PROCESSING_INSTRUCTION, COMMENT, and DOCDECL in some implementations + * may not be supported (for example when serializing to WBXML). + * In such case IllegalStateException will be thrown and it is recommened + * to use an optional feature to signal that implementation is not + * supporting this kind of output. + */ + +public interface XmlSerializer { + + /** + * Set feature identified by name (recommended to be URI for uniqueness). + * Some well known optional features are defined in + * <a href="http://www.xmlpull.org/v1/doc/features.html"> + * http://www.xmlpull.org/v1/doc/features.html</a>. + * + * If feature is not recocgnized or can not be set + * then IllegalStateException MUST be thrown. + * + * @exception IllegalStateException If the feature is not supported or can not be set + */ + void setFeature(String name, + boolean state) + throws IllegalArgumentException, IllegalStateException; + + + /** + * Return the current value of the feature with given name. + * <p><strong>NOTE:</strong> unknown properties are <strong>always</strong> returned as null + * + * @param name The name of feature to be retrieved. + * @return The value of named feature. + * @exception IllegalArgumentException if feature string is null + */ + boolean getFeature(String name); + + + /** + * Set the value of a property. + * (the property name is recommened to be URI for uniqueness). + * Some well known optional properties are defined in + * <a href="http://www.xmlpull.org/v1/doc/properties.html"> + * http://www.xmlpull.org/v1/doc/properties.html</a>. + * + * If property is not recocgnized or can not be set + * then IllegalStateException MUST be thrown. + * + * @exception IllegalStateException if the property is not supported or can not be set + */ + void setProperty(String name, + Object value) + throws IllegalArgumentException, IllegalStateException; + + /** + * Look up the value of a property. + * + * The property name is any fully-qualified URI. I + * <p><strong>NOTE:</strong> unknown properties are <string>always</strong> returned as null + * + * @param name The name of property to be retrieved. + * @return The value of named property. + */ + Object getProperty(String name); + + /** + * Set to use binary output stream with given encoding. + */ + void setOutput (OutputStream os, String encoding) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Set the output to the given writer. + * <p><b>WARNING</b> no information about encoding is available! + */ + void setOutput (Writer writer) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Write <?xml declaration with encoding (if encoding not null) + * and standalone flag (if standalone not null) + * This method can only be called just after setOutput. + */ + void startDocument (String encoding, Boolean standalone) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Finish writing. All unclosed start tags will be closed and output + * will be flushed. After calling this method no more output can be + * serialized until next call to setOutput() + */ + void endDocument () + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Binds the given prefix to the given namespace. + * This call is valid for the next element including child elements. + * The prefix and namespace MUST be always declared even if prefix + * is not used in element (startTag() or attribute()) - for XML 1.0 + * it must result in declaring <code>xmlns:prefix='namespace'</code> + * (or <code>xmlns:prefix="namespace"</code> depending what character is used + * to quote attribute value). + * + * <p><b>NOTE:</b> this method MUST be called directly before startTag() + * and if anything but startTag() or setPrefix() is called next there will be exception. + * <p><b>NOTE:</b> prefixes "xml" and "xmlns" are already bound + * and can not be redefined see: + * <a href="http://www.w3.org/XML/xml-names-19990114-errata#NE05">Namespaces in XML Errata</a>. + * <p><b>NOTE:</b> to set default namespace use as prefix empty string. + * + * @param prefix must be not null (or IllegalArgumentException is thrown) + * @param namespace must be not null + */ + void setPrefix (String prefix, String namespace) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Return namespace that corresponds to given prefix + * If there is no prefix bound to this namespace return null + * but if generatePrefix is false then return generated prefix. + * + * <p><b>NOTE:</b> if the prefix is empty string "" and defualt namespace is bound + * to this prefix then empty string ("") is returned. + * + * <p><b>NOTE:</b> prefixes "xml" and "xmlns" are already bound + * will have values as defined + * <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces in XML specification</a> + */ + String getPrefix (String namespace, boolean generatePrefix) + throws IllegalArgumentException; + + /** + * Returns the current depth of the element. + * Outside the root element, the depth is 0. The + * depth is incremented by 1 when startTag() is called. + * The depth is decremented after the call to endTag() + * event was observed. + * + * <pre> + * <!-- outside --> 0 + * <root> 1 + * sometext 1 + * <foobar> 2 + * </foobar> 2 + * </root> 1 + * <!-- outside --> 0 + * </pre> + */ + int getDepth(); + + /** + * Returns the namespace URI of the current element as set by startTag(). + * + * <p><b>NOTE:</b> that measn in particaulr that: <ul> + * <li>if there was startTag("", ...) then getNamespace() returns "" + * <li>if there was startTag(null, ...) then getNamespace() returns null + * </ul> + * + * @return namespace set by startTag() that is currently in scope + */ + String getNamespace (); + + /** + * Returns the name of the current element as set by startTag(). + * It can only be null before first call to startTag() + * or when last endTag() is called to close first startTag(). + * + * @return namespace set by startTag() that is currently in scope + */ + String getName(); + + /** + * Writes a start tag with the given namespace and name. + * If there is no prefix defined for the given namespace, + * a prefix will be defined automatically. + * The explicit prefixes for namespaces can be established by calling setPrefix() + * immediately before this method. + * If namespace is null no namespace prefix is printed but just name. + * If namespace is empty string then serialzier will make sure that + * default empty namespace is declared (in XML 1.0 xmlns='') + * or throw IllegalStateException if default namespace is already bound + * to non-empty string. + */ + XmlSerializer startTag (String namespace, String name) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Write an attribute. Calls to attribute() MUST follow a call to + * startTag() immediately. If there is no prefix defined for the + * given namespace, a prefix will be defined automatically. + * If namespace is null or empty string + * no namespace prefix is printed but just name. + */ + XmlSerializer attribute (String namespace, String name, String value) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Write end tag. Repetition of namespace and name is just for avoiding errors. + * <p><b>Background:</b> in kXML endTag had no arguments, and non matching tags were + * very difficult to find... + * If namespace is null no namespace prefix is printed but just name. + * If namespace is empty string then serialzier will make sure that + * default empty namespace is declared (in XML 1.0 xmlns=''). + */ + XmlSerializer endTag (String namespace, String name) + throws IOException, IllegalArgumentException, IllegalStateException; + + + // /** + // * Writes a start tag with the given namespace and name. + // * <br />If there is no prefix defined (prefix == null) for the given namespace, + // * a prefix will be defined automatically. + // * <br />If explicit prefixes is passed (prefix != null) then it will be used + // *and namespace declared if not already declared or + // * throw IllegalStateException the same prefix was already set on this + // * element (setPrefix()) and was bound to different namespace. + // * <br />If namespace is null then prefix must be null too or IllegalStateException is thrown. + // * <br />If namespace is null then no namespace prefix is printed but just name. + // * <br />If namespace is empty string then serializer will make sure that + // * default empty namespace is declared (in XML 1.0 xmlns='') + // * or throw IllegalStateException if default namespace is already bound + // * to non-empty string. + // */ + // XmlSerializer startTag (String prefix, String namespace, String name) + // throws IOException, IllegalArgumentException, IllegalStateException; + // + // /** + // * Write an attribute. Calls to attribute() MUST follow a call to + // * startTag() immediately. + // * <br />If there is no prefix defined (prefix == null) for the given namespace, + // * a prefix will be defined automatically. + // * <br />If explicit prefixes is passed (prefix != null) then it will be used + // * and namespace declared if not already declared or + // * throw IllegalStateException the same prefix was already set on this + // * element (setPrefix()) and was bound to different namespace. + // * <br />If namespace is null then prefix must be null too or IllegalStateException is thrown. + // * <br />If namespace is null then no namespace prefix is printed but just name. + // * <br />If namespace is empty string then serializer will make sure that + // * default empty namespace is declared (in XML 1.0 xmlns='') + // * or throw IllegalStateException if default namespace is already bound + // * to non-empty string. + // */ + // XmlSerializer attribute (String prefix, String namespace, String name, String value) + // throws IOException, IllegalArgumentException, IllegalStateException; + // + // /** + // * Write end tag. Repetition of namespace, prefix, and name is just for avoiding errors. + // * <br />If namespace or name arguments are different from corresponding startTag call + // * then IllegalArgumentException is thrown, if prefix argument is not null and is different + // * from corresponding starTag then IllegalArgumentException is thrown. + // * <br />If namespace is null then prefix must be null too or IllegalStateException is thrown. + // * <br />If namespace is null then no namespace prefix is printed but just name. + // * <br />If namespace is empty string then serializer will make sure that + // * default empty namespace is declared (in XML 1.0 xmlns=''). + // * <p><b>Background:</b> in kXML endTag had no arguments, and non matching tags were + // * very difficult to find...</p> + // */ + // ALEK: This is really optional as prefix in end tag MUST correspond to start tag but good for error checking + // XmlSerializer endTag (String prefix, String namespace, String name) + // throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Writes text, where special XML chars are escaped automatically + */ + XmlSerializer text (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Writes text, where special XML chars are escaped automatically + */ + XmlSerializer text (char [] buf, int start, int len) + throws IOException, IllegalArgumentException, IllegalStateException; + + void cdsect (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + void entityRef (String text) throws IOException, + IllegalArgumentException, IllegalStateException; + void processingInstruction (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + void comment (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + void docdecl (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + void ignorableWhitespace (String text) + throws IOException, IllegalArgumentException, IllegalStateException; + + /** + * Write all pending output to the stream. + * If method startTag() or attribute() was called then start tag is closed (final >) + * before flush() is called on underlying output stream. + * + * <p><b>NOTE:</b> if there is need to close start tag + * (so no more attribute() calls are allowed) but without flushinging output + * call method text() with empty string (text("")). + * + */ + void flush () + throws IOException; + +} + diff --git a/xml/src/main/java/org/xmlpull/v1/sax2/Driver.java b/xml/src/main/java/org/xmlpull/v1/sax2/Driver.java new file mode 100644 index 0000000..0bd2d4f --- /dev/null +++ b/xml/src/main/java/org/xmlpull/v1/sax2/Driver.java @@ -0,0 +1,469 @@ +/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- //------100-columns-wide------>|*/ +// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/) + +package org.xmlpull.v1.sax2; + +import java.io.InputStream; +import java.io.IOException; +import java.io.Reader; + +// not J2ME classes -- remove if you want to run in MIDP devices +import java.net.URL; +import java.net.MalformedURLException; + + +// not J2ME classes +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +import org.xml.sax.Attributes; +import org.xml.sax.DTDHandler; +import org.xml.sax.ContentHandler; +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +/** + * SAX2 Driver that pulls events from XmlPullParser + * and comverts them into SAX2 callbacks. + * + * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a> + */ + +public class Driver implements Locator, XMLReader, Attributes +{ + + protected static final String DECLARATION_HANDLER_PROPERTY = + "http://xml.org/sax/properties/declaration-handler"; + + protected static final String LEXICAL_HANDLER_PROPERTY = + "http://xml.org/sax/properties/lexical-handler"; + + protected static final String NAMESPACES_FEATURE = + "http://xml.org/sax/features/namespaces"; + + protected static final String NAMESPACE_PREFIXES_FEATURE = + "http://xml.org/sax/features/namespace-prefixes"; + + protected static final String VALIDATION_FEATURE = + "http://xml.org/sax/features/validation"; + + protected static final String APACHE_SCHEMA_VALIDATION_FEATURE = + "http://apache.org/xml/features/validation/schema"; + + protected static final String APACHE_DYNAMIC_VALIDATION_FEATURE = + "http://apache.org/xml/features/validation/dynamic"; + + protected ContentHandler contentHandler = new DefaultHandler(); + protected ErrorHandler errorHandler = new DefaultHandler();; + + protected String systemId; + + protected XmlPullParser pp; + + //private final static boolean DEBUG = false; + + /** + */ + public Driver() throws XmlPullParserException { + final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + pp = factory.newPullParser(); + } + + public Driver(XmlPullParser pp) throws XmlPullParserException { + this.pp = pp; + } + + // -- Attributes interface + + public int getLength() { return pp.getAttributeCount(); } + public String getURI(int index) { return pp.getAttributeNamespace(index); } + public String getLocalName(int index) { return pp.getAttributeName(index); } + public String getQName(int index) { + final String prefix = pp.getAttributePrefix(index); + if(prefix != null) { + return prefix+':'+pp.getAttributeName(index); + } else { + return pp.getAttributeName(index); + } + } + public String getType(int index) { return pp.getAttributeType(index); } + public String getValue(int index) { return pp.getAttributeValue(index); } + + public int getIndex(String uri, String localName) { + for (int i = 0; i < pp.getAttributeCount(); i++) + { + if(pp.getAttributeNamespace(i).equals(uri) + && pp.getAttributeName(i).equals(localName)) + { + return i; + } + + } + return -1; + } + + public int getIndex(String qName) { + for (int i = 0; i < pp.getAttributeCount(); i++) + { + if(pp.getAttributeName(i).equals(qName)) + { + return i; + } + + } + return -1; + } + + public String getType(String uri, String localName) { + for (int i = 0; i < pp.getAttributeCount(); i++) + { + if(pp.getAttributeNamespace(i).equals(uri) + && pp.getAttributeName(i).equals(localName)) + { + return pp.getAttributeType(i); + } + + } + return null; + } + public String getType(String qName) { + for (int i = 0; i < pp.getAttributeCount(); i++) + { + if(pp.getAttributeName(i).equals(qName)) + { + return pp.getAttributeType(i); + } + + } + return null; + } + public String getValue(String uri, String localName) { + return pp.getAttributeValue(uri, localName); + } + public String getValue(String qName) { + return pp.getAttributeValue(null, qName); + } + + // -- Locator interface + + public String getPublicId() { return null; } + public String getSystemId() { return systemId; } + public int getLineNumber() { return pp.getLineNumber(); } + public int getColumnNumber() { return pp.getColumnNumber(); } + + // --- XMLReader interface + + public boolean getFeature(String name) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if(NAMESPACES_FEATURE.equals(name)) { + return pp.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES); + } else if(NAMESPACE_PREFIXES_FEATURE.equals(name)) { + return pp.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES); + } else if(VALIDATION_FEATURE.equals(name)) { + return pp.getFeature(XmlPullParser.FEATURE_VALIDATION); + // } else if(APACHE_SCHEMA_VALIDATION_FEATURE.equals(name)) { + // return false; //TODO + // } else if(APACHE_DYNAMIC_VALIDATION_FEATURE.equals(name)) { + // return false; //TODO + } else { + return pp.getFeature(name); + //throw new SAXNotRecognizedException("unrecognized feature "+name); + } + } + + public void setFeature (String name, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException + { + try { + if(NAMESPACES_FEATURE.equals(name)) { + pp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, value); + } else if(NAMESPACE_PREFIXES_FEATURE.equals(name)) { + if(pp.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES) != value) { + pp.setFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES, value); + } + } else if(VALIDATION_FEATURE.equals(name)) { + pp.setFeature(XmlPullParser.FEATURE_VALIDATION, value); + // } else if(APACHE_SCHEMA_VALIDATION_FEATURE.equals(name)) { + // // can ignore as validation must be false ... + // // if(true == value) { + // // throw new SAXNotSupportedException("schema validation is not supported"); + // // } + // } else if(APACHE_DYNAMIC_VALIDATION_FEATURE.equals(name)) { + // if(true == value) { + // throw new SAXNotSupportedException("dynamic validation is not supported"); + // } + } else { + pp.setFeature(name, value); + //throw new SAXNotRecognizedException("unrecognized feature "+name); + } + } catch(XmlPullParserException ex) { + // throw new SAXNotSupportedException("problem with setting feature "+name+": "+ex); + } + } + + public Object getProperty (String name) + throws SAXNotRecognizedException, SAXNotSupportedException + { + if(DECLARATION_HANDLER_PROPERTY.equals(name)) { + return null; + } else if(LEXICAL_HANDLER_PROPERTY.equals(name)) { + return null; + } else { + return pp.getProperty(name); + //throw new SAXNotRecognizedException("not recognized get property "+name); + } + } + + public void setProperty (String name, Object value) + throws SAXNotRecognizedException, SAXNotSupportedException + { + // + if(DECLARATION_HANDLER_PROPERTY.equals(name)) { + throw new SAXNotSupportedException("not supported setting property "+name);//+" to "+value); + } else if(LEXICAL_HANDLER_PROPERTY.equals(name)) { + throw new SAXNotSupportedException("not supported setting property "+name);//+" to "+value); + } else { + try { + pp.setProperty(name, value); + } catch(XmlPullParserException ex) { + throw new SAXNotSupportedException("not supported set property "+name+": "+ ex); + } + //throw new SAXNotRecognizedException("not recognized set property "+name); + } + } + + public void setEntityResolver (EntityResolver resolver) {} + + public EntityResolver getEntityResolver () { return null; } + + public void setDTDHandler (DTDHandler handler) {} + + public DTDHandler getDTDHandler () { return null; } + + public void setContentHandler (ContentHandler handler) + { + this.contentHandler = handler; + } + + public ContentHandler getContentHandler() { return contentHandler; } + + public void setErrorHandler(ErrorHandler handler) { + this.errorHandler = handler; + } + + public ErrorHandler getErrorHandler() { return errorHandler; } + + public void parse(InputSource source) throws SAXException, IOException + { + + systemId = source.getSystemId(); + contentHandler.setDocumentLocator(this); + + final Reader reader = source.getCharacterStream(); + try { + if (reader == null) { + InputStream stream = source.getByteStream(); + final String encoding = source.getEncoding(); + + if (stream == null) { + systemId = source.getSystemId(); + if(systemId == null) { + SAXParseException saxException = new SAXParseException( + "null source systemId" , this); + errorHandler.fatalError(saxException); + return; + } + // NOTE: replace with Connection to run in J2ME environment + try { + final URL url = new URL(systemId); + stream = url.openStream(); + } catch (MalformedURLException nue) { + try { + stream = new FileInputStream(systemId); + } catch (FileNotFoundException fnfe) { + final SAXParseException saxException = new SAXParseException( + "could not open file with systemId "+systemId, this, fnfe); + errorHandler.fatalError(saxException); + return; + } + } + } + pp.setInput(stream, encoding); + } else { + pp.setInput(reader); + } + } catch (XmlPullParserException ex) { + final SAXParseException saxException = new SAXParseException( + "parsing initialization error: "+ex, this, ex); + //if(DEBUG) ex.printStackTrace(); + errorHandler.fatalError(saxException); + return; + } + + // start parsing - move to first start tag + try { + contentHandler.startDocument(); + // get first event + pp.next(); + // it should be start tag... + if(pp.getEventType() != XmlPullParser.START_TAG) { + final SAXParseException saxException = new SAXParseException( + "expected start tag not"+pp.getPositionDescription(), this); + //throw saxException; + errorHandler.fatalError(saxException); + return; + } + } catch (XmlPullParserException ex) { + final SAXParseException saxException = new SAXParseException( + "parsing initialization error: "+ex, this, ex); + //ex.printStackTrace(); + errorHandler.fatalError(saxException); + return; + } + + // now real parsing can start! + + parseSubTree(pp); + + // and finished ... + + contentHandler.endDocument(); + } + + public void parse(String systemId) throws SAXException, IOException { + parse(new InputSource(systemId)); + } + + + public void parseSubTree(XmlPullParser pp) throws SAXException, IOException { + this.pp = pp; + final boolean namespaceAware = pp.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES); + try { + if(pp.getEventType() != XmlPullParser.START_TAG) { + throw new SAXException( + "start tag must be read before skiping subtree"+pp.getPositionDescription()); + } + final int[] holderForStartAndLength = new int[2]; + final StringBuffer rawName = new StringBuffer(16); + String prefix = null; + String name = null; + int level = pp.getDepth() - 1; + int type = XmlPullParser.START_TAG; + + LOOP: + do { + switch(type) { + case XmlPullParser.START_TAG: + if(namespaceAware) { + final int depth = pp.getDepth() - 1; + final int countPrev = + (level > depth) ? pp.getNamespaceCount(depth) : 0; + //int countPrev = pp.getNamespaceCount(pp.getDepth() - 1); + final int count = pp.getNamespaceCount(depth + 1); + for (int i = countPrev; i < count; i++) + { + contentHandler.startPrefixMapping( + pp.getNamespacePrefix(i), + pp.getNamespaceUri(i) + ); + } + name = pp.getName(); + prefix = pp.getPrefix(); + if(prefix != null) { + rawName.setLength(0); + rawName.append(prefix); + rawName.append(':'); + rawName.append(name); + } + startElement(pp.getNamespace(), + name, + // TODO Fixed this. Was "not equals". + prefix == null ? name : rawName.toString()); + } else { + startElement(pp.getNamespace(), + pp.getName(), + pp.getName()); + } + //++level; + + break; + case XmlPullParser.TEXT: + final char[] chars = pp.getTextCharacters(holderForStartAndLength); + contentHandler.characters(chars, + holderForStartAndLength[0], //start + holderForStartAndLength[1] //len + ); + break; + case XmlPullParser.END_TAG: + //--level; + if(namespaceAware) { + name = pp.getName(); + prefix = pp.getPrefix(); + if(prefix != null) { + rawName.setLength(0); + rawName.append(prefix); + rawName.append(':'); + rawName.append(name); + } + contentHandler.endElement(pp.getNamespace(), + name, + prefix != null ? name : rawName.toString() + ); + // when entering show prefixes for all levels!!!! + final int depth = pp.getDepth(); + final int countPrev = + (level > depth) ? pp.getNamespaceCount(pp.getDepth()) : 0; + int count = pp.getNamespaceCount(pp.getDepth() - 1); + // undeclare them in reverse order + for (int i = count - 1; i >= countPrev; i--) + { + contentHandler.endPrefixMapping( + pp.getNamespacePrefix(i) + ); + } + } else { + contentHandler.endElement(pp.getNamespace(), + pp.getName(), + pp.getName() + ); + + } + break; + case XmlPullParser.END_DOCUMENT: + break LOOP; + } + type = pp.next(); + } while(pp.getDepth() > level); + } catch (XmlPullParserException ex) { + final SAXParseException saxException = new SAXParseException("parsing error: "+ex, this, ex); + ex.printStackTrace(); + errorHandler.fatalError(saxException); + } + } + + /** + * Calls {@link ContentHandler#startElement(String, String, String, Attributes) startElement} + * on the <code>ContentHandler</code> with <code>this</code> driver object as the + * {@link Attributes} implementation. In default implementation + * {@link Attributes} object is valid only during this method call and may not + * be stored. Sub-classes can overwrite this method to cache attributes. + */ + protected void startElement(String namespace, String localName, String qName) throws SAXException { + contentHandler.startElement(namespace, localName, qName, this); + } + +} |