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
26package com.sun.org.apache.xpath.internal.jaxp;
27
28import com.sun.org.apache.xpath.internal.objects.XObject;
29import java.util.Objects;
30import javax.xml.transform.TransformerException;
31import javax.xml.xpath.XPathNodes;
32import javax.xml.xpath.XPathEvaluationResult;
33import javax.xml.xpath.XPathEvaluationResult.XPathResultType;
34import org.w3c.dom.Node;
35import org.w3c.dom.NodeList;
36import 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 */
44class 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, null in case of unexpected type.
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                        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 the specified class type to the internal result type
182     *
183     * @param <T> The expected class type.
184     * @param type the class type
185     * @return the internal XObject type.
186     */
187    static <T> int classToInternalType(Class<T> type) {
188        if (type.isAssignableFrom(Boolean.class)) {
189            return XObject.CLASS_BOOLEAN;
190        } else if (Number.class.isAssignableFrom(type)) {
191            return XObject.CLASS_NUMBER;
192        } else if (type.isAssignableFrom(String.class)) {
193            return XObject.CLASS_STRING;
194        } else if (type.isAssignableFrom(XPathNodes.class)) {
195            return XObject.CLASS_NODESET;
196        } else if (type.isAssignableFrom(Node.class)) {
197            return XObject.CLASS_RTREEFRAG;
198        }
199        return XObject.CLASS_NULL;
200    }
201}
202