diff --git a/docs/Changelog.md b/docs/Changelog.md index 62658f6e6..3a2d5d074 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -5,6 +5,7 @@ - Android: scoped storage example [#785](https://github.com/mapsforge/vtm/pull/785) - Mapsforge: map stream support [#784](https://github.com/mapsforge/vtm/pull/784) - Render theme from Android content providers [#783](https://github.com/mapsforge/vtm/pull/783) +- Render theme xml pull parser [#786](https://github.com/mapsforge/vtm/pull/786) - Many other minor improvements and bug fixes - [Solved issues](https://github.com/mapsforge/vtm/issues?q=is%3Aclosed+milestone%3A0.15.0) diff --git a/docs/Integration.md b/docs/Integration.md index b5290c784..dba780a82 100644 --- a/docs/Integration.md +++ b/docs/Integration.md @@ -10,6 +10,7 @@ Current version is [![Maven Central](https://img.shields.io/maven-central/v/org. ```groovy implementation 'org.mapsforge:vtm:[CURRENT-VERSION]' implementation 'org.mapsforge:vtm-themes:[CURRENT-VERSION]' +implementation 'net.sf.kxml:kxml2:2.3.0' implementation 'org.slf4j:slf4j-api:1.7.28' ``` diff --git a/vtm-android-example/src/org/oscim/android/filepicker/ValidRenderTheme.java b/vtm-android-example/src/org/oscim/android/filepicker/ValidRenderTheme.java index bb8c0ba62..8cce66099 100644 --- a/vtm-android-example/src/org/oscim/android/filepicker/ValidRenderTheme.java +++ b/vtm-android-example/src/org/oscim/android/filepicker/ValidRenderTheme.java @@ -1,6 +1,6 @@ /* * Copyright 2010, 2011, 2012 mapsforge.org - * Copyright 2016-2018 devemux86 + * Copyright 2016-2020 devemux86 * Copyright 2017 Longri * * This program is free software: you can redistribute it and/or modify it under the @@ -16,18 +16,11 @@ */ package org.oscim.android.filepicker; -import org.oscim.theme.ExternalRenderTheme; -import org.oscim.theme.ThemeFile; -import org.oscim.theme.XmlThemeBuilder; +import org.oscim.theme.ThemeLoader; import org.oscim.tiling.TileSource.OpenResult; -import org.xml.sax.InputSource; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.DefaultHandler; import java.io.File; -import javax.xml.parsers.SAXParserFactory; - /** * Accepts all valid render theme XML files. */ @@ -36,13 +29,8 @@ public final class ValidRenderTheme implements ValidFileFilter { @Override public boolean accept(File file) { - try { - ThemeFile theme = new ExternalRenderTheme(file.getAbsolutePath()); - DefaultHandler renderThemeHandler = new XmlThemeBuilder(theme); - XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); - xmlReader.setContentHandler(renderThemeHandler); - xmlReader.parse(new InputSource(theme.getRenderThemeAsStream())); + ThemeLoader.load(file.getAbsolutePath()); mOpenResult = OpenResult.SUCCESS; } catch (Exception e) { mOpenResult = new OpenResult(e.getMessage()); diff --git a/vtm-app/src/org/oscim/app/filefilter/ValidRenderTheme.java b/vtm-app/src/org/oscim/app/filefilter/ValidRenderTheme.java index 004032773..1ee56011b 100644 --- a/vtm-app/src/org/oscim/app/filefilter/ValidRenderTheme.java +++ b/vtm-app/src/org/oscim/app/filefilter/ValidRenderTheme.java @@ -1,6 +1,6 @@ /* * Copyright 2010, 2011, 2012 mapsforge.org - * Copyright 2016 devemux86 + * Copyright 2016-2020 devemux86 * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software @@ -15,40 +15,30 @@ */ package org.oscim.app.filefilter; -import org.oscim.theme.ExternalRenderTheme; -import org.oscim.theme.ThemeFile; -import org.oscim.theme.XmlThemeBuilder; +import org.oscim.theme.ThemeLoader; import org.oscim.tiling.TileSource.OpenResult; -import org.xml.sax.InputSource; -import org.xml.sax.XMLReader; import java.io.File; -import javax.xml.parsers.SAXParserFactory; - /** * Accepts all valid render theme XML files. */ public final class ValidRenderTheme implements ValidFileFilter { - private OpenResult openResult; + private OpenResult mOpenResult; @Override public boolean accept(File file) { try { - ThemeFile theme = new ExternalRenderTheme(file.getAbsolutePath()); - XmlThemeBuilder renderThemeHandler = new XmlThemeBuilder(theme); - XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); - xmlReader.setContentHandler(renderThemeHandler); - xmlReader.parse(new InputSource(theme.getRenderThemeAsStream())); - this.openResult = OpenResult.SUCCESS; + ThemeLoader.load(file.getAbsolutePath()); + mOpenResult = OpenResult.SUCCESS; } catch (Exception e) { - this.openResult = new OpenResult(e.getMessage()); + mOpenResult = new OpenResult(e.getMessage()); } - return this.openResult.isSuccess(); + return mOpenResult.isSuccess(); } @Override public OpenResult getFileOpenResult() { - return this.openResult; + return mOpenResult; } } diff --git a/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlPullParser.java b/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlPullParser.java new file mode 100644 index 000000000..b3cb8552b --- /dev/null +++ b/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlPullParser.java @@ -0,0 +1,1124 @@ +/* -*- 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 functionality provided + * in XMLPULL V1 API (visit this website to + * learn more about API and its implementations). + * + *

There are following different + * kinds of parser depending on which features are set:

+ * + * + *

There are two key methods: next() and nextToken(). While next() provides + * access to high level parsing events, nextToken() allows access to lower + * level tokens. + * + *

The current event state of the parser + * can be determined by calling the + * getEventType() method. + * Initially, the parser is in the START_DOCUMENT + * state. + * + *

The method next() 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 (). + * + *

Th following event types are seen by next()

+ *
START_TAG
An XML start tag was read. + *
TEXT
Text content was read; + * the text content can be retrieved using the getText() method. + * (when in validating mode next() will not report ignorable whitespace, use nextToken() instead) + *
END_TAG
An end tag was read + *
END_DOCUMENT
No more events are available + *
+ * + *

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:

+ * + * A minimal example for using this API may look as follows: + *
+ * import java.io.IOException;
+ * import java.io.StringReader;
+ *
+ * import org.xmlpull.v1.XmlPullParser;
+ * import org.xmlpull.v1.XmlPullParserException;
+ * import org.xmlpull.v1.XmlPullParserFactory;
+ *
+ * public class SimpleXmlPullApp
+ * {
+ *
+ *     public static void main (String args[])
+ *         throws XmlPullParserException, IOException
+ *     {
+ *         XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+ *         factory.setNamespaceAware(true);
+ *         XmlPullParser xpp = factory.newPullParser();
+ *
+ *         xpp.setInput( 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.START_TAG) {
+ *              System.out.println("Start tag "+xpp.getName());
+ *          } else if(eventType == XmlPullParser.END_TAG) {
+ *              System.out.println("End tag "+xpp.getName());
+ *          } else if(eventType == XmlPullParser.TEXT) {
+ *              System.out.println("Text "+xpp.getText());
+ *          }
+ *          eventType = xpp.next();
+ *         }
+ *         System.out.println("End document");
+ *     }
+ * }
+ * 
+ * + *

The above example will generate the following output: + *

+ * Start document
+ * Start tag foo
+ * Text Hello World!
+ * End tag foo
+ * End document
+ * 
+ * + *

For more details on API usage, please refer to the + * quick Introduction available at http://www.xmlpull.org + * + * @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 Stefan Haustein + * @author Aleksander Slominski + */ + +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()). + * + * @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. + *

NOTE: subsequent calls to + * next() or nextToken() + * may result in exception being thrown. + * + * @see #next + * @see #nextToken + */ + int END_DOCUMENT = 1; + + /** + * Returned from getEventType(), + * next(), nextToken() 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 namespaces are enabled. + * 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(), next(), or + * nextToken() 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(). + *

Please note: next() will + * accumulate multiple + * events into one TEXT event, skipping IGNORABLE_WHITESPACE, + * PROCESSING_INSTRUCTION and COMMENT events, + * In contrast, nextToken() 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 changing line ends when examining low + * level events, whereas for high level applications the text is + * normalized appropriately. + * + * @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 nextToken(). + * 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 calling getText(). + * + * @see #nextToken + * @see #getText + */ + int CDSECT = 5; + + /** + * An entity reference was just read; + * this token is available from nextToken() + * only. The entity name is available by calling getName(). If available, + * the replacement text can be obtained by calling getText(); otherwise, + * the user is responsible 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 nextToken()). + * 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() + * + *

NOTE: 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 nextToken(). + * 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 nextToken() 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 nextToken() 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 malicious + * 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. + *

NOTE: 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 feature is activated, then the document declaration + * must be processed by the parser. + * + *

Please note: 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 specification are reported. + * This implies that FEATURE_PROCESS_DOCDECL is true and both, the + * internal and external document type declaration will be processed. + *

Please Note: 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. + *

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 features 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. + *

Please note: unknown features are + * always 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. + *

NOTE: unknown properties are always + * 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. + * + *

NOTE: 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 + * http://xmlpull.org/v1/doc/features.html#detect-encoding + * 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 supports 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 + * XML 1.0 Section 4.5 + * Construction of Internal Entity Replacement Text. + * 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. + * + *

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. + * + *

Please notes: 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;, ampersand with &amp; + * and so on. + * + *

Note: 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). + * + *

Note: 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. + * + *

NOTE: 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. + *

NOTE: to retrieve list of namespaces declared in current element:

+     *       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);
+     *           // ...
+     *      }
+     * 
+ * + * @see #getNamespacePrefix + * @see #getNamespaceUri + * @see #getNamespace() + * @see #getNamespace(String) + */ + int getNamespaceCount(int depth) throws XmlPullParserException; + + /** + * Returns the namespace prefix 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. + *

Please note: 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. + *

NOTE: 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. + * + *

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). + * + *

This method is a convenience method for + * + *

+     *  for (int i = getNamespaceCount(getDepth ())-1; i >= 0; i--) {
+     *   if (getNamespacePrefix(i).equals( prefix )) {
+     *     return getNamespaceUri(i);
+     *   }
+     *  }
+     *  return null;
+     * 
+ * + *

Please note: parser implementations + * may provide more efficient lookup, e.g. using a Hashtable. + * The 'xml' prefix is bound to "http://www.w3.org/XML/1998/namespace", as + * defined in the + * Namespaces in XML + * specification. Analogous, the 'xmlns' prefix is resolved to + * http://www.w3.org/2000/xmlns/ + * + * @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. + * + *

+     * <!-- outside -->     0
+     * <root>                  1
+     *   sometext                 1
+     *     <foobar>         2
+     *     </foobar>        2
+     * </root>              1
+     * <!-- outside -->     0
+     * 
+ */ + 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 1. + * 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. + * + *

Please note: 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. + * + *

NOTE: 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. + * + *

Please note: 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. + *

Please note: 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/>). + *

NOTE: 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. + * + *

NOTE: if FEATURE_REPORT_NAMESPACE_ATTRIBUTES is set + * then namespace attributes (xmlns:ns='...') must be reported + * with namespace + * http://www.w3.org/2000/xmlns/ + * (visit this URL for description!). + * The default namespace attribute (xmlns="...") will be reported with empty namespace. + *

NOTE:The xml prefix is bound as defined in + * Namespaces in XML + * specification to "http://www.w3.org/XML/1998/namespace". + * + * @param index zero-based index of attribute + * @return attribute namespace, + * empty string ("") is returned if namespaces 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. + * + *

NOTE: attribute value must be normalized + * (including entity replacement text if PROCESS_DOCDECL is false) as described in + * XML 1.0 section + * 3.3.3 Attribute-Value Normalization + * + * @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. + * + *

NOTE: attribute value must be normalized + * (including entity replacement text if PROCESS_DOCDECL is false) as described in + * XML 1.0 section + * 3.3.3 Attribute-Value Normalization + * + * @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 will be coalesced and only one + * TEXT event must be returned for whole element content + * (comments and processing instructions will be ignored and entity references + * must be expanded or exception must be thrown if entity reference can not be expanded). + * If element content is empty (content is "") then no TEXT event will be reported. + * + *

NOTE: 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. + * + *

If special feature + * FEATURE_XML_ROUNDTRIP + * (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 + * XML 1.0 End-of-Line Handling + * and. Also when this feature is enabled exact content of START_TAG, END_TAG, + * DOCDECL and PROCESSING_INSTRUCTION is available. + * + *

Here is the list of tokens that can be returned from nextToken() + * and what getText() and getTextCharacters() returns:

+ *
START_DOCUMENT
null + *
END_DOCUMENT
null + *
START_TAG
null unless FEATURE_XML_ROUNDTRIP + * enabled and then returns XML tag, ex: <tag attr='val'> + *
END_TAG
null unless FEATURE_XML_ROUNDTRIP + * id enabled and then returns XML tag, ex: </tag> + *
TEXT
return element content. + *
Note: that element content may be delivered in multiple consecutive TEXT events. + *
IGNORABLE_WHITESPACE
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 otherwise reporting is optional. + *
Note: that element content may be delivered in multiple consecutive IGNORABLE_WHITESPACE events. + *
CDSECT
+ * return text inside CDATA + * (ex. 'fo<o' from <!CDATA[fo<o]]>) + *
PROCESSING_INSTRUCTION
+ * 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. + *
COMMENT
return comment content ex. 'foo bar' from <!--foo bar--> + *
ENTITY_REF
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;). + *
NOTE: this is the only place where value returned from getText() and + * getTextCharacters() are different + *
NOTE: 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) + *
NOTE: character entities (ex. &#32;) and standard entities such as + * &amp; &lt; &gt; &quot; &apos; are reported as well + * and are not reported as TEXT tokens but as ENTITY_REF tokens! + * This requirement is added to allow to do roundtrip of XML documents! + *
DOCDECL
+ * if FEATURE_XML_ROUNDTRIP is true or PROCESS_DOCDECL is false + * then return what is inside of DOCDECL for example it returns:
+     * " titlepage SYSTEM "http://www.foo.bar/dtds/typo.dtd"
+     * [<!ENTITY % active.links "INCLUDE">]"
+ *

for input document that contained:

+     * <!DOCTYPE titlepage SYSTEM "http://www.foo.bar/dtds/typo.dtd"
+     * [<!ENTITY % active.links "INCLUDE">]>
+ * otherwise if FEATURE_XML_ROUNDTRIP is false and PROCESS_DOCDECL is true + * then what is returned is undefined (it may be even null) + *
+ *
+ * + *

NOTE: there is no guarantee 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) + * + *

NOTE: whether returned text of token is end-of-line normalized + * is depending on FEATURE_XML_ROUNDTRIP. + * + *

NOTE: 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. + * + *

Essentially it does this + *

+     *  if (type != getEventType()
+     *  || (namespace != null &&  !namespace.equals( getNamespace () ) )
+     *  || (name != null &&  !name.equals( getName() ) ) )
+     *     throw new XmlPullParserException( "expected "+ TYPES[ type ]+getPositionDescription());
+     * 
+ */ + 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. + * + *

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:

    + *
  1. <tag>foo</tag> + *
  2. <tag></tag> (which is equivalent to <tag/> + * both input can be parsed with the same code: + *
    +     *   p.nextTag()
    +     *   p.requireEvent(p.START_TAG, "", "tag");
    +     *   String content = p.nextText();
    +     *   p.requireEvent(p.END_TAG, "", "tag");
    +     * 
    + * This function together with nextTag make it very easy to parse XML that has + * no mixed content. + * + * + *

    Essentially it does this + *

    +     *  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);
    +     *  }
    +     * 
    + * + *

    Warning: Prior to API level 14, the pull parser returned by {@code + * android.util.Xml} did not always advance to the END_TAG event when this method was called. + * Work around by using manually advancing after calls to nextText():

    +     *  String text = xpp.nextText();
    +     *  if (xpp.getEventType() != XmlPullParser.END_TAG) {
    +     *      xpp.next();
    +     *  }
    +     * 
    + */ + 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. + * + *

    essentially it does this + *

    +     *   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;
    +     * 
    + */ + int nextTag() throws XmlPullParserException, IOException; + +} diff --git a/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlPullParserException.java b/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlPullParserException.java new file mode 100644 index 000000000..b4b4b7135 --- /dev/null +++ b/vtm-web/src/org/oscim/gdx/emu/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 Aleksander Slominski + */ +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/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlPullParserFactory.java b/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlPullParserFactory.java new file mode 100644 index 000000000..35e725d08 --- /dev/null +++ b/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlPullParserFactory.java @@ -0,0 +1,237 @@ +/* -*- 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.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * This class is used to create implementations of XML Pull Parser defined in XMPULL V1 API. + * + * @see XmlPullParser + * + * @author Aleksander Slominski + * @author Stefan Haustein + */ + +public class XmlPullParserFactory { + + public static final String PROPERTY_NAME = "org.xmlpull.v1.XmlPullParserFactory"; + protected ArrayList parserClasses; + protected ArrayList serializerClasses; + + /** Unused, but we have to keep it because it's public API. */ + protected String classNamesLocation = null; + + // features are kept there + // TODO: This can't be made final because it's a public API. + protected HashMap features = new HashMap(); + + /** + * Protected constructor to be called by factory implementations. + */ + protected XmlPullParserFactory() { + parserClasses = new ArrayList(); + serializerClasses = new ArrayList(); + + // GWT + /*try { + parserClasses.add(Class.forName("com.android.org.kxml2.io.KXmlParser")); + serializerClasses.add(Class.forName("com.android.org.kxml2.io.KXmlSerializer")); + } catch (ClassNotFoundException e) { + throw new AssertionError(); + }*/ + } + + /** + * Set the features to be set when XML Pull Parser is created by this factory. + *

    NOTE: 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, state); + } + + + /** + * Return the current value of the feature with given name. + *

    NOTE: 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 always returned as false + */ + public boolean getFeature(String name) { + Boolean value = 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, 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, 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. + */ + public XmlPullParser newPullParser() throws XmlPullParserException { + final XmlPullParser pp = getParserInstance(); + for (Map.Entry entry : features.entrySet()) { + // NOTE: This test is needed for compatibility reasons. We guarantee + // that we only set a feature on a parser if its value is true. + if (entry.getValue()) { + pp.setFeature(entry.getKey(), entry.getValue()); + } + } + + return pp; + } + + private XmlPullParser getParserInstance() throws XmlPullParserException { + ArrayList exceptions = null; + + if (parserClasses != null && !parserClasses.isEmpty()) { + exceptions = new ArrayList(); + for (Object o : parserClasses) { + // GWT + /*try { + if (o != null) { + Class parserClass = (Class) o; + return (XmlPullParser) parserClass.newInstance(); + } + } catch (InstantiationException e) { + exceptions.add(e); + } catch (IllegalAccessException e) { + exceptions.add(e); + } catch (ClassCastException e) { + exceptions.add(e); + }*/ + } + } + + throw newInstantiationException("Invalid parser class list", exceptions); + } + + private XmlSerializer getSerializerInstance() throws XmlPullParserException { + ArrayList exceptions = null; + + if (serializerClasses != null && !serializerClasses.isEmpty()) { + exceptions = new ArrayList(); + for (Object o : serializerClasses) { + // GWT + /*try { + if (o != null) { + Class serializerClass = (Class) o; + return (XmlSerializer) serializerClass.newInstance(); + } + } catch (InstantiationException e) { + exceptions.add(e); + } catch (IllegalAccessException e) { + exceptions.add(e); + } catch (ClassCastException e) { + exceptions.add(e); + }*/ + } + } + + throw newInstantiationException("Invalid serializer class list", exceptions); + } + + private static XmlPullParserException newInstantiationException(String message, + ArrayList exceptions) { + if (exceptions == null || exceptions.isEmpty()) { + return new XmlPullParserException(message); + } else { + XmlPullParserException exception = new XmlPullParserException(message); + for (Exception ex : exceptions) { + exception.addSuppressed(ex); + } + + return exception; + } + } + + /** + * Creates a new instance of a XML Serializer. + * + *

    NOTE: 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 { + return getSerializerInstance(); + } + + /** + * Creates a new instance of a PullParserFactory that can be used + * to create XML pull parsers. The factory will always return instances + * of Android's built-in {@link XmlPullParser} and {@link XmlSerializer}. + */ + public static XmlPullParserFactory newInstance () throws XmlPullParserException { + return new XmlPullParserFactory(); + } + + /** + * Creates a factory that always returns instances of Android's built-in + * {@link XmlPullParser} and {@link XmlSerializer} implementation. This + * does not support factories capable of creating arbitrary parser + * and serializer implementations. Both arguments to this method are unused. + */ + public static XmlPullParserFactory newInstance (String unused, Class unused2) + throws XmlPullParserException { + return newInstance(); + } +} diff --git a/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlSerializer.java b/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlSerializer.java new file mode 100644 index 000000000..101a2473f --- /dev/null +++ b/vtm-web/src/org/oscim/gdx/emu/org/xmlpull/v1/XmlSerializer.java @@ -0,0 +1,325 @@ +package org.xmlpull.v1; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; + +/** + * Define an interface to serialization of XML Infoset. + * This interface abstracts away if serialized XML is XML 1.0 compatible text or + * other formats of XML 1.0 serializations (such as binary XML for example with WBXML). + * + *

    PLEASE NOTE: This interface will be part of XmlPull 1.2 API. + * It is included as basis for discussion. It may change in any way. + * + *

    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): + *

      + *
    • IllegalArgumentException - for almost all methods to signal that + * argument is illegal + *
    • IllegalStateException - 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) + *
    + * + *

    NOTE: 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 recommended + * 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 + * + * http://www.xmlpull.org/v1/doc/features.html. + * + * If feature is not recognized 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. + *

    NOTE: unknown properties are always 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 recommended to be URI for uniqueness). + * Some well known optional properties are defined in + * + * http://www.xmlpull.org/v1/doc/properties.html. + * + * If property is not recognized 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 + *

    NOTE: unknown properties are always 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. + *

    WARNING 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 xmlns:prefix='namespace' + * (or xmlns:prefix="namespace" depending what character is used + * to quote attribute value). + * + *

    NOTE: this method MUST be called directly before startTag() + * and if anything but startTag() or setPrefix() is called next there will be exception. + *

    NOTE: prefixes "xml" and "xmlns" are already bound + * and can not be redefined see: + * Namespaces in XML Errata. + *

    NOTE: 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. + * + *

    NOTE: if the prefix is empty string "" and default namespace is bound + * to this prefix then empty string ("") is returned. + * + *

    NOTE: prefixes "xml" and "xmlns" are already bound + * will have values as defined + * Namespaces in XML specification + */ + 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. + * + *

    +     * <!-- outside -->     0
    +     * <root>               1
    +     *   sometext                 1
    +     *     <foobar>         2
    +     *     </foobar>        2
    +     * </root>              1
    +     * <!-- outside -->     0
    +     * 
    + */ + int getDepth(); + + /** + * Returns the namespace URI of the current element as set by startTag(). + * + *

    NOTE: that means in particular that:

      + *
    • if there was startTag("", ...) then getNamespace() returns "" + *
    • if there was startTag(null, ...) then getNamespace() returns null + *
    + * + * @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 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 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. + *

    Background: 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 serializer 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. + // *
    If there is no prefix defined (prefix == null) for the given namespace, + // * a prefix will be defined automatically. + // *
    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. + // *
    If namespace is null then prefix must be null too or IllegalStateException is thrown. + // *
    If namespace is null then no namespace prefix is printed but just name. + // *
    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. + // *
    If there is no prefix defined (prefix == null) for the given namespace, + // * a prefix will be defined automatically. + // *
    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. + // *
    If namespace is null then prefix must be null too or IllegalStateException is thrown. + // *
    If namespace is null then no namespace prefix is printed but just name. + // *
    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. + // *
    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. + // *
    If namespace is null then prefix must be null too or IllegalStateException is thrown. + // *
    If namespace is null then no namespace prefix is printed but just name. + // *
    If namespace is empty string then serializer will make sure that + // * default empty namespace is declared (in XML 1.0 xmlns=''). + // *

    Background: in kXML endTag had no arguments, and non matching tags were + // * very difficult to find...

    + // */ + // 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. + * + *

    NOTE: if there is need to close start tag + * (so no more attribute() calls are allowed) but without flushing output + * call method text() with empty string (text("")). + * + */ + void flush () + throws IOException; + +} diff --git a/vtm/build.gradle b/vtm/build.gradle index bd4b69c91..25372d7e6 100644 --- a/vtm/build.gradle +++ b/vtm/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'java-library' apply plugin: 'maven' dependencies { + api 'net.sf.kxml:kxml2:2.3.0' api "org.slf4j:slf4j-api:$slf4jVersion" compileOnly 'com.google.code.findbugs:jsr305:3.0.2' } diff --git a/vtm/src/org/oscim/theme/XmlAtlasThemeBuilder.java b/vtm/src/org/oscim/theme/XmlAtlasThemeBuilder.java index 6eaf241f0..9e99c6410 100644 --- a/vtm/src/org/oscim/theme/XmlAtlasThemeBuilder.java +++ b/vtm/src/org/oscim/theme/XmlAtlasThemeBuilder.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Longri - * Copyright 2017-2018 devemux86 + * Copyright 2017-2020 devemux86 * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software @@ -17,7 +17,6 @@ import org.oscim.backend.CanvasAdapter; import org.oscim.backend.Platform; -import org.oscim.backend.XMLReaderAdapter; import org.oscim.backend.canvas.Bitmap; import org.oscim.renderer.atlas.TextureAtlas; import org.oscim.renderer.atlas.TextureRegion; @@ -26,8 +25,11 @@ import org.oscim.theme.styles.RenderStyle; import org.oscim.theme.styles.SymbolStyle; import org.oscim.theme.styles.SymbolStyle.SymbolBuilder; +import org.oscim.utils.IOUtils; import org.oscim.utils.TextureAtlasUtils; +import org.xmlpull.v1.XmlPullParser; +import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -51,20 +53,23 @@ public static IRenderTheme read(ThemeFile theme) throws ThemeException { * @throws ThemeException if an error occurs while parsing the render theme XML. */ public static IRenderTheme read(ThemeFile theme, ThemeCallback themeCallback) throws ThemeException { - Map outputMap = new HashMap<>(); - List atlasList = new ArrayList<>(); - XmlAtlasThemeBuilder renderThemeHandler = new XmlAtlasThemeBuilder(theme, themeCallback, outputMap, atlasList); - + InputStream inputStream = null; try { - new XMLReaderAdapter().parse(renderThemeHandler, theme.getRenderThemeAsStream()); + XmlPullParser pullParser = getXmlPullParserFactory().newPullParser(); + Map outputMap = new HashMap<>(); + List atlasList = new ArrayList<>(); + XmlAtlasThemeBuilder renderThemeHandler = new XmlAtlasThemeBuilder(theme, pullParser, themeCallback, outputMap, atlasList); + inputStream = theme.getRenderThemeAsStream(); + pullParser.setInput(inputStream, null); + renderThemeHandler.processRenderTheme(); + TextureAtlasUtils.createTextureRegions(renderThemeHandler.bitmapMap, outputMap, atlasList, + true, CanvasAdapter.platform == Platform.IOS); + return replaceThemeSymbols(renderThemeHandler.mRenderTheme, outputMap); } catch (Exception e) { throw new ThemeException(e.getMessage()); + } finally { + IOUtils.closeQuietly(inputStream); } - - TextureAtlasUtils.createTextureRegions(renderThemeHandler.bitmapMap, outputMap, atlasList, - true, CanvasAdapter.platform == Platform.IOS); - - return replaceThemeSymbols(renderThemeHandler.mRenderTheme, outputMap); } private static IRenderTheme replaceThemeSymbols(RenderTheme renderTheme, Map regionMap) { @@ -98,14 +103,14 @@ private static void replaceRuleSymbols(Rule rule, Map reg private final Map bitmapMap = new HashMap<>(); - public XmlAtlasThemeBuilder(ThemeFile theme, + public XmlAtlasThemeBuilder(ThemeFile theme, XmlPullParser pullParser, Map regionMap, List atlasList) { - this(theme, null, regionMap, atlasList); + this(theme, pullParser, null, regionMap, atlasList); } - public XmlAtlasThemeBuilder(ThemeFile theme, ThemeCallback themeCallback, + public XmlAtlasThemeBuilder(ThemeFile theme, XmlPullParser pullParser, ThemeCallback themeCallback, Map regionMap, List atlasList) { - super(theme, themeCallback); + super(theme, pullParser, themeCallback); this.regionMap = regionMap; this.atlasList = atlasList; } diff --git a/vtm/src/org/oscim/theme/XmlThemeBuilder.java b/vtm/src/org/oscim/theme/XmlThemeBuilder.java index 87bbee6e8..c660dadb7 100644 --- a/vtm/src/org/oscim/theme/XmlThemeBuilder.java +++ b/vtm/src/org/oscim/theme/XmlThemeBuilder.java @@ -1,7 +1,8 @@ /* - * Copyright 2010, 2011, 2012 mapsforge.org + * Copyright 2010, 2011, 2012, 2013 mapsforge.org * Copyright 2013 Hannes Janetzek - * Copyright 2016-2019 devemux86 + * Copyright 2014 Ludwig M Brinckmann + * Copyright 2016-2020 devemux86 * Copyright 2016-2017 Longri * Copyright 2016-2020 Andrey Novikov * Copyright 2018-2019 Gustl22 @@ -24,7 +25,6 @@ package org.oscim.theme; import org.oscim.backend.CanvasAdapter; -import org.oscim.backend.XMLReaderAdapter; import org.oscim.backend.canvas.Bitmap; import org.oscim.backend.canvas.Canvas; import org.oscim.backend.canvas.Color; @@ -49,27 +49,29 @@ import org.oscim.theme.styles.SymbolStyle.SymbolBuilder; import org.oscim.theme.styles.TextStyle.TextBuilder; import org.oscim.utils.FastMath; +import org.oscim.utils.IOUtils; import org.oscim.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.helpers.DefaultHandler; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; import java.io.IOException; +import java.io.InputStream; import java.util.*; import static java.lang.Boolean.parseBoolean; import static java.lang.Float.parseFloat; import static java.lang.Integer.parseInt; -public class XmlThemeBuilder extends DefaultHandler { +public class XmlThemeBuilder { private static final Logger log = LoggerFactory.getLogger(XmlThemeBuilder.class); private static final int RENDER_THEME_VERSION_MAPSFORGE = 6; private static final int RENDER_THEME_VERSION_VTM = 1; + private static XmlPullParserFactory xmlPullParserFactory = null; private enum Element { RENDER_THEME, RENDERING_INSTRUCTION, RULE, STYLE, ATLAS, RECT, RENDERING_STYLE, TAG_TRANSFORM @@ -109,15 +111,30 @@ public static IRenderTheme read(ThemeFile theme) throws ThemeException { * @throws ThemeException if an error occurs while parsing the render theme XML. */ public static IRenderTheme read(ThemeFile theme, ThemeCallback themeCallback) throws ThemeException { - XmlThemeBuilder renderThemeHandler = new XmlThemeBuilder(theme, themeCallback); - + InputStream inputStream = null; try { - new XMLReaderAdapter().parse(renderThemeHandler, theme.getRenderThemeAsStream()); + XmlPullParser pullParser = getXmlPullParserFactory().newPullParser(); + XmlThemeBuilder renderThemeHandler = new XmlThemeBuilder(theme, pullParser, themeCallback); + inputStream = theme.getRenderThemeAsStream(); + pullParser.setInput(inputStream, null); + renderThemeHandler.processRenderTheme(); + return renderThemeHandler.mRenderTheme; } catch (Exception e) { throw new ThemeException(e.getMessage()); + } finally { + IOUtils.closeQuietly(inputStream); } + } - return renderThemeHandler.mRenderTheme; + public static XmlPullParserFactory getXmlPullParserFactory() throws XmlPullParserException { + if (xmlPullParserFactory == null) { + xmlPullParserFactory = XmlPullParserFactory.newInstance(); + } + return xmlPullParserFactory; + } + + public static void setXmlPullParserFactory(XmlPullParserFactory xmlPullParserFactory) { + XmlThemeBuilder.xmlPullParserFactory = xmlPullParserFactory; } /** @@ -128,19 +145,17 @@ public static IRenderTheme read(ThemeFile theme, ThemeCallback themeCallback) th * @param value the XML attribute value. * @param attributeIndex the XML attribute index position. */ - private static void logUnknownAttribute(String element, String name, - String value, int attributeIndex) { - log.debug("unknown attribute in element {} () : {} = {}", - element, attributeIndex, name, value); + private static void logUnknownAttribute(String element, String name, String value, int attributeIndex) { + log.debug("unknown attribute in element {} {} : {} = {}", element, attributeIndex, name, value); } private final ArrayList mRulesList = new ArrayList<>(); private final Stack mElementStack = new Stack<>(); private final Stack mRuleStack = new Stack<>(); - private final HashMap mStyles = new HashMap<>(10); + private final Map> mStyles = new HashMap<>(10); - private final HashMap> mTextStyles = new HashMap<>(10); - private final HashMap> mSymbolStyles = new HashMap<>(10); + private final Map> mTextStyles = new HashMap<>(10); + private final Map> mSymbolStyles = new HashMap<>(10); private final AreaBuilder mAreaBuilder = AreaStyle.builder(); private final CircleBuilder mCircleBuilder = CircleStyle.builder(); @@ -157,6 +172,8 @@ private static void logUnknownAttribute(String element, String name, private float mStrokeScale = 1; float mTextScale = 1; + private final XmlPullParser mPullParser; + private String qName; final ThemeFile mTheme; private final ThemeCallback mThemeCallback; RenderTheme mRenderTheme; @@ -168,21 +185,38 @@ private static void logUnknownAttribute(String element, String name, private XmlRenderThemeStyleLayer mCurrentLayer; private XmlRenderThemeStyleMenu mRenderThemeStyleMenu; - private Map mTransformKeyMap = new HashMap<>(); - private Map mTransformTagMap = new HashMap<>(); + private final Map mTransformKeyMap = new HashMap<>(); + private final Map mTransformTagMap = new HashMap<>(); - public XmlThemeBuilder(ThemeFile theme) { - this(theme, null); + public XmlThemeBuilder(ThemeFile theme, XmlPullParser pullParser) { + this(theme, pullParser, null); } - public XmlThemeBuilder(ThemeFile theme, ThemeCallback themeCallback) { + public XmlThemeBuilder(ThemeFile theme, XmlPullParser pullParser, ThemeCallback themeCallback) { mTheme = theme; + mPullParser = pullParser; mThemeCallback = themeCallback; mMapsforgeTheme = theme.isMapsforgeTheme(); mScale = CanvasAdapter.getScale(); } - @Override + public void processRenderTheme() throws XmlPullParserException, IOException { + int eventType = mPullParser.getEventType(); + do { + if (eventType == XmlPullParser.START_DOCUMENT) { + // no-op + } else if (eventType == XmlPullParser.START_TAG) { + startElement(); + } else if (eventType == XmlPullParser.END_TAG) { + endElement(); + } else if (eventType == XmlPullParser.TEXT) { + // not implemented + } + eventType = mPullParser.next(); + } while (eventType != XmlPullParser.END_DOCUMENT); + endDocument(); + } + public void endDocument() { if (mMapsforgeTheme) { // Building rule for Mapsforge themes @@ -209,11 +243,12 @@ RenderTheme createTheme(Rule[] rules) { return new RenderTheme(mMapBackground, mTextScale, rules, mLevels, mTransformKeyMap, mTransformTagMap, mMapsforgeTheme); } - @Override - public void endElement(String uri, String localName, String qName) { + public void endElement() { + qName = mPullParser.getName(); + mElementStack.pop(); - if (ELEMENT_NAME_MATCH_MAPSFORGE.equals(localName) || ELEMENT_NAME_MATCH_VTM.equals(localName)) { + if (ELEMENT_NAME_MATCH_MAPSFORGE.equals(qName) || ELEMENT_NAME_MATCH_VTM.equals(qName)) { mRuleStack.pop(); if (mRuleStack.empty()) { if (isVisible(mCurrentRule)) { @@ -222,7 +257,7 @@ public void endElement(String uri, String localName, String qName) { } else { mCurrentRule = mRuleStack.peek(); } - } else if (ELEMENT_NAME_STYLE_MENU.equals(localName)) { + } else if (ELEMENT_NAME_STYLE_MENU.equals(qName)) { // when we are finished parsing the menu part of the file, we can get the // categories to render from the initiator. This allows the creating action // to select which of the menu options to choose @@ -233,118 +268,108 @@ public void endElement(String uri, String localName, String qName) { } } - @Override - public void error(SAXParseException exception) { - log.debug(exception.getMessage()); - } - - @Override - public void warning(SAXParseException exception) { - log.debug(exception.getMessage()); - } + public void startElement() throws ThemeException { + qName = mPullParser.getName(); - @Override - public void startElement(String uri, String localName, String qName, - Attributes attributes) throws ThemeException { try { - if (ELEMENT_NAME_RENDER_THEME.equals(localName)) { - checkState(localName, Element.RENDER_THEME); - createRenderTheme(localName, attributes); + if (ELEMENT_NAME_RENDER_THEME.equals(qName)) { + checkState(qName, Element.RENDER_THEME); + createRenderTheme(qName); - } else if (ELEMENT_NAME_MATCH_MAPSFORGE.equals(localName) || ELEMENT_NAME_MATCH_VTM.equals(localName)) { - checkState(localName, Element.RULE); - RuleBuilder rule = createRule(localName, attributes); + } else if (ELEMENT_NAME_MATCH_MAPSFORGE.equals(qName) || ELEMENT_NAME_MATCH_VTM.equals(qName)) { + checkState(qName, Element.RULE); + RuleBuilder rule = createRule(qName); if (!mRuleStack.empty() && isVisible(rule)) { mCurrentRule.addSubRule(rule); } mCurrentRule = rule; mRuleStack.push(mCurrentRule); - } else if ("style-text".equals(localName)) { - checkState(localName, Element.STYLE); - handleTextElement(localName, attributes, true, false); + } else if ("style-text".equals(qName)) { + checkState(qName, Element.STYLE); + handleTextElement(qName, true, false); - } else if ("style-symbol".equals(localName)) { - checkState(localName, Element.STYLE); - handleSymbolElement(localName, attributes, true); + } else if ("style-symbol".equals(qName)) { + checkState(qName, Element.STYLE); + handleSymbolElement(qName, true); - } else if ("style-area".equals(localName)) { - checkState(localName, Element.STYLE); - handleAreaElement(localName, attributes, true); + } else if ("style-area".equals(qName)) { + checkState(qName, Element.STYLE); + handleAreaElement(qName, true); - } else if ("style-line".equals(localName)) { - checkState(localName, Element.STYLE); - handleLineElement(localName, attributes, true, false); + } else if ("style-line".equals(qName)) { + checkState(qName, Element.STYLE); + handleLineElement(qName, true, false); - } else if ("outline-layer".equals(localName)) { - checkState(localName, Element.RENDERING_INSTRUCTION); - LineStyle line = createLine(null, localName, attributes, mLevels++, true, false); + } else if ("outline-layer".equals(qName)) { + checkState(qName, Element.RENDERING_INSTRUCTION); + LineStyle line = createLine(null, qName, mLevels++, true, false); mStyles.put(OUTLINE_STYLE + line.style, line); - } else if ("area".equals(localName)) { - checkState(localName, Element.RENDERING_INSTRUCTION); - handleAreaElement(localName, attributes, false); + } else if ("area".equals(qName)) { + checkState(qName, Element.RENDERING_INSTRUCTION); + handleAreaElement(qName, false); - } else if ("caption".equals(localName)) { - checkState(localName, Element.RENDERING_INSTRUCTION); - handleTextElement(localName, attributes, false, true); + } else if ("caption".equals(qName)) { + checkState(qName, Element.RENDERING_INSTRUCTION); + handleTextElement(qName, false, true); - } else if ("circle".equals(localName)) { - checkState(localName, Element.RENDERING_INSTRUCTION); - CircleStyle circle = createCircle(localName, attributes, mLevels++); + } else if ("circle".equals(qName)) { + checkState(qName, Element.RENDERING_INSTRUCTION); + CircleStyle circle = createCircle(qName, mLevels++); if (isVisible(circle)) mCurrentRule.addStyle(circle); - } else if ("line".equals(localName)) { - checkState(localName, Element.RENDERING_INSTRUCTION); - handleLineElement(localName, attributes, false, false); + } else if ("line".equals(qName)) { + checkState(qName, Element.RENDERING_INSTRUCTION); + handleLineElement(qName, false, false); - } else if ("text".equals(localName) || "pathText".equals(localName)) { - checkState(localName, Element.RENDERING_INSTRUCTION); - handleTextElement(localName, attributes, false, false); + } else if ("text".equals(qName) || "pathText".equals(qName)) { + checkState(qName, Element.RENDERING_INSTRUCTION); + handleTextElement(qName, false, false); - } else if ("symbol".equals(localName)) { - checkState(localName, Element.RENDERING_INSTRUCTION); - handleSymbolElement(localName, attributes, false); + } else if ("symbol".equals(qName)) { + checkState(qName, Element.RENDERING_INSTRUCTION); + handleSymbolElement(qName, false); - } else if ("outline".equals(localName)) { - checkState(localName, Element.RENDERING_INSTRUCTION); - LineStyle outline = createOutline(attributes.getValue("use"), attributes); + } else if ("outline".equals(qName)) { + checkState(qName, Element.RENDERING_INSTRUCTION); + LineStyle outline = createOutline(getStringAttribute("use")); if (outline != null && isVisible(outline)) mCurrentRule.addStyle(outline); - } else if ("extrusion".equals(localName)) { - checkState(localName, Element.RENDERING_INSTRUCTION); - ExtrusionStyle extrusion = createExtrusion(localName, attributes, mLevels++); + } else if ("extrusion".equals(qName)) { + checkState(qName, Element.RENDERING_INSTRUCTION); + ExtrusionStyle extrusion = createExtrusion(qName, mLevels++); if (isVisible(extrusion)) mCurrentRule.addStyle(extrusion); - } else if ("lineSymbol".equals(localName)) { - checkState(localName, Element.RENDERING_INSTRUCTION); - handleLineElement(localName, attributes, false, true); + } else if ("lineSymbol".equals(qName)) { + checkState(qName, Element.RENDERING_INSTRUCTION); + handleLineElement(qName, false, true); - } else if ("atlas".equals(localName)) { - checkState(localName, Element.ATLAS); - createAtlas(localName, attributes); + } else if ("atlas".equals(qName)) { + checkState(qName, Element.ATLAS); + createAtlas(qName); - } else if ("rect".equals(localName)) { - checkState(localName, Element.RECT); - createTextureRegion(localName, attributes); + } else if ("rect".equals(qName)) { + checkState(qName, Element.RECT); + createTextureRegion(qName); - } else if ("cat".equals(localName)) { + } else if ("cat".equals(qName)) { checkState(qName, Element.RENDERING_STYLE); - mCurrentLayer.addCategory(getStringAttribute(attributes, "id")); + mCurrentLayer.addCategory(getStringAttribute("id")); - } else if ("layer".equals(localName)) { + } else if ("layer".equals(qName)) { // render theme menu layer checkState(qName, Element.RENDERING_STYLE); boolean enabled = false; - if (getStringAttribute(attributes, "enabled") != null) { - enabled = Boolean.valueOf(getStringAttribute(attributes, "enabled")); + if (getStringAttribute("enabled") != null) { + enabled = Boolean.parseBoolean(getStringAttribute("enabled")); } - boolean visible = Boolean.valueOf(getStringAttribute(attributes, "visible")); - mCurrentLayer = mRenderThemeStyleMenu.createLayer(getStringAttribute(attributes, "id"), visible, enabled); - String parent = getStringAttribute(attributes, "parent"); + boolean visible = Boolean.parseBoolean(getStringAttribute("visible")); + mCurrentLayer = mRenderThemeStyleMenu.createLayer(getStringAttribute("id"), visible, enabled); + String parent = getStringAttribute("parent"); if (null != parent) { XmlRenderThemeStyleLayer parentEntry = mRenderThemeStyleMenu.getLayer(parent); if (null != parentEntry) { @@ -357,40 +382,38 @@ public void startElement(String uri, String localName, String qName, } } - } else if ("name".equals(localName)) { + } else if ("name".equals(qName)) { // render theme menu name checkState(qName, Element.RENDERING_STYLE); - mCurrentLayer.addTranslation(getStringAttribute(attributes, "lang"), getStringAttribute(attributes, "value")); + mCurrentLayer.addTranslation(getStringAttribute("lang"), getStringAttribute("value")); - } else if ("overlay".equals(localName)) { + } else if ("overlay".equals(qName)) { // render theme menu overlay checkState(qName, Element.RENDERING_STYLE); - XmlRenderThemeStyleLayer overlay = mRenderThemeStyleMenu.getLayer(getStringAttribute(attributes, "id")); + XmlRenderThemeStyleLayer overlay = mRenderThemeStyleMenu.getLayer(getStringAttribute("id")); if (overlay != null) { mCurrentLayer.addOverlay(overlay); } - } else if ("stylemenu".equals(localName)) { + } else if ("stylemenu".equals(qName)) { checkState(qName, Element.RENDERING_STYLE); - mRenderThemeStyleMenu = new XmlRenderThemeStyleMenu(getStringAttribute(attributes, "id"), - getStringAttribute(attributes, "defaultlang"), getStringAttribute(attributes, "defaultvalue")); + mRenderThemeStyleMenu = new XmlRenderThemeStyleMenu(getStringAttribute("id"), + getStringAttribute("defaultlang"), getStringAttribute("defaultvalue")); - } else if ("tag-transform".equals(localName)) { + } else if ("tag-transform".equals(qName)) { checkState(qName, Element.TAG_TRANSFORM); - tagTransform(localName, attributes); + tagTransform(qName); } else { - log.error("unknown element: {}", localName); - throw new SAXException("unknown element: " + localName); + log.error("unknown element: {}", qName); + throw new XmlPullParserException("unknown element: " + qName); } - } catch (SAXException e) { - throw new ThemeException(e.getMessage()); - } catch (IOException e) { + } catch (XmlPullParserException | IOException e) { throw new ThemeException(e.getMessage()); } } - private RuleBuilder createRule(String localName, Attributes attributes) { + private RuleBuilder createRule(String qName) { String cat = null; int element = Rule.Element.ANY; int closed = Closed.ANY; @@ -400,9 +423,9 @@ private RuleBuilder createRule(String localName, Attributes attributes) { byte zoomMax = Byte.MAX_VALUE; int selector = 0; - for (int i = 0; i < attributes.getLength(); i++) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("e".equals(name)) { String val = value.toUpperCase(Locale.ENGLISH); @@ -440,7 +463,7 @@ else if ("NO".equals(val)) if ("when-matched".equals(value)) selector |= Selector.WHEN_MATCHED; } else { - logUnknownAttribute(localName, name, value, i); + logUnknownAttribute(qName, name, value, i); } } @@ -474,10 +497,9 @@ private TextureRegion getAtlasRegion(String src) { return texture; } - private void handleLineElement(String localName, Attributes attributes, boolean isStyle, boolean hasSymbol) - throws SAXException { + private void handleLineElement(String qName, boolean isStyle, boolean hasSymbol) { - String use = attributes.getValue("use"); + String use = getStringAttribute("use"); LineStyle style = null; if (use != null) { @@ -488,7 +510,7 @@ private void handleLineElement(String localName, Attributes attributes, boolean } } - LineStyle line = createLine(style, localName, attributes, mLevels++, false, hasSymbol); + LineStyle line = createLine(style, qName, mLevels++, false, hasSymbol); if (isStyle) { mStyles.put(LINE_STYLE + line.style, line); @@ -497,9 +519,9 @@ private void handleLineElement(String localName, Attributes attributes, boolean mCurrentRule.addStyle(line); /* Note 'outline' will not be inherited, it's just a * shortcut to add the outline RenderInstruction. */ - String outlineValue = attributes.getValue("outline"); + String outlineValue = getStringAttribute("outline"); if (outlineValue != null) { - LineStyle outline = createOutline(outlineValue, attributes); + LineStyle outline = createOutline(outlineValue); if (outline != null) mCurrentRule.addStyle(outline); } @@ -513,17 +535,16 @@ private void handleLineElement(String localName, Attributes attributes, boolean * @param isOutline is outline layer * @return a new Line with the given rendering attributes. */ - private LineStyle createLine(LineStyle line, String elementName, Attributes attributes, - int level, boolean isOutline, boolean hasSymbol) { + private LineStyle createLine(LineStyle line, String elementName, int level, boolean isOutline, boolean hasSymbol) { LineBuilder b = mLineBuilder.set(line); b.isOutline(isOutline); b.level(level); b.themeCallback(mThemeCallback); String src = null; - for (int i = 0; i < attributes.getLength(); i++) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("id".equals(name)) b.style = value; @@ -671,10 +692,9 @@ else if ("repeat-gap".equals(name)) return b.build(); } - private void handleAreaElement(String localName, Attributes attributes, boolean isStyle) - throws SAXException { + private void handleAreaElement(String qName, boolean isStyle) { - String use = attributes.getValue("use"); + String use = getStringAttribute("use"); AreaStyle style = null; if (use != null) { @@ -685,7 +705,7 @@ private void handleAreaElement(String localName, Attributes attributes, boolean } } - AreaStyle area = createArea(style, localName, attributes, mLevels++); + AreaStyle area = createArea(style, qName, mLevels++); if (isStyle) { mStyles.put(AREA_STYLE + area.style, area); @@ -698,16 +718,15 @@ private void handleAreaElement(String localName, Attributes attributes, boolean /** * @return a new Area with the given rendering attributes. */ - private AreaStyle createArea(AreaStyle area, String elementName, Attributes attributes, - int level) { + private AreaStyle createArea(AreaStyle area, String elementName, int level) { AreaBuilder b = mAreaBuilder.set(area); b.level(level); b.themeCallback(mThemeCallback); String src = null; - for (int i = 0; i < attributes.getLength(); i++) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("id".equals(name)) b.style = value; @@ -766,15 +785,15 @@ else if ("symbol-scaling".equals(name)) return b.build(); } - private LineStyle createOutline(String style, Attributes attributes) { + private LineStyle createOutline(String style) { if (style != null) { LineStyle line = (LineStyle) mStyles.get(OUTLINE_STYLE + style); if (line != null && line.outline) { String cat = null; - for (int i = 0; i < attributes.getLength(); i++) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("cat".equals(name)) { cat = value; @@ -790,12 +809,12 @@ private LineStyle createOutline(String style, Attributes attributes) { return null; } - private void createAtlas(String elementName, Attributes attributes) throws IOException { + private void createAtlas(String elementName) throws IOException { String img = null; - for (int i = 0; i < attributes.getLength(); i++) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("img".equals(name)) { img = value; @@ -810,16 +829,16 @@ private void createAtlas(String elementName, Attributes attributes) throws IOExc mTextureAtlas = new TextureAtlas(bitmap); } - private void createTextureRegion(String elementName, Attributes attributes) { + private void createTextureRegion(String elementName) { if (mTextureAtlas == null) return; String regionName = null; Rect r = null; - for (int i = 0, n = attributes.getLength(); i < n; i++) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("id".equals(name)) { regionName = value; @@ -841,12 +860,12 @@ private void createTextureRegion(String elementName, Attributes attributes) { mTextureAtlas.addTextureRegion(regionName.intern(), r); } - private void checkElement(String elementName, Element element) throws SAXException { + private void checkElement(String elementName, Element element) throws XmlPullParserException { Element parentElement; switch (element) { case RENDER_THEME: if (!mElementStack.empty()) { - throw new SAXException(UNEXPECTED_ELEMENT_STACK_NOT_EMPTY + elementName); + throw new XmlPullParserException(UNEXPECTED_ELEMENT_STACK_NOT_EMPTY + elementName); } return; @@ -854,34 +873,34 @@ private void checkElement(String elementName, Element element) throws SAXExcepti parentElement = mElementStack.peek(); if (parentElement != Element.RENDER_THEME && parentElement != Element.RULE) { - throw new SAXException(UNEXPECTED_ELEMENT_RULE_PARENT_ELEMENT_MISMATCH + elementName); + throw new XmlPullParserException(UNEXPECTED_ELEMENT_RULE_PARENT_ELEMENT_MISMATCH + elementName); } return; case STYLE: parentElement = mElementStack.peek(); if (parentElement != Element.RENDER_THEME) { - throw new SAXException(UNEXPECTED_ELEMENT_STYLE_PARENT_ELEMENT_MISMATCH + elementName); + throw new XmlPullParserException(UNEXPECTED_ELEMENT_STYLE_PARENT_ELEMENT_MISMATCH + elementName); } return; case RENDERING_INSTRUCTION: if (mElementStack.peek() != Element.RULE) { - throw new SAXException(UNEXPECTED_ELEMENT_RENDERING_INSTRUCTION_PARENT_ELEMENT_MISMATCH + elementName); + throw new XmlPullParserException(UNEXPECTED_ELEMENT_RENDERING_INSTRUCTION_PARENT_ELEMENT_MISMATCH + elementName); } return; case ATLAS: parentElement = mElementStack.peek(); if (parentElement != Element.RENDER_THEME) { - throw new SAXException(UNEXPECTED_ELEMENT_ATLAS_PARENT_ELEMENT_MISMATCH + elementName); + throw new XmlPullParserException(UNEXPECTED_ELEMENT_ATLAS_PARENT_ELEMENT_MISMATCH + elementName); } return; case RECT: parentElement = mElementStack.peek(); if (parentElement != Element.ATLAS) { - throw new SAXException(UNEXPECTED_ELEMENT_RECT_PARENT_ELEMENT_MISMATCH + elementName); + throw new XmlPullParserException(UNEXPECTED_ELEMENT_RECT_PARENT_ELEMENT_MISMATCH + elementName); } return; @@ -891,28 +910,28 @@ private void checkElement(String elementName, Element element) throws SAXExcepti case TAG_TRANSFORM: parentElement = mElementStack.peek(); if (parentElement != Element.RENDER_THEME) { - throw new SAXException(UNEXPECTED_ELEMENT_TAG_TRANSFORM_PARENT_ELEMENT_MISMATCH + elementName); + throw new XmlPullParserException(UNEXPECTED_ELEMENT_TAG_TRANSFORM_PARENT_ELEMENT_MISMATCH + elementName); } return; } - throw new SAXException("unknown enum value: " + element); + throw new XmlPullParserException("unknown enum value: " + element); } - private void checkState(String elementName, Element element) throws SAXException { + private void checkState(String elementName, Element element) throws XmlPullParserException { checkElement(elementName, element); mElementStack.push(element); } - private void createRenderTheme(String elementName, Attributes attributes) { + private void createRenderTheme(String elementName) { Integer version = null; int mapBackground = Color.WHITE; float baseStrokeWidth = 1; float baseTextScale = 1; - for (int i = 0; i < attributes.getLength(); ++i) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("schemaLocation".equals(name)) continue; @@ -950,10 +969,9 @@ else if ("base-text-scale".equals(name) || "base-text-size".equals(name)) mTextScale = baseTextScale; } - private void handleTextElement(String localName, Attributes attributes, boolean isStyle, - boolean isCaption) throws SAXException { + private void handleTextElement(String qName, boolean isStyle, boolean isCaption) { - String style = attributes.getValue("use"); + String style = getStringAttribute("use"); TextBuilder pt = null; if (style != null) { @@ -964,7 +982,7 @@ private void handleTextElement(String localName, Attributes attributes, boolean } } - TextBuilder b = createText(localName, attributes, isCaption, pt); + TextBuilder b = createText(qName, isCaption, pt); if (isStyle) { log.debug("put style {}", b.style); mTextStyles.put(b.style, TextStyle.builder().from(b)); @@ -979,8 +997,7 @@ private void handleTextElement(String localName, Attributes attributes, boolean * @param caption ... * @return a new Text with the given rendering attributes. */ - private TextBuilder createText(String elementName, Attributes attributes, - boolean caption, TextBuilder style) { + private TextBuilder createText(String elementName, boolean caption, TextBuilder style) { TextBuilder b; if (style == null) { b = mTextBuilder.reset(); @@ -995,9 +1012,9 @@ private TextBuilder createText(String elementName, Attributes attributes, b.priority = DEFAULT_PRIORITY; } - for (int i = 0; i < attributes.getLength(); i++) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("id".equals(name)) b.style = value; @@ -1101,14 +1118,14 @@ else if ("position".equals(name)) { * @param level the drawing level of this instruction. * @return a new Circle with the given rendering attributes. */ - private CircleStyle createCircle(String elementName, Attributes attributes, int level) { + private CircleStyle createCircle(String elementName, int level) { CircleBuilder b = mCircleBuilder.reset(); b.level(level); b.themeCallback(mThemeCallback); - for (int i = 0; i < attributes.getLength(); i++) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("r".equals(name) || "radius".equals(name)) b.radius(Float.parseFloat(value) * mScale * mStrokeScale); @@ -1139,10 +1156,9 @@ else if ("stroke-width".equals(name)) return b.build(); } - private void handleSymbolElement(String localName, Attributes attributes, boolean isStyle) - throws SAXException { + private void handleSymbolElement(String qName, boolean isStyle) { - String style = attributes.getValue("use"); + String style = getStringAttribute("use"); SymbolBuilder ps = null; if (style != null) { @@ -1153,7 +1169,7 @@ private void handleSymbolElement(String localName, Attributes attributes, boolea } } - SymbolBuilder b = createSymbol(localName, attributes, ps); + SymbolBuilder b = createSymbol(qName, ps); if (isStyle) { log.debug("put style {}", b.style); mSymbolStyles.put(b.style, SymbolStyle.builder().from(b)); @@ -1167,8 +1183,7 @@ private void handleSymbolElement(String localName, Attributes attributes, boolea /** * @return a new Symbol with the given rendering attributes. */ - private SymbolBuilder createSymbol(String elementName, Attributes attributes, - SymbolBuilder style) { + private SymbolBuilder createSymbol(String elementName, SymbolBuilder style) { SymbolBuilder b; if (style == null) b = mSymbolBuilder.reset(); @@ -1176,9 +1191,9 @@ private SymbolBuilder createSymbol(String elementName, Attributes attributes, b = mSymbolBuilder.from(style); b.themeCallback(mThemeCallback); - for (int i = 0; i < attributes.getLength(); i++) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("id".equals(name)) b.style = value; @@ -1247,14 +1262,14 @@ SymbolStyle buildSymbol(SymbolBuilder b, String src, Bitmap bitmap) { return b.bitmap(bitmap).build(); } - private ExtrusionStyle createExtrusion(String elementName, Attributes attributes, int level) { + private ExtrusionStyle createExtrusion(String elementName, int level) { ExtrusionBuilder b = mExtrusionBuilder.reset(); b.level(level); b.themeCallback(mThemeCallback); - for (int i = 0; i < attributes.getLength(); ++i) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); if ("cat".equals(name)) b.cat(value); @@ -1287,10 +1302,11 @@ else if ("default-height".equals(name)) return b.build(); } - private String getStringAttribute(Attributes attributes, String name) { - for (int i = 0; i < attributes.getLength(); ++i) { - if (attributes.getLocalName(i).equals(name)) { - return attributes.getValue(i); + private String getStringAttribute(String name) { + int n = mPullParser.getAttributeCount(); + for (int i = 0; i < n; i++) { + if (mPullParser.getAttributeName(i).equals(name)) { + return mPullParser.getAttributeValue(i); } } return null; @@ -1300,7 +1316,7 @@ private String getStringAttribute(Attributes attributes, String name) { * A style is visible if categories is not set or the style has no category * or the categories contain the style's category. */ - private boolean isVisible(RenderStyle renderStyle) { + private boolean isVisible(RenderStyle renderStyle) { return mCategories == null || renderStyle.cat == null || mCategories.contains(renderStyle.cat); } @@ -1321,13 +1337,13 @@ private static float[] parseFloatArray(String dashString) { return dashIntervals; } - private void tagTransform(String localName, Attributes attributes) { + private void tagTransform(String qName) { String k, v, libK, libV; k = v = libK = libV = null; - for (int i = 0; i < attributes.getLength(); i++) { - String name = attributes.getLocalName(i); - String value = attributes.getValue(i); + for (int i = 0, n = mPullParser.getAttributeCount(); i < n; ++i) { + String name = mPullParser.getAttributeName(i); + String value = mPullParser.getAttributeValue(i); switch (name) { case "k": @@ -1343,12 +1359,12 @@ private void tagTransform(String localName, Attributes attributes) { libV = value; break; default: - logUnknownAttribute(localName, name, value, i); + logUnknownAttribute(qName, name, value, i); } } if (k == null || k.isEmpty() || libK == null || libK.isEmpty()) { - log.debug("empty key in element " + localName); + log.debug("empty key in element " + qName); return; }