diff --git a/CodenameOne/src/com/codename1/processing/Result.java b/CodenameOne/src/com/codename1/processing/Result.java index 245199c448..a349477ce1 100644 --- a/CodenameOne/src/com/codename1/processing/Result.java +++ b/CodenameOne/src/com/codename1/processing/Result.java @@ -1,1031 +1,1123 @@ -/* - Copyright (c) 2007, Sun Microsystems, Inc. - - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name of Sun Microsystems, Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - Derivative Revision History: - - 2012-03 - derivative work from original Sun source, removed references - to Sun's JSON parser, support for any structured document that implements - a StructuredSource interface. Added globbing and backtracking support - (backed by structured document impl), support for predicate expressions, - nested expressions, and various XPath style features. - - */ -package com.codename1.processing; - -import com.codename1.xml.Element; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; - -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Vector; - -/** - * An evaluator for a very small expression language to extract primitive types - * from structured information. This implementation is layered over the - * {@link com.codename1.io.JSONParser} and {@link com.codename1.xml.XMLParser} classes. This - * expression language allows applications to extract information from - * structured data returned by web services with minimal effort. You can read more about it {@link com.codename1.processing here}. - *

- * The expression language works a lot like a very small subset of XPath - the - * expression syntax uses the / character for sub-elements and square brackets - * for arrays. - *

- * Some sample expressions: - * - *

{@code
- *  Simple expression, get the title of the first photo element.
- *
- *  /photos/photo[1]/title
- *
- *  Globally find the first name of a person with a last name of 'Coolman'.
- *
- *  //person[lastname='Coolman']/firstName
- *
- *  Get the latitude value of the second last result element.
- *
- *  /results[last()-1]/geometry/bounds/northeast/lat
- *
- *  Get the names of players from Germany
- *
- *  /tournament/player[@nationality='Germany']/name
- *
- *  Get the purchase order numbers of any order with a lineitem worth over $5
- *
- *  //order/lineitem[price > 5]/../@ponum
- * etc
- * }
- * - * @author Eric Coolman (2012-03 - derivative work from original Sun source). - */ -public class Result { - - public static final String JSON = "json"; - public static final String XML = "xml"; - public static final char SEPARATOR = '/'; - public static final char ARRAY_START = '['; - public static final char ARRAY_END = ']'; - private static final Object SELECT_GLOB = "//"; - private static final Object SELECT_PARENT = ".."; - - private StructuredContent root; - private Map namespaceAliases; - - /** - * Internal method, do not use. - *

- * Construct an evaluator object from a StructuredContent element. - * - * @param content a parsed dom - * @return Result a result evaluator object - * @throws IllegalArgumentException thrown if null content is passed. - */ - private Result(final StructuredContent obj, boolean subTree) throws IllegalArgumentException { - if (obj == null) { - throw new IllegalArgumentException("dom object cannot be null"); - } - this.root = obj; - if (!subTree && root.getParent() != null) { - root = root.getParent(); // <--- WHY?? - } - } - - /** - * Internal method, do not use. - *

- * Create an evaluator object from a StructuredContent element. - * - * @param content a parsed dom - * @param subTree If true, then this is treated as a subtree, and won't try to take the parent as the actual root. - * @return Result a result evaluator object - * @throws IllegalArgumentException thrown if null content is passed. - */ - static Result fromContent(StructuredContent content, boolean subTree) - throws IllegalArgumentException { - if (content == null) { - throw new IllegalArgumentException("content cannot be null"); - } - return new Result(content, subTree); - } - - // TODO: add a cache mapping subpaths to objects to improve performance - - /** - * Internal method, do not use. - *

- * Create an evaluator object from a StructuredContent element. - * - * @param content a parsed dom - * @return Result a result evaluator object - * @throws IllegalArgumentException thrown if null content is passed. - */ - static Result fromContent(StructuredContent content) { - return fromContent(content, false); - } - - /** - * Create an evaluator object from a structured content document (XML, JSON, - * etc) as a string. - * - * @param content structured content document as a string. - * @param format an identifier for the type of content passed (ie. xml, - * json, etc). - * @return Result a result evaluator object - * @throws IllegalArgumentException thrown if null content or format is - * passed. - */ - public static Result fromContent(String content, String format) - throws IllegalArgumentException { - if (content == null) { - throw new IllegalArgumentException("content cannot be null"); - } - if (format == null) { - throw new IllegalArgumentException("format cannot be null"); - } - try { - return fromContent(new InputStreamReader(new ByteArrayInputStream(content.getBytes("UTF-8")), "UTF-8"), - format); - } catch (IOException e) { - // should never get here with a string - throw new IllegalArgumentException(e.getMessage()); - } - } - - /** - * Create an evaluator object from a structured content document (XML, JSON, - * etc) input stream. Normally you would use this method within a content - * request implementation, for example: - * - *

-     * ConnectionRequest request = new ConnectionRequest() {
-     * 	protected void readResponse(InputStream input) throws IOException {
-     * 		Result evaluator = Result.fromContent(input, Result.JSON);
-     * 		// ... evaluate the result here
-     *    }
-     * 	// ... etc
-     * };
-     * 
- * - * @param content structured content document as a string. - * @param format an identifier for the type of content passed (ie. xml, - * json, etc). - * @return Result a result evaluator object - * @throws IllegalArgumentException thrown if null content or format is - * passed. - */ - public static Result fromContent(InputStream content, String format) - throws IllegalArgumentException, IOException { - if (content == null) { - throw new IllegalArgumentException("content cannot be null"); - } - if (format == null) { - throw new IllegalArgumentException("format cannot be null"); - } - StructuredContent sc; - if ("xml".equals(format)) { - sc = new XMLContent(content); - } else if ("json".equals(format)) { - sc = new JSONContent(content); - } else { - throw new IllegalArgumentException("Unrecognized format: " + format); - } - return fromContent(sc); - } - - /** - * Create an evaluator object from a structured content document (XML, JSON, - * etc) input stream. Normally you would use this method within a content - * request implementation, for example: - * - *
-     * ConnectionRequest request = new ConnectionRequest() {
-     * 	protected void readResponse(InputStream input) throws IOException {
-     * 		Result evaluator = Result.fromContent(input, Result.JSON);
-     * 		// ... evaluate the result here
-     *    }
-     * 	// ... etc
-     * };
-     * 
- * - * @param content structured content document as a string. - * @param format an identifier for the type of content passed (ie. xml, - * json, etc). - * @return Result a result evaluator object - * @throws IllegalArgumentException thrown if null content or format is - * passed. - */ - public static Result fromContent(Reader content, String format) - throws IllegalArgumentException, IOException { - if (content == null) { - throw new IllegalArgumentException("content cannot be null"); - } - if (format == null) { - throw new IllegalArgumentException("format cannot be null"); - } - StructuredContent sc; - if ("xml".equals(format)) { - sc = new XMLContent(content); - } else if ("json".equals(format)) { - sc = new JSONContent(content); - } else { - throw new IllegalArgumentException("Unrecognized format: " + format); - } - return fromContent(sc); - } - - /** - * Create an evaluator object from a parsed XML DOM. - * - * @param content a parsed XML DOM. - * @return Result a result evaluator object - * @throws IllegalArgumentException thrown if null content is passed. - */ - public static Result fromContent(Element content) - throws IllegalArgumentException { - if (content == null) { - throw new IllegalArgumentException("content cannot be null"); - } - return fromContent(new XMLContent(content)); - } - - /** - * Create an evaluator object from parsed JSON content DOM. - * - * @param content JSON content input stream - * @return Result a result evaluator object - */ - public static Result fromContent(Map content) - throws IllegalArgumentException { - if (content == null) { - throw new IllegalArgumentException("content cannot be null"); - } - return fromContent(new MapContent(content)); - } - - /** - * Returns a hashcode value for the object. - * - * @see Object#hashCode() - */ - public int hashCode() { - return root.hashCode(); - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @see Object#equals(Object) - */ - public boolean equals(final Object other) { - return root.equals(other); - } - - /** - * Convert the object to a formatted structured content document. For - * example, an XML or JSON document. - * - * @return a structured content document as a string - */ - public String toString() { - return root.toString(); - } - - /** - * Get a boolean value from the requested path. - *

- * For example: JSON - * - *

-     * {
-     * "settings" : [
-     * {
-     *     "toggle" : "true",
-     *     ... etc
-     * }
-     * 
- * - * Expression - * - *
-     * boolean value = result.getAsBoolean("/settings[0]/toggle");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - */ - public boolean getAsBoolean(final String path) - throws IllegalArgumentException { - String s = getAsString(path); - if (s == null) { - return false; - } - if ("true".equals(s)) { - return true; - } else return "1".equals(s); - } - - /** - * Get an integer value from the requested path. - *

- * For example: JSON - * - *

-     * {
-     * "settings"
-     * {
-     *     "connection"
-     *     {
-     *          "max_retries" : "20",
-     *          ... etc
-     *     }
-     * }
-     * 
- * - * Expression - * - *
-     * int value = result.getAsInteger("//connection/max_retries");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalException on error traversing the document, ie. traversing - * into an array without using subscripts. - */ - public int getAsInteger(final String path) throws IllegalArgumentException { - String s = getAsString(path); - if (s == null) { - return 0; - } - if (s.indexOf('.') > -1) { - return (int) Double.parseDouble(s); - } else { - return Integer.parseInt(s); - } - } - - /** - * Get a long value from the requested path. - *

- * For example: JSON - * - *

-     * {
-     * "settings"
-     * {
-     *     "connection"
-     *     {
-     *          "timeout_milliseconds" : "100000",
-     *          ... etc
-     *     }
-     * }
-     * 
- * - * Expression - * - *
-     * long value = result.getAsLong("/settings/connection/timeout_milliseconds");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - */ - public long getAsLong(final String path) throws IllegalArgumentException { - String s = getAsString(path); - if (s == null) { - return 0; - } +/* + Copyright (c) 2007, Sun Microsystems, Inc. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Sun Microsystems, Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Derivative Revision History: + + 2012-03 - derivative work from original Sun source, removed references + to Sun's JSON parser, support for any structured document that implements + a StructuredSource interface. Added globbing and backtracking support + (backed by structured document impl), support for predicate expressions, + nested expressions, and various XPath style features. + + */ +package com.codename1.processing; + +import com.codename1.xml.Element; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +/** + * An evaluator for a very small expression language to extract primitive types + * from structured information. This implementation is layered over the + * {@link com.codename1.io.JSONParser} and {@link com.codename1.xml.XMLParser} classes. This + * expression language allows applications to extract information from + * structured data returned by web services with minimal effort. You can read more about it {@link com.codename1.processing here}. + *

+ * The expression language works a lot like a very small subset of XPath - the + * expression syntax uses the / character for sub-elements and square brackets + * for arrays. + *

+ * Some sample expressions: + * + *

{@code
+ *  Simple expression, get the title of the first photo element.
+ *
+ *  /photos/photo[1]/title
+ *
+ *  Globally find the first name of a person with a last name of 'Coolman'.
+ *
+ *  //person[lastname='Coolman']/firstName
+ *
+ *  Get the latitude value of the second last result element.
+ *
+ *  /results[last()-1]/geometry/bounds/northeast/lat
+ *
+ *  Get the names of players from Germany
+ *
+ *  /tournament/player[@nationality='Germany']/name
+ *
+ *  Get the purchase order numbers of any order with a lineitem worth over $5
+ *
+ *  //order/lineitem[price > 5]/../@ponum
+ * etc
+ * }
+ * + * @author Eric Coolman (2012-03 - derivative work from original Sun source). + */ +public class Result { + + public static final String JSON = "json"; + public static final String XML = "xml"; + public static final char SEPARATOR = '/'; + public static final char ARRAY_START = '['; + public static final char ARRAY_END = ']'; + private static final Object SELECT_GLOB = "//"; + private static final Object SELECT_PARENT = ".."; + + private StructuredContent root; + private Map namespaceAliases; + + /** + * Internal method, do not use. + *

+ * Construct an evaluator object from a StructuredContent element. + * + * @param content a parsed dom + * @return Result a result evaluator object + * @throws IllegalArgumentException thrown if null content is passed. + */ + private Result(final StructuredContent obj, boolean subTree) throws IllegalArgumentException { + if (obj == null) { + throw new IllegalArgumentException("dom object cannot be null"); + } + this.root = obj; + if (!subTree && root.getParent() != null) { + root = root.getParent(); // <--- WHY?? + } + } + + /** + * Internal method, do not use. + *

+ * Create an evaluator object from a StructuredContent element. + * + * @param content a parsed dom + * @param subTree If true, then this is treated as a subtree, and won't try to take the parent as the actual root. + * @return Result a result evaluator object + * @throws IllegalArgumentException thrown if null content is passed. + */ + static Result fromContent(StructuredContent content, boolean subTree) + throws IllegalArgumentException { + if (content == null) { + throw new IllegalArgumentException("content cannot be null"); + } + return new Result(content, subTree); + } + + // TODO: add a cache mapping subpaths to objects to improve performance + + /** + * Internal method, do not use. + *

+ * Create an evaluator object from a StructuredContent element. + * + * @param content a parsed dom + * @return Result a result evaluator object + * @throws IllegalArgumentException thrown if null content is passed. + */ + static Result fromContent(StructuredContent content) { + return fromContent(content, false); + } + + /** + * Create an evaluator object from a structured content document (XML, JSON, + * etc) as a string. + * + * @param content structured content document as a string. + * @param format an identifier for the type of content passed (ie. xml, + * json, etc). + * @return Result a result evaluator object + * @throws IllegalArgumentException thrown if null content or format is + * passed. + */ + public static Result fromContent(String content, String format) + throws IllegalArgumentException { + if (content == null) { + throw new IllegalArgumentException("content cannot be null"); + } + if (format == null) { + throw new IllegalArgumentException("format cannot be null"); + } + try { + return fromContent(new InputStreamReader(new ByteArrayInputStream(content.getBytes("UTF-8")), "UTF-8"), + format); + } catch (IOException e) { + // should never get here with a string + throw new IllegalArgumentException(e.getMessage()); + } + } + + /** + * Create an evaluator object from a structured content document (XML, JSON, + * etc) input stream. Normally you would use this method within a content + * request implementation, for example: + * + *

+     * ConnectionRequest request = new ConnectionRequest() {
+     * 	protected void readResponse(InputStream input) throws IOException {
+     * 		Result evaluator = Result.fromContent(input, Result.JSON);
+     * 		// ... evaluate the result here
+     *    }
+     * 	// ... etc
+     * };
+     * 
+ * + * @param content structured content document as a string. + * @param format an identifier for the type of content passed (ie. xml, + * json, etc). + * @return Result a result evaluator object + * @throws IllegalArgumentException thrown if null content or format is + * passed. + */ + public static Result fromContent(InputStream content, String format) + throws IllegalArgumentException, IOException { + if (content == null) { + throw new IllegalArgumentException("content cannot be null"); + } + if (format == null) { + throw new IllegalArgumentException("format cannot be null"); + } + StructuredContent sc; + if ("xml".equals(format)) { + sc = new XMLContent(content); + } else if ("json".equals(format)) { + sc = new JSONContent(content); + } else { + throw new IllegalArgumentException("Unrecognized format: " + format); + } + return fromContent(sc); + } + + /** + * Create an evaluator object from a structured content document (XML, JSON, + * etc) input stream. Normally you would use this method within a content + * request implementation, for example: + * + *
+     * ConnectionRequest request = new ConnectionRequest() {
+     * 	protected void readResponse(InputStream input) throws IOException {
+     * 		Result evaluator = Result.fromContent(input, Result.JSON);
+     * 		// ... evaluate the result here
+     *    }
+     * 	// ... etc
+     * };
+     * 
+ * + * @param content structured content document as a string. + * @param format an identifier for the type of content passed (ie. xml, + * json, etc). + * @return Result a result evaluator object + * @throws IllegalArgumentException thrown if null content or format is + * passed. + */ + public static Result fromContent(Reader content, String format) + throws IllegalArgumentException, IOException { + if (content == null) { + throw new IllegalArgumentException("content cannot be null"); + } + if (format == null) { + throw new IllegalArgumentException("format cannot be null"); + } + StructuredContent sc; + if ("xml".equals(format)) { + sc = new XMLContent(content); + } else if ("json".equals(format)) { + sc = new JSONContent(content); + } else { + throw new IllegalArgumentException("Unrecognized format: " + format); + } + return fromContent(sc); + } + + /** + * Create an evaluator object from a parsed XML DOM. + * + * @param content a parsed XML DOM. + * @return Result a result evaluator object + * @throws IllegalArgumentException thrown if null content is passed. + */ + public static Result fromContent(Element content) + throws IllegalArgumentException { + if (content == null) { + throw new IllegalArgumentException("content cannot be null"); + } + return fromContent(new XMLContent(content)); + } + + /** + * Create an evaluator object from parsed JSON content DOM. + * + * @param content JSON content input stream + * @return Result a result evaluator object + */ + public static Result fromContent(Map content) + throws IllegalArgumentException { + if (content == null) { + throw new IllegalArgumentException("content cannot be null"); + } + return fromContent(new MapContent(content)); + } + + /** + * Returns a hashcode value for the object. + * + * @see Object#hashCode() + */ + public int hashCode() { + return root.hashCode(); + } + + /** + * Indicates whether some other object is "equal to" this one. + * + * @see Object#equals(Object) + */ + public boolean equals(final Object other) { + return root.equals(other); + } + + /** + * Convert the object to a formatted structured content document. For + * example, an XML or JSON document. + * + * @return a structured content document as a string + */ + public String toString() { + return root.toString(); + } + + /** + * Get a boolean value from the requested path. + *

+ * For example: JSON + * + *

+     * {
+     * "settings" : [
+     * {
+     *     "toggle" : "true",
+     *     ... etc
+     * }
+     * 
+ * + * Expression + * + *
+     * boolean value = result.getAsBoolean("/settings[0]/toggle");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + */ + public boolean getAsBoolean(final String path) + throws IllegalArgumentException { + String s = getAsString(path); + if (s == null) { + return false; + } + if ("true".equals(s)) { + return true; + } else return "1".equals(s); + } + + /** + * Get an integer value from the requested path. + *

+ * For example: JSON + * + *

+     * {
+     * "settings"
+     * {
+     *     "connection"
+     *     {
+     *          "max_retries" : "20",
+     *          ... etc
+     *     }
+     * }
+     * 
+ * + * Expression + * + *
+     * int value = result.getAsInteger("//connection/max_retries");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalException on error traversing the document, ie. traversing + * into an array without using subscripts. + */ + public int getAsInteger(final String path) throws IllegalArgumentException { + String s = getAsString(path); + if (s == null) { + return 0; + } + if (s.indexOf('.') > -1) { + return (int) Double.parseDouble(s); + } else { + return Integer.parseInt(s); + } + } + + /** + * Get a long value from the requested path. + *

+ * For example: JSON + * + *

+     * {
+     * "settings"
+     * {
+     *     "connection"
+     *     {
+     *          "timeout_milliseconds" : "100000",
+     *          ... etc
+     *     }
+     * }
+     * 
+ * + * Expression + * + *
+     * long value = result.getAsLong("/settings/connection/timeout_milliseconds");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + */ + public long getAsLong(final String path) throws IllegalArgumentException { + String s = getAsString(path); + if (s == null) { + return 0; + } return (long) Double.parseDouble(s); - } - - /** - * Get a double value from the requested path. - *

- * For example: JSON - * - *

-     * {
-     *  "geometry" : {
-     *    "bounds" : {
-     *      "northeast" : {
-     *        "lat" : 42.94959820,
-     *        "lng" : -81.24873959999999
-     *       },
-     *       "southwest" : {
-     *         "lat" : 42.94830,
-     *         "lng" : -81.24901740000001
-     *       }
-     *    },
-     *    "location" : {
-     *      "lat" : 42.94886990,
-     *      "lng" : -81.24876030
-     *    },
-     *    "location_type" : "RANGE_INTERPOLATED",
-     *    "viewport" : {
-     *      "northeast" : {
-     *         "lat" : 42.95029808029150,
-     *         "lng" : -81.24752951970851
-     *      },
-     *      "southwest" : {
-     *         "lat" : 42.94760011970850,
-     *          "lng" : -81.25022748029151
-     *      }
-     *   }
-     *   // etc
-     * 
- * - * Expression - * - *
-     * double neBoundsLat = result.getAsDouble("//bounds/northeast/lat");
-     * double neBoundsLong = result.getAsDouble("//bounds/northeast/lng");
-     * double swBoundsLat = result.getAsDouble("//bounds/southwest/lat");
-     * double swBoundsLong = result.getAsDouble("//bounds/southwest/lng");
-     *
-     * double memberDiscount = result.getAsDouble("pricing.members.members");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - */ - public double getAsDouble(final String path) - throws IllegalArgumentException { - String s = getAsString(path); - if (s == null) { - return 0; - } - return Double.parseDouble(s); - } - - /** - * Get a string value from the requested path. - *

- * For example: JSON - * - *

-     * {
-     * "profile"
-     * {
-     *     "location"
-     *     {
-     *          "city" : "London",
-     *          "region" : "Ontario",
-     *          "country" : "Canada",
-     *          ... etc
-     *     },
-     * }
-     * 
- * - * Expression - * - *
-     * String city = result.getAsDouble("//city");
-     * String province = result.getAsDouble("//location//region");
-     * String country = result.getAsDouble("profile//location//country");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - */ - public String getAsString(final String path) - throws IllegalArgumentException { - Object o = _internalGet(path); - if (o instanceof StructuredContent) { - return ((StructuredContent) o).getText(); - } - return (String) o; - } - - /** - * Get the object value from the requested path. This method may return a - * Map, List, String, or null. - * - * @param path - * @return the object at the given path, or null. - * @throws IllegalArgumentException - */ - public Object get(final String path) - throws IllegalArgumentException { - Object o = _internalGet(path); - if (o instanceof StructuredContent) { - return ((StructuredContent) o).getNativeRoot(); - } - return o; - } - - /** - * Internal function, do not use. This version does not convert the - * structured content nodes, so not to be called by end user. - * - * @param path - * @return a StructuredContent node, a String, or null - * @throws IllegalArgumentException - */ - private Object _internalGet(final String path) throws IllegalArgumentException { - List v = _internalGetAsArray(path); - if (v == null || v.size() == 0) { - return null; - } - return v.get(0); - } - - /** - * Get the size of an array at the requested path. - *

- * For example: JSON - * - *

-     * {
-     *    "results" : [
-     *       {
-     *         "address_components" : [
-     *           {
-     *             "long_name" : "921-989",
-     *             "short_name" : "921-989",
-     *             "types" : [ "street_number" ]
-     *           },
-     *           {
-     *             "long_name" : "Country Club Crescent",
-     *             "short_name" : "Country Club Crescent",
-     *             "types" : [ "route" ]
-     *           },
-     *           {
-     *             "long_name" : "Ontario",
-     *             "short_name" : "ON",
-     *             "types" : [ "administrative_area_level_1", "political" ]
-     *           },
-     *           ... etc
-     *       }
-     *  }
-     * 
- * - * Expression - * - *
-     * int size = result.getSizeOfArray("/results[0]/address_components");
-     * int size2 = result.getSizeOfArray("results");
-     * int size3 = result.getSizeOfArray("/results[0]/address_components[2]/types");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - */ - public int getSizeOfArray(final String path) - throws IllegalArgumentException { - final List array = _internalGetAsArray(path); - return array == null ? 0 : array.size(); - } - - // TODO: add array accessors for other types, or parameterize by type - - /** - * Get an array of string values from the requested path. - *

- * For example: JSON - * - *

-     * {
-     *    "results" : [
-     *       {
-     *         "address_components" : [
-     *           {
-     *             "long_name" : "921-989",
-     *             "short_name" : "921-989",
-     *             "types" : [ "street_number" ]
-     *           },
-     *           {
-     *             "long_name" : "Country Club Crescent",
-     *             "short_name" : "Country Club Crescent",
-     *             "types" : [ "route" ]
-     *           },
-     *           {
-     *             "long_name" : "Ontario",
-     *             "short_name" : "ON",
-     *             "types" : [ "administrative_area_level_1", "political" ]
-     *           },
-     *           ... etc
-     *       }
-     *  }
-     * 
- * - * Expression - * - *
-     * String types[] = result
-     * 		.getAsStringArray("/results[0]/address_components[2]/types");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - */ - public String[] getAsStringArray(final String path) - throws IllegalArgumentException { - final List jarr = _internalGetAsArray(path); - final String[] arr = new String[jarr == null ? 0 : jarr.size()]; - int alen = arr.length; - for (int i = 0; i < alen; i++) { - StructuredContent element = (StructuredContent) jarr.get(i); - arr[i] = element.getText(); - } - return arr; - } - - /** - * Get an array of values from the requested path. - *

- * For example: JSON - * - *

-     * {
-     *    "results" : [
-     *       {
-     *         "address_components" : [
-     *           {
-     *             "long_name" : "921-989",
-     *             "short_name" : "921-989",
-     *             "types" : [ "street_number" ]
-     *           },
-     *           {
-     *             "long_name" : "Country Club Crescent",
-     *             "short_name" : "Country Club Crescent",
-     *             "types" : [ "route" ]
-     *           },
-     *           {
-     *             "long_name" : "Ontario",
-     *             "short_name" : "ON",
-     *             "types" : [ "administrative_area_level_1", "political" ]
-     *           },
-     *           ... etc
-     *       }
-     *  }
-     * 
- * - * Expression - * - *
-     * String types[] = result
-     * 		.getAsStringArray("/results[0]/address_components[2]/types");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - * @throws NumberFormatException if the value at path can not be converted - * to an integer. - */ - public int[] getAsIntegerArray(final String path) - throws IllegalArgumentException { - final List jarr = _internalGetAsArray(path); - final int[] arr = new int[jarr == null ? 0 : jarr.size()]; - int alen = arr.length; - for (int i = 0; i < alen; i++) { - StructuredContent element = (StructuredContent) jarr.get(i); - String s = element.getText(); - arr[i] = Integer.parseInt(s); - } - return arr; - } - - /** - * Get an array of values from the requested path. - *
-     * String types[] = result
-     * 		.getAsStringArray("/results[0]/address_components[2]/types");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - * @throws NumberFormatException if the value at path can not be converted - * to a long. - */ - public long[] getAsLongArray(final String path) - throws IllegalArgumentException { - final List jarr = _internalGetAsArray(path); - final long[] arr = new long[jarr == null ? 0 : jarr.size()]; - int alen = arr.length; - for (int i = 0; i < alen; i++) { - StructuredContent element = (StructuredContent) jarr.get(i); - String s = element.getText(); - arr[i] = Long.parseLong(s); - } - return arr; - } - - /** - * Get an array of values from the requested path. - *
-     * String types[] = result
-     * 		.getAsStringArray("/results[0]/address_components[2]/types");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - * @throws NumberFormatException if the value at path can not be converted - * to a double. - */ - public double[] getAsDoubleArray(final String path) - throws IllegalArgumentException { - final List jarr = _internalGetAsArray(path); - final double[] arr = new double[jarr == null ? 0 : jarr.size()]; - int alen = arr.length; - for (int i = 0; i < alen; i++) { - StructuredContent element = (StructuredContent) jarr.get(i); - String s = element.getText(); - arr[i] = Double.parseDouble(s); - } - return arr; - } - - /** - * Get an array of values from the requested path. - *
-     * String types[] = result
-     * 		.getAsStringArray("/results[0]/address_components[2]/types");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - */ - public boolean[] getAsBooleanArray(final String path) - throws IllegalArgumentException { - final List jarr = _internalGetAsArray(path); - final boolean[] arr = new boolean[jarr == null ? 0 : jarr.size()]; - int alen = arr.length; - for (int i = 0; i < alen; i++) { - StructuredContent element = (StructuredContent) jarr.get(i); - String s = element.getText(); - boolean b = false; - if ("true".equals(s)) { - b = true; - } else if ("1".equals(s)) { - b = true; - } - arr[i] = b; - } - return arr; - } - - /** - * Get a List of values from the requested path. - *

- * For example: JSON - * - *

-     * {
-     *    "results" : [
-     *       {
-     *         "address_components" : [
-     *           {
-     *             "long_name" : "921-989",
-     *             "short_name" : "921-989",
-     *             "types" : [ "street_number" ]
-     *           },
-     *           {
-     *             "long_name" : "Country Club Crescent",
-     *             "short_name" : "Country Club Crescent",
-     *             "types" : [ "route" ]
-     *           },
-     *           ... etc
-     *       }
-     *  }
-     * 
- * - * Expression - * - *
-     * List addressComponents = result.getAsList("/results[0]/address_components");
-     * result = Result.fromContent(addressComponents);
-     * String longName = result.getAsString("[1]/long_name");
-     * 
- * - * @param path Path expression to evaluate - * @return the value at the requested path - * @throws IllegalArgumentException on error traversing the document, ie. - * traversing into an array without using subscripts. - */ - public List getAsArray(final String path) throws IllegalArgumentException { - List array = _internalGetAsArray(path); - for (int i = 0; i < array.size(); i++) { - array.set(i, ((StructuredContent) array.get(i)).getNativeRoot()); - } - return array; - } - - /** - * Internal function, do not use. This version does not convert the - * structured content nodes in the array, so not to be called by end user. - * - * @param path - * @return - * @throws IllegalArgumentException - */ - private List _internalGetAsArray(final String path) - throws IllegalArgumentException { - final List tokens = new ResultTokenizer(path).tokenize(namespaceAliases); - if (tokens.isEmpty()) { - return tokens; - } - final StructuredContent obj = apply(root, tokens, 0); - if (obj == null) { - return new Vector(); - } - String key = (String) tokens.get(tokens.size() - 1); - // if the last element of expression is a glob, handle it here - if ((tokens.size() > 1) - && SELECT_GLOB.equals(tokens.get(tokens.size() - 2))) { - return obj.getDescendants(key); - } - // if the last element of expression is an attribute, handle it here - if (key.startsWith("@")) { - key = key.substring(1); - String v = obj.getAttribute(key); - List array = new Vector(); - if (v != null) { - // this will allow caller to get parent of an attribute if - // needed - array.add(new MapContent(v, obj)); - } - return array; - } else if (key.charAt(0) == Result.ARRAY_END && tokens.size() >= 4) { - // Handle path ending with a predicate instead of a key - //key = (String)tokens.get(tokens.size() - 4); - List array = new Vector(); - //array.add(new MapContent(key, obj)); - array.add(obj); - return array; - } - // otherwise, last element of expression selects a child node. - return obj.getChildren(key); - } - - /** - * Internal worker utility method, traverses dom based on path tokens - * - * @param start - * @param tokens - * @param firstToken - * @return - * @throws IllegalArgumentException - */ - private StructuredContent apply(final StructuredContent start, - final List tokens, final int firstToken) - throws IllegalArgumentException { - - if (start == null) { - return null; - } - - final int nTokens = tokens.size(); - if (firstToken >= nTokens) { - return start; - } - boolean glob = false; - for (int i = firstToken; i < nTokens; i++) { - final String tok1 = (String) tokens.get(i); - if (tok1.length() == 1 - && ResultTokenizer.isDelimiter(tok1.charAt(0))) { - continue; - } - if (tok1.length() == 2) { - if (tok1.equals(SELECT_GLOB)) { - glob = true; - continue; - } else if (tok1.equals(SELECT_PARENT)) { - return apply(start.getParent(), tokens, i + 1); - } - } - - if (i + 1 >= nTokens) { - return start; - } - final String tok2 = (String) tokens.get(i + 1); - final char t2 = tok2.charAt(0); - switch (t2) { - case SEPARATOR: - List children; - if (glob) { - children = start.getDescendants(tok1); - } else { - children = start.getChildren(tok1); - } - if (children.size() > 0) { - return apply(new SubContent(children, start), - tokens, i + 2); - } - return null; - case ARRAY_START: - if (i + 2 >= nTokens) { - throw new IllegalArgumentException( - "Syntax error: array must be followed by a dimension: " - + tok1); - } - final String tok3 = (String) tokens.get(i + 2); - - Evaluator evaluator = EvaluatorFactory - .createEvaluator(tok3); - - if (i + 3 >= nTokens) { - throw new IllegalArgumentException( - "Syntax error: array dimension must be closed: " - + tok3); - } - final String tok4 = (String) tokens.get(i + 3); - if (tok4.length() != 1 && tok4.charAt(0) != ARRAY_END) { - throw new IllegalArgumentException( - "Syntax error: illegal close of array dimension: " - + tok4); - } - i += 4; - if (i < nTokens) { - final String tok5 = (String) tokens.get(i); - if (tok5.length() != 1 && tok5.charAt(0) != SEPARATOR) { - throw new IllegalArgumentException( - "Syntax error: illegal separator after array: " - + tok4); - } - } - final List array; - if (glob) { - array = start.getDescendants(tok1); - } else { - array = start.getChildren(tok1); - } - - Object selected = evaluator.evaluate(array); - - if (selected instanceof StructuredContent) { - return apply((StructuredContent) selected, tokens, - i + 1); - } else { - if (selected != null && ((List) selected).size() > 0) { - List v = new Vector(); - for (Object o : (List) selected) { - StructuredContent sc = apply((StructuredContent) o, tokens, i + 1); - v.add(sc); - } - return new SubContent(v, start); - } - } - } - } - - return start; - } - - public void mapNamespaceAlias(String namespaceURI, String alias) { + } + + /** + * Get a double value from the requested path. + *

+ * For example: JSON + * + *

+     * {
+     *  "geometry" : {
+     *    "bounds" : {
+     *      "northeast" : {
+     *        "lat" : 42.94959820,
+     *        "lng" : -81.24873959999999
+     *       },
+     *       "southwest" : {
+     *         "lat" : 42.94830,
+     *         "lng" : -81.24901740000001
+     *       }
+     *    },
+     *    "location" : {
+     *      "lat" : 42.94886990,
+     *      "lng" : -81.24876030
+     *    },
+     *    "location_type" : "RANGE_INTERPOLATED",
+     *    "viewport" : {
+     *      "northeast" : {
+     *         "lat" : 42.95029808029150,
+     *         "lng" : -81.24752951970851
+     *      },
+     *      "southwest" : {
+     *         "lat" : 42.94760011970850,
+     *          "lng" : -81.25022748029151
+     *      }
+     *   }
+     *   // etc
+     * 
+ * + * Expression + * + *
+     * double neBoundsLat = result.getAsDouble("//bounds/northeast/lat");
+     * double neBoundsLong = result.getAsDouble("//bounds/northeast/lng");
+     * double swBoundsLat = result.getAsDouble("//bounds/southwest/lat");
+     * double swBoundsLong = result.getAsDouble("//bounds/southwest/lng");
+     *
+     * double memberDiscount = result.getAsDouble("pricing.members.members");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + */ + public double getAsDouble(final String path) + throws IllegalArgumentException { + String s = getAsString(path); + if (s == null) { + return 0; + } + return Double.parseDouble(s); + } + + /** + * Get a string value from the requested path. + *

+ * For example: JSON + * + *

+     * {
+     * "profile"
+     * {
+     *     "location"
+     *     {
+     *          "city" : "London",
+     *          "region" : "Ontario",
+     *          "country" : "Canada",
+     *          ... etc
+     *     },
+     * }
+     * 
+ * + * Expression + * + *
+     * String city = result.getAsDouble("//city");
+     * String province = result.getAsDouble("//location//region");
+     * String country = result.getAsDouble("profile//location//country");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + */ + public String getAsString(final String path) + throws IllegalArgumentException { + Object o = _internalGet(path); + if (o instanceof StructuredContent) { + return ((StructuredContent) o).getText(); + } + return (String) o; + } + + /** + * Get the object value from the requested path. This method may return a + * Map, List, String, or null. + * + * @param path + * @return the object at the given path, or null. + * @throws IllegalArgumentException + */ + public Object get(final String path) + throws IllegalArgumentException { + Object o = _internalGet(path); + if (o instanceof StructuredContent) { + return ((StructuredContent) o).getNativeRoot(); + } + return o; + } + + /** + * Internal function, do not use. This version does not convert the + * structured content nodes, so not to be called by end user. + * + * @param path + * @return a StructuredContent node, a String, or null + * @throws IllegalArgumentException + */ + private Object _internalGet(final String path) throws IllegalArgumentException { + List v = _internalGetAsArray(path); + if (v == null || v.size() == 0) { + return null; + } + return v.get(0); + } + + /** + * Get the size of an array at the requested path. + *

+ * For example: JSON + * + *

+     * {
+     *    "results" : [
+     *       {
+     *         "address_components" : [
+     *           {
+     *             "long_name" : "921-989",
+     *             "short_name" : "921-989",
+     *             "types" : [ "street_number" ]
+     *           },
+     *           {
+     *             "long_name" : "Country Club Crescent",
+     *             "short_name" : "Country Club Crescent",
+     *             "types" : [ "route" ]
+     *           },
+     *           {
+     *             "long_name" : "Ontario",
+     *             "short_name" : "ON",
+     *             "types" : [ "administrative_area_level_1", "political" ]
+     *           },
+     *           ... etc
+     *       }
+     *  }
+     * 
+ * + * Expression + * + *
+     * int size = result.getSizeOfArray("/results[0]/address_components");
+     * int size2 = result.getSizeOfArray("results");
+     * int size3 = result.getSizeOfArray("/results[0]/address_components[2]/types");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + */ + public int getSizeOfArray(final String path) + throws IllegalArgumentException { + final List array = _internalGetAsArray(path); + return array == null ? 0 : array.size(); + } + + + /** + * Get an array of string values from the requested path. + *

+ * For example: JSON + * + *

+     * {
+     *    "results" : [
+     *       {
+     *         "address_components" : [
+     *           {
+     *             "long_name" : "921-989",
+     *             "short_name" : "921-989",
+     *             "types" : [ "street_number" ]
+     *           },
+     *           {
+     *             "long_name" : "Country Club Crescent",
+     *             "short_name" : "Country Club Crescent",
+     *             "types" : [ "route" ]
+     *           },
+     *           {
+     *             "long_name" : "Ontario",
+     *             "short_name" : "ON",
+     *             "types" : [ "administrative_area_level_1", "political" ]
+     *           },
+     *           ... etc
+     *       }
+     *  }
+     * 
+ * + * Expression + * + *
+     * String types[] = result
+     * 		.getAsStringArray("/results[0]/address_components[2]/types");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + */ + public String[] getAsStringArray(final String path) + throws IllegalArgumentException { + final List jarr = _internalGetAsArray(path); + final String[] arr = new String[jarr == null ? 0 : jarr.size()]; + int alen = arr.length; + for (int i = 0; i < alen; i++) { + arr[i] = _getText(jarr.get(i)); + } + return arr; + } + + private String _getText(Object o) { + if (o instanceof StructuredContent) { + return ((StructuredContent) o).getText(); + } + return o.toString(); + } + + /** + * Get an array of values from the requested path. + *

+ * For example: JSON + * + *

+     * {
+     *    "results" : [
+     *       {
+     *         "address_components" : [
+     *           {
+     *             "long_name" : "921-989",
+     *             "short_name" : "921-989",
+     *             "types" : [ "street_number" ]
+     *           },
+     *           {
+     *             "long_name" : "Country Club Crescent",
+     *             "short_name" : "Country Club Crescent",
+     *             "types" : [ "route" ]
+     *           },
+     *           {
+     *             "long_name" : "Ontario",
+     *             "short_name" : "ON",
+     *             "types" : [ "administrative_area_level_1", "political" ]
+     *           },
+     *           ... etc
+     *       }
+     *  }
+     * 
+ * + * Expression + * + *
+     * String types[] = result
+     * 		.getAsStringArray("/results[0]/address_components[2]/types");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + * @throws NumberFormatException if the value at path can not be converted + * to an integer. + */ + public int[] getAsIntegerArray(final String path) + throws IllegalArgumentException { + final List jarr = _internalGetAsArray(path); + final int[] arr = new int[jarr == null ? 0 : jarr.size()]; + int alen = arr.length; + for (int i = 0; i < alen; i++) { + String s = _getText(jarr.get(i)); + if (s.indexOf('.') > -1) { + arr[i] = (int) Double.parseDouble(s); + } else { + arr[i] = Integer.parseInt(s); + } + } + return arr; + } + + /** + * Get an array of values from the requested path. + *
+     * String types[] = result
+     * 		.getAsStringArray("/results[0]/address_components[2]/types");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + * @throws NumberFormatException if the value at path can not be converted + * to a long. + */ + public long[] getAsLongArray(final String path) + throws IllegalArgumentException { + final List jarr = _internalGetAsArray(path); + final long[] arr = new long[jarr == null ? 0 : jarr.size()]; + int alen = arr.length; + for (int i = 0; i < alen; i++) { + String s = _getText(jarr.get(i)); + if (s.indexOf('.') > -1) { + arr[i] = (long) Double.parseDouble(s); + } else { + arr[i] = Long.parseLong(s); + } + } + return arr; + } + + /** + * Get an array of values from the requested path. + *
+     * String types[] = result
+     * 		.getAsStringArray("/results[0]/address_components[2]/types");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + * @throws NumberFormatException if the value at path can not be converted + * to a double. + */ + public double[] getAsDoubleArray(final String path) + throws IllegalArgumentException { + final List jarr = _internalGetAsArray(path); + final double[] arr = new double[jarr == null ? 0 : jarr.size()]; + int alen = arr.length; + for (int i = 0; i < alen; i++) { + String s = _getText(jarr.get(i)); + arr[i] = Double.parseDouble(s); + } + return arr; + } + + /** + * Get an array of values from the requested path. + *
+     * String types[] = result
+     * 		.getAsStringArray("/results[0]/address_components[2]/types");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + */ + public boolean[] getAsBooleanArray(final String path) + throws IllegalArgumentException { + final List jarr = _internalGetAsArray(path); + final boolean[] arr = new boolean[jarr == null ? 0 : jarr.size()]; + int alen = arr.length; + for (int i = 0; i < alen; i++) { + String s = _getText(jarr.get(i)); + boolean b = false; + if ("true".equals(s)) { + b = true; + } else if ("1".equals(s)) { + b = true; + } + arr[i] = b; + } + return arr; + } + + /** + * Get an array of short values from the requested path. + *
+     * short values[] = result.getAsShortArray("//values");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + * @throws NumberFormatException if the value at path can not be converted + * to a short. + */ + public short[] getAsShortArray(final String path) + throws IllegalArgumentException { + final List jarr = _internalGetAsArray(path); + final short[] arr = new short[jarr == null ? 0 : jarr.size()]; + int alen = arr.length; + for (int i = 0; i < alen; i++) { + String s = _getText(jarr.get(i)); + if (s.indexOf('.') > -1) { + arr[i] = (short) Double.parseDouble(s); + } else { + arr[i] = Short.parseShort(s); + } + } + return arr; + } + + /** + * Get an array of float values from the requested path. + *
+     * float values[] = result.getAsFloatArray("//values");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + * @throws NumberFormatException if the value at path can not be converted + * to a float. + */ + public float[] getAsFloatArray(final String path) + throws IllegalArgumentException { + final List jarr = _internalGetAsArray(path); + final float[] arr = new float[jarr == null ? 0 : jarr.size()]; + int alen = arr.length; + for (int i = 0; i < alen; i++) { + String s = _getText(jarr.get(i)); + arr[i] = Float.parseFloat(s); + } + return arr; + } + + /** + * Get an array of byte values from the requested path. + *
+     * byte values[] = result.getAsByteArray("//values");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + * @throws NumberFormatException if the value at path can not be converted + * to a byte. + */ + public byte[] getAsByteArray(final String path) + throws IllegalArgumentException { + final List jarr = _internalGetAsArray(path); + final byte[] arr = new byte[jarr == null ? 0 : jarr.size()]; + int alen = arr.length; + for (int i = 0; i < alen; i++) { + String s = _getText(jarr.get(i)); + if (s.indexOf('.') > -1) { + arr[i] = (byte) Double.parseDouble(s); + } else { + arr[i] = Byte.parseByte(s); + } + } + return arr; + } + + /** + * Get a List of values from the requested path. + *

+ * For example: JSON + * + *

+     * {
+     *    "results" : [
+     *       {
+     *         "address_components" : [
+     *           {
+     *             "long_name" : "921-989",
+     *             "short_name" : "921-989",
+     *             "types" : [ "street_number" ]
+     *           },
+     *           {
+     *             "long_name" : "Country Club Crescent",
+     *             "short_name" : "Country Club Crescent",
+     *             "types" : [ "route" ]
+     *           },
+     *           ... etc
+     *       }
+     *  }
+     * 
+ * + * Expression + * + *
+     * List addressComponents = result.getAsList("/results[0]/address_components");
+     * result = Result.fromContent(addressComponents);
+     * String longName = result.getAsString("[1]/long_name");
+     * 
+ * + * @param path Path expression to evaluate + * @return the value at the requested path + * @throws IllegalArgumentException on error traversing the document, ie. + * traversing into an array without using subscripts. + */ + public List getAsArray(final String path) throws IllegalArgumentException { + List array = _internalGetAsArray(path); + for (int i = 0; i < array.size(); i++) { + array.set(i, ((StructuredContent) array.get(i)).getNativeRoot()); + } + return array; + } + + /** + * Internal function, do not use. This version does not convert the + * structured content nodes in the array, so not to be called by end user. + * + * @param path + * @return + * @throws IllegalArgumentException + */ + private List _internalGetAsArray(final String path) + throws IllegalArgumentException { + final List tokens = new ResultTokenizer(path).tokenize(namespaceAliases); + if (tokens.isEmpty()) { + return tokens; + } + final StructuredContent obj = apply(root, tokens, 0); + if (obj == null) { + return new Vector(); + } + String key = (String) tokens.get(tokens.size() - 1); + // if the last element of expression is a glob, handle it here + if ((tokens.size() > 1) + && SELECT_GLOB.equals(tokens.get(tokens.size() - 2))) { + return obj.getDescendants(key); + } + // if the last element of expression is an attribute, handle it here + if (key.startsWith("@")) { + key = key.substring(1); + String v = obj.getAttribute(key); + List array = new Vector(); + if (v != null) { + // this will allow caller to get parent of an attribute if + // needed + array.add(new MapContent(v, obj)); + } + return array; + } else if (key.charAt(0) == Result.ARRAY_END && tokens.size() >= 4) { + // Handle path ending with a predicate instead of a key + //key = (String)tokens.get(tokens.size() - 4); + List array = new Vector(); + //array.add(new MapContent(key, obj)); + array.add(obj); + return array; + } + // otherwise, last element of expression selects a child node. + return obj.getChildren(key); + } + + /** + * Internal worker utility method, traverses dom based on path tokens + * + * @param start + * @param tokens + * @param firstToken + * @return + * @throws IllegalArgumentException + */ + private StructuredContent apply(final StructuredContent start, + final List tokens, final int firstToken) + throws IllegalArgumentException { + + if (start == null) { + return null; + } + + final int nTokens = tokens.size(); + if (firstToken >= nTokens) { + return start; + } + boolean glob = false; + for (int i = firstToken; i < nTokens; i++) { + final String tok1 = (String) tokens.get(i); + if (tok1.length() == 1 + && ResultTokenizer.isDelimiter(tok1.charAt(0))) { + continue; + } + if (tok1.length() == 2) { + if (tok1.equals(SELECT_GLOB)) { + glob = true; + continue; + } else if (tok1.equals(SELECT_PARENT)) { + return apply(start.getParent(), tokens, i + 1); + } + } + + if (i + 1 >= nTokens) { + return start; + } + final String tok2 = (String) tokens.get(i + 1); + final char t2 = tok2.charAt(0); + switch (t2) { + case SEPARATOR: + List children; + if (glob) { + children = start.getDescendants(tok1); + } else { + children = start.getChildren(tok1); + } + if (children.size() > 0) { + return apply(new SubContent(children, start), + tokens, i + 2); + } + return null; + case ARRAY_START: + if (i + 2 >= nTokens) { + throw new IllegalArgumentException( + "Syntax error: array must be followed by a dimension: " + + tok1); + } + final String tok3 = (String) tokens.get(i + 2); + + Evaluator evaluator = EvaluatorFactory + .createEvaluator(tok3); + + if (i + 3 >= nTokens) { + throw new IllegalArgumentException( + "Syntax error: array dimension must be closed: " + + tok3); + } + final String tok4 = (String) tokens.get(i + 3); + if (tok4.length() != 1 && tok4.charAt(0) != ARRAY_END) { + throw new IllegalArgumentException( + "Syntax error: illegal close of array dimension: " + + tok4); + } + i += 4; + if (i < nTokens) { + final String tok5 = (String) tokens.get(i); + if (tok5.length() != 1 && tok5.charAt(0) != SEPARATOR) { + throw new IllegalArgumentException( + "Syntax error: illegal separator after array: " + + tok4); + } + } + final List array; + if (glob) { + array = start.getDescendants(tok1); + } else { + array = start.getChildren(tok1); + } + + Object selected = evaluator.evaluate(array); + + if (selected instanceof StructuredContent) { + return apply((StructuredContent) selected, tokens, + i + 1); + } else { + if (selected != null && ((List) selected).size() > 0) { + List v = new Vector(); + for (Object o : (List) selected) { + StructuredContent sc = apply((StructuredContent) o, tokens, i + 1); + v.add(sc); + } + return new SubContent(v, start); + } + } + } + } + + return start; + } + + public void mapNamespaceAlias(String namespaceURI, String alias) { Map attributes = root.getChild(0).getAttributes(); if (attributes == null) { return;