1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.org.apache.xpath.internal.jaxp;
  27 
  28 import com.sun.org.apache.xpath.internal.objects.XObject;
  29 import java.util.Objects;
  30 import javax.xml.transform.TransformerException;
  31 import javax.xml.xpath.XPathNodes;
  32 import javax.xml.xpath.XPathEvaluationResult;
  33 import javax.xml.xpath.XPathEvaluationResult.XPathResultType;
  34 import org.w3c.dom.Node;
  35 import org.w3c.dom.NodeList;
  36 import org.w3c.dom.traversal.NodeIterator;
  37 
  38 
  39 /**
  40  * This is the implementation of XPathEvaluationResult that represents the
  41  * result of the evaluation of an XPath expression within the context of a
  42  * particular node.
  43  */
  44 class XPathResultImpl<T> implements XPathEvaluationResult<T> {
  45 
  46     XObject resultObject;
  47     int resultType;
  48     Class<T> type;
  49     XPathResultType mapToType;
  50     NodeList nodeList = null;
  51     int currentIndex;
  52     Node currentNode;
  53 
  54     boolean boolValue = false;
  55     Node node = null;
  56     double numValue;
  57     String strValue;
  58 
  59     /**
  60      * Construct an XPathEvaluationResult object.
  61      *
  62      * @param resultObject internal XPath result object
  63      * @param type class type
  64      * @throws TransformerException if there is an error reading the XPath
  65      * result.
  66      */
  67     public XPathResultImpl(XObject resultObject, Class<T> type)
  68             throws TransformerException {
  69         this.resultObject = resultObject;
  70         resultType = resultObject.getType();
  71         this.type = type;
  72         getResult(resultObject);
  73     }
  74 
  75     /**
  76      * Return the result type as an enum specified by {@code XPathResultType}
  77      * @return the result type
  78      */
  79     @Override
  80     public XPathResultType type() {
  81         return mapToType;
  82     }
  83 
  84     /**
  85      * Returns the value of the result as the type &lt;T&gt; specified for the class.
  86      *
  87      * @return The value of the result.
  88      */
  89     @Override
  90     public T value() {
  91         Objects.requireNonNull(type);
  92         try {
  93             return getValue(resultObject, type);
  94         } catch (TransformerException ex) {
  95             throw new RuntimeException(ex);
  96         }
  97     }
  98 
  99     /**
 100      * Read the XObject and set values in accordance with the result type
 101      * @param resultObject  internal XPath result object
 102      * @throws TransformerException  if there is an error reading the XPath
 103      * result.
 104      */
 105     private void getResult(XObject resultObject) throws TransformerException {
 106         switch (resultType) {
 107             case XObject.CLASS_BOOLEAN:
 108                 boolValue = resultObject.bool();
 109                 mapToType = XPathResultType.BOOLEAN;
 110                 break;
 111             case XObject.CLASS_NUMBER:
 112                 numValue = resultObject.num();
 113                 mapToType = XPathResultType.NUMBER;
 114                 break;
 115             case XObject.CLASS_STRING:
 116                 strValue = resultObject.str();
 117                 mapToType = XPathResultType.STRING;
 118                 break;
 119             case XObject.CLASS_NODESET:
 120                 mapToType = XPathResultType.NODESET;
 121                 nodeList = resultObject.nodelist();
 122                 break;
 123             case XObject.CLASS_RTREEFRAG:  //NODE
 124                 mapToType = XPathResultType.NODE;
 125                 NodeIterator ni = resultObject.nodeset();
 126                 //Return the first node, or null
 127                 node = ni.nextNode();
 128                 break;
 129         }
 130     }
 131 
 132     /**
 133      * Read the internal result object and return the value in accordance with
 134      * the type specified.
 135      *
 136      * @param <T> The expected class type.
 137      * @param resultObject internal XPath result object
 138      * @param type the class type
 139      * @return The value of the result.
 140      * @throws TransformerException  if there is an error reading the XPath
 141      * result.
 142      */
 143     static <T> T getValue(XObject resultObject, Class<T> type) throws TransformerException {
 144         Objects.requireNonNull(type);
 145         if (type.isAssignableFrom(XPathEvaluationResult.class)) {
 146             return type.cast(new XPathResultImpl<T>(resultObject, type));
 147         }
 148         int resultType = classToInternalType(type);
 149         switch (resultType) {
 150             case XObject.CLASS_BOOLEAN:
 151                 return type.cast(resultObject.bool());
 152             case XObject.CLASS_NUMBER:
 153                 if (Double.class.isAssignableFrom(type)) {
 154                     return type.cast(resultObject.num());
 155                 } else if (Integer.class.isAssignableFrom(type)) {
 156                     return type.cast((int)resultObject.num());
 157                 } else if (Long.class.isAssignableFrom(type)) {
 158                     return type.cast((long)resultObject.num());
 159                 }
 160                 /*
 161                   This is to suppress warnings. By the current specification,
 162                 among numeric types, only Double, Integer and Long are supported.
 163                 */
 164                 break;
 165             case XObject.CLASS_STRING:
 166                 return type.cast(resultObject.str());
 167             case XObject.CLASS_NODESET:
 168                 XPathNodes nodeSet = new XPathNodesImpl(resultObject.nodelist(),
 169                         classToType(Node.class), Node.class);
 170                 return type.cast(nodeSet);
 171             case XObject.CLASS_RTREEFRAG:  //NODE
 172                 NodeIterator ni = resultObject.nodeset();
 173                 //Return the first node, or null
 174                 return type.cast(ni.nextNode());
 175         }
 176 
 177         return null;
 178     }
 179 
 180     /**
 181      * Map class types to the XPathResultType
 182      *
 183      * @param <T> The expected class type.
 184      * @param type the class type
 185      * @return an XPathResultType enum type
 186      */
 187     static <T> XPathResultType classToType(Class<T> type) {
 188         if (type.isAssignableFrom(Boolean.class)) {
 189             return XPathResultType.BOOLEAN;
 190         } else if (type.isAssignableFrom(Double.class)) {
 191             return XPathResultType.NUMBER;
 192         } else if (type.isAssignableFrom(String.class)) {
 193             return XPathResultType.STRING;
 194         } else if (type.isAssignableFrom(NodeList.class)) {
 195             return XPathResultType.NODESET;
 196         } else if (type.isAssignableFrom(Node.class)) {
 197             return XPathResultType.NODE;
 198         }
 199         return XPathResultType.ANY;
 200     }
 201 
 202     /**
 203      * Map the specified class type to the internal result type
 204      *
 205      * @param <T> The expected class type.
 206      * @param type the class type
 207      * @return the internal XObject type.
 208      */
 209     static <T> int classToInternalType(Class<T> type) {
 210         if (type.isAssignableFrom(Boolean.class)) {
 211             return XObject.CLASS_BOOLEAN;
 212         } else if (Number.class.isAssignableFrom(type)) {
 213             return XObject.CLASS_NUMBER;
 214         } else if (type.isAssignableFrom(String.class)) {
 215             return XObject.CLASS_STRING;
 216         } else if (type.isAssignableFrom(XPathNodes.class)) {
 217             return XObject.CLASS_NODESET;
 218         } else if (type.isAssignableFrom(Node.class)) {
 219             return XObject.CLASS_RTREEFRAG;
 220         }
 221         return XObject.CLASS_NULL;
 222     }
 223 }