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  * This is the implementation of XPathEvaluationResult that represents the 
  40  * result of the evaluation of an XPath expression within the context of a 
  41  * particular node.
  42  */
  43 class XPathResultImpl<T> implements XPathEvaluationResult<T> {
  44 
  45     XObject resultObject;
  46     int resultType;
  47     Class<T> type;
  48     XPathResultType mapToType;
  49     NodeList nodeList = null;
  50     int currentIndex;
  51     Node currentNode;
  52 
  53     boolean boolValue = false;
  54     Node node = null;
  55     double numValue;
  56     String strValue;
  57 
  58     /**
  59      * Construct an XPathEvaluationResult object.
  60      *
  61      * @param resultObject internal XPath result object
  62      * @param type class type
  63      * @throws TransformerException if there is an error reading the XPath
  64      * result.
  65      */
  66     public XPathResultImpl(XObject resultObject, Class<T> type)
  67             throws TransformerException {
  68         this.resultObject = resultObject;
  69         resultType = resultObject.getType();
  70         this.type = type;
  71         getResult(resultObject);
  72     }
  73 
  74     /**
  75      * Return the result type as an enum specified by {@code XPathResultType}
  76      * @return the result type
  77      */
  78     @Override
  79     public XPathResultType type() {
  80         return mapToType;
  81     }
  82 
  83     /**
  84      * Returns the value of the result as the type &lt;T&gt; specified for the class.
  85      *
  86      * @return The value of the result.
  87      */
  88     @Override
  89     public T value() {
  90         Objects.requireNonNull(type);
  91         try {
  92             return getValue(resultObject, type);
  93         } catch (TransformerException ex) {
  94             throw new RuntimeException(ex);
  95         }
  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 (Number.class.isAssignableFrom(type)) {
 158                     return type.cast(resultObject.num());
 159                 }
 160             case XObject.CLASS_STRING:
 161                 return type.cast(resultObject.str());
 162             case XObject.CLASS_NODESET:
 163                 XPathNodes nodeSet = new XPathNodesImpl(resultObject.nodelist(),
 164                         classToType(Node.class), Node.class);
 165                 return type.cast(nodeSet);
 166             case XObject.CLASS_RTREEFRAG:  //NODE
 167                 NodeIterator ni = resultObject.nodeset();
 168                 //Return the first node, or null
 169                 return type.cast(ni.nextNode());
 170         }
 171 
 172         return null;
 173     }
 174 
 175     /**
 176      * Map class types to the XPathResultType
 177      *
 178      * @param <T> The expected class type.
 179      * @param type the class type
 180      * @return an XPathResultType enum type
 181      */
 182     static <T> XPathResultType classToType(Class<T> type) {
 183         if (type.isAssignableFrom(Boolean.class)) {
 184             return XPathResultType.BOOLEAN;
 185         } else if (type.isAssignableFrom(Double.class)) {
 186             return XPathResultType.NUMBER;
 187         } else if (type.isAssignableFrom(String.class)) {
 188             return XPathResultType.STRING;
 189         } else if (type.isAssignableFrom(NodeList.class)) {
 190             return XPathResultType.NODESET;
 191         } else if (type.isAssignableFrom(Node.class)) {
 192             return XPathResultType.NODE;
 193         }
 194         return XPathResultType.ANY;
 195     }
 196 
 197     /**
 198      * Map the specified class type to the internal result type
 199      *
 200      * @param <T> The expected class type.
 201      * @param type the class type
 202      * @return the internal XObject type.
 203      */
 204     static <T> int classToInternalType(Class<T> type) {
 205         if (type.isAssignableFrom(Boolean.class)) {
 206             return XObject.CLASS_BOOLEAN;
 207         } else if (Number.class.isAssignableFrom(type)) {
 208             return XObject.CLASS_NUMBER;
 209         } else if (type.isAssignableFrom(String.class)) {
 210             return XObject.CLASS_STRING;
 211         } else if (type.isAssignableFrom(XPathNodes.class)) {
 212             return XObject.CLASS_NODESET;
 213         } else if (type.isAssignableFrom(Node.class)) {
 214             return XObject.CLASS_RTREEFRAG;
 215         }
 216         return XObject.CLASS_NULL;
 217     }
 218 }