##### Coverage Report - org.apache.commons.scxml.Builtin

Classes in this File | Line Coverage | Branch Coverage | Complexity
Builtin |
41% |
32% |
| 3.3;3.3

1 |  |
     /*
2 |  |
      * Licensed to the Apache Software Foundation (ASF) under one or more
3 |  |
      * contributor license agreements.  See the NOTICE file distributed with
4 |  |
      * this work for additional information regarding copyright ownership.
5 |  |
      * The ASF licenses this file to You under the Apache License, Version 2.0
6 |  |
      * (the "License"); you may not use this file except in compliance with
7 |  |
      * the License.  You may obtain a copy of the License at
8 |  |
      *
9 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
10 |  |
      *
11 |  |
      * Unless required by applicable law or agreed to in writing, software
12 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
13 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 |  |
      * See the License for the specific language governing permissions and
15 |  |
      * limitations under the License.
16 |  |
      */
17 |  |
     package org.apache.commons.scxml;
18 |  |
19 |  |
     import java.io.Serializable;
20 |  |
     import java.util.Iterator;
21 |  |
     import java.util.Map;
22 |  |
     import java.util.Set;
23 |  |
24 |  |
     import javax.xml.transform.TransformerException;
25 |  |
26 |  |
     import org.apache.commons.logging.Log;
27 |  |
     import org.apache.commons.logging.LogFactory;
28 |  |
     import org.apache.commons.scxml.model.TransitionTarget;
29 |  |
     import org.apache.xml.utils.PrefixResolver;
30 |  |
     import org.apache.xpath.XPath;
31 |  |
     import org.apache.xpath.XPathAPI;
32 |  |
     import org.apache.xpath.XPathContext;
33 |  |
     import org.w3c.dom.Node;
34 |  |
     import org.w3c.dom.NodeList;
35 |  |
36 |  |
     /**
37 |  |
      * Implementations of builtin functions defined by the SCXML
38 |  |
      * specification.
39 |  |
      *
40 |  |
      * The current version of the specification defines one builtin
41 |  |
      * predicate In()
42 |  |
      */
43 | 308 |
     public class Builtin implements Serializable {
44 |  |
45 |  |
         /**
46 |  |
          * Serial version UID.
47 |  |
          */
48 |  |
         private static final long serialVersionUID = 1L;
49 |  |
50 |  |
         /**
51 |  |
          * Implements the In() predicate for SCXML documents. The method
52 |  |
          * name chosen is different since &quot;in&quot; is a reserved token
53 |  |
          * in some expression languages.
54 |  |
          *
55 |  |
          * Does this state belong to the given Set of States.
56 |  |
          * Simple ID based comparator, assumes IDs are unique.
57 |  |
          *
58 |  |
          * @param allStates The Set of State objects to look in
59 |  |
          * @param state The State ID to compare with
60 |  |
          * @return Whether this State belongs to this Set
61 |  |
          */
62 |  |
         public static boolean isMember(final Set allStates,
63 |  |
                 final String state) {
64 | 7 |
             for (Iterator i = allStates.iterator(); i.hasNext();) {
65 | 6 |
                 TransitionTarget tt = (TransitionTarget) i.next();
66 | 6 |
                 if (state.equals(tt.getId())) {
67 | 5 |
                     return true;
68 |  |
                 }
69 |  |
             }
70 | 2 |
             return false;
71 |  |
         }
72 |  |
73 |  |
         /**
74 |  |
          * Implements the Data() function for Commons SCXML documents, that
75 |  |
          * can be used to obtain a node from one of the XML data trees.
76 |  |
          * Manifests within "location" attribute of &lt;assign&gt; element,
77 |  |
          * for Commons JEXL and Commons EL based documents.
78 |  |
          *
79 |  |
          * @param namespaces The current document namespaces map at XPath location
80 |  |
          * @param data The context Node, though the method accepts an Object
81 |  |
          *             so error is reported by Commons SCXML, rather
82 |  |
          *             than the underlying expression language.
83 |  |
          * @param path The XPath expression.
84 |  |
          * @return The first node matching the path, or null if no nodes match.
85 |  |
          */
86 |  |
         public static Node dataNode(final Map namespaces, final Object data,
87 |  |
                 final String path) {
88 | 75 |
             if (data == null || !(data instanceof Node)) {
89 | 0 |
                 Log log = LogFactory.getLog(Builtin.class);
90 | 0 |
                 log.error("Data(): Cannot evaluate an XPath expression"
91 |  |
                     + " in the absence of a context Node, null returned");
92 | 0 |
                 return null;
93 |  |
             }
94 | 75 |
             Node dataNode = (Node) data;
95 | 75 |
             NodeList result = null;
96 |  |
             try {
97 | 75 |
                 if (namespaces == null || namespaces.size() == 0) {
98 | 0 |
                     Log log = LogFactory.getLog(Builtin.class);
99 | 0 |
                     if (log.isDebugEnabled()) {
100 | 0 |
                         log.debug("Turning off namespaced XPath evaluation since "
101 |  |
                             + "no namespace information is available for path: "
102 |  |
                             + path);
103 |  |
                     }
104 | 0 |
                     result = XPathAPI.selectNodeList(dataNode, path);
105 |  |
                 } else {
106 | 75 |
                     XPathContext xpathSupport = new XPathContext();
107 | 75 |
                     PrefixResolver prefixResolver =
108 |  |
                         new DataPrefixResolver(namespaces);
109 | 75 |
                     XPath xpath = new XPath(path, null, prefixResolver,
110 |  |
                         XPath.SELECT);
111 | 75 |
                     int ctxtNode = xpathSupport.getDTMHandleFromNode(dataNode);
112 | 75 |
                     result = xpath.execute(xpathSupport, ctxtNode,
113 |  |
                         prefixResolver).nodelist();
114 |  |
                 }
115 | 0 |
             } catch (TransformerException te) {
116 | 0 |
                 Log log = LogFactory.getLog(Builtin.class);
117 | 0 |
                 log.error(te.getMessage(), te);
118 | 0 |
                 return null;
119 | 75 |
             }
120 | 75 |
             int length = result.getLength();
121 | 75 |
             if (length == 0) {
122 | 0 |
                 Log log = LogFactory.getLog(Builtin.class);
123 | 0 |
                 log.warn("Data(): No nodes matching the XPath expression \""
124 |  |
                     + path + "\", returning null");
125 | 0 |
                 return null;
126 |  |
             } else {
127 | 75 |
                 if (length > 1) {
128 | 0 |
                     Log log = LogFactory.getLog(Builtin.class);
129 | 0 |
                     log.warn("Data(): Multiple nodes matching XPath expression"
130 |  |
                         + path + "\", returning first");
131 |  |
                 }
132 | 75 |
                 return result.item(0);
133 |  |
             }
134 |  |
         }
135 |  |
136 |  |
         /**
137 |  |
          * A variant of the Data() function for Commons SCXML documents,
138 |  |
          * coerced to a Double, a Long or a String, whichever succeeds,
139 |  |
          * in that order.
140 |  |
          * Manifests within rvalue expressions in the document,
141 |  |
          * for Commons JEXL and Commons EL based documents..
142 |  |
          *
143 |  |
          * @param namespaces The current document namespaces map at XPath location
144 |  |
          * @param data The context Node, though the method accepts an Object
145 |  |
          *             so error is reported by Commons SCXML, rather
146 |  |
          *             than the underlying expression language.
147 |  |
          * @param path The XPath expression.
148 |  |
          * @return The first node matching the path, coerced to a String, or null
149 |  |
          *         if no nodes match.
150 |  |
          */
151 |  |
         public static Object data(final Map namespaces, final Object data,
152 |  |
                 final String path) {
153 | 58 |
             Object retVal = null;
154 | 58 |
             String strVal = SCXMLHelper.getNodeValue(dataNode(namespaces,
155 |  |
                 data, path));
156 |  |
             // try as a double
157 |  |
             try {
158 | 58 |
                 double d = Double.parseDouble(strVal);
159 | 50 |
                 retVal = new Double(d);
160 | 8 |
             } catch (NumberFormatException notADouble) {
161 |  |
                 // else as a long
162 |  |
                 try {
163 | 8 |
                     long l = Long.parseLong(strVal);
164 | 0 |
                     retVal = new Long(l);
165 | 8 |
                 } catch (NumberFormatException notALong) {
166 |  |
                     // fallback to string
167 | 8 |
                     retVal = strVal;
168 | 0 |
                 }
169 | 50 |
             }
170 | 58 |
             return retVal;
171 |  |
         }
172 |  |
173 |  |
         /**
174 |  |
          * Implements the Data() function for Commons SCXML documents, that
175 |  |
          * can be used to obtain a node from one of the XML data trees.
176 |  |
          * Manifests within "location" attribute of &lt;assign&gt; element,
177 |  |
          * for Commons JEXL and Commons EL based documents.
178 |  |
          *
179 |  |
          * @param data The context Node, though the method accepts an Object
180 |  |
          *             so error is reported by Commons SCXML, rather
181 |  |
          *             than the underlying expression language.
182 |  |
          * @param path The XPath expression.
183 |  |
          * @return The first node matching the path, or null if no nodes match.
184 |  |
          *
185 |  |
          * @deprecated Use {@link #dataNode(Map,Object,String)} instead
186 |  |
          */
187 |  |
         public static Node dataNode(final Object data, final String path) {
188 | 0 |
             if (data == null || !(data instanceof Node)) {
189 | 0 |
                 Log log = LogFactory.getLog(Builtin.class);
190 | 0 |
                 log.error("Data(): Cannot evaluate an XPath expression"
191 |  |
                     + " in the absence of a context Node, null returned");
192 | 0 |
                 return null;
193 |  |
             }
194 | 0 |
             Node dataNode = (Node) data;
195 | 0 |
             NodeList result = null;
196 |  |
             try {
197 | 0 |
                 result = XPathAPI.selectNodeList(dataNode, path);
198 | 0 |
             } catch (TransformerException te) {
199 | 0 |
                 Log log = LogFactory.getLog(Builtin.class);
200 | 0 |
                 log.error(te.getMessage(), te);
201 | 0 |
                 return null;
202 | 0 |
             }
203 | 0 |
             int length = result.getLength();
204 | 0 |
             if (length == 0) {
205 | 0 |
                 Log log = LogFactory.getLog(Builtin.class);
206 | 0 |
                 log.warn("Data(): No nodes matching the XPath expression \""
207 |  |
                     + path + "\", returning null");
208 | 0 |
                 return null;
209 |  |
             } else {
210 | 0 |
                 if (length > 1) {
211 | 0 |
                     Log log = LogFactory.getLog(Builtin.class);
212 | 0 |
                     log.warn("Data(): Multiple nodes matching XPath expression"
213 |  |
                         + path + "\", returning first");
214 |  |
                 }
215 | 0 |
                 return result.item(0);
216 |  |
             }
217 |  |
         }
218 |  |
219 |  |
         /**
220 |  |
          * A variant of the Data() function for Commons SCXML documents,
221 |  |
          * coerced to a Double, a Long or a String, whichever succeeds,
222 |  |
          * in that order.
223 |  |
          * Manifests within rvalue expressions in the document,
224 |  |
          * for Commons JEXL and Commons EL based documents..
225 |  |
          *
226 |  |
          * @param data The context Node, though the method accepts an Object
227 |  |
          *             so error is reported by Commons SCXML, rather
228 |  |
          *             than the underlying expression language.
229 |  |
          * @param path The XPath expression.
230 |  |
          * @return The first node matching the path, coerced to a String, or null
231 |  |
          *         if no nodes match.
232 |  |
          *
233 |  |
          * @deprecated Use {@link #data(Map,Object,String)} instead
234 |  |
          */
235 |  |
         public static Object data(final Object data, final String path) {
236 | 0 |
             Object retVal = null;
237 | 0 |
             String strVal = SCXMLHelper.getNodeValue(dataNode(data, path));
238 |  |
             // try as a double
239 |  |
             try {
240 | 0 |
                 double d = Double.parseDouble(strVal);
241 | 0 |
                 retVal = new Double(d);
242 | 0 |
             } catch (NumberFormatException notADouble) {
243 |  |
                 // else as a long
244 |  |
                 try {
245 | 0 |
                     long l = Long.parseLong(strVal);
246 | 0 |
                     retVal = new Long(l);
247 | 0 |
                 } catch (NumberFormatException notALong) {
248 |  |
                     // fallback to string
249 | 0 |
                     retVal = strVal;
250 | 0 |
                 }
251 | 0 |
             }
252 | 0 |
             return retVal;
253 |  |
         }
254 |  |
255 |  |
         /**
256 |  |
          * Prefix resolver for XPaths pointing to &lt;data&gt; nodes.
257 |  |
          */
258 | 308 |
         private static class DataPrefixResolver implements PrefixResolver {
259 |  |
260 |  |
             /** Cached namespaces. */
261 |  |
             private Map namespaces;
262 |  |
263 |  |
             /**
264 |  |
              * Constructor.
265 |  |
              * @param namespaces The prefix to namespace URI map.
266 |  |
              */
267 | 75 |
             private DataPrefixResolver(final Map namespaces) {
268 | 75 |
                 this.namespaces = namespaces;
269 | 75 |
             }
270 |  |
271 |  |
             /** {@inheritDoc} */
272 |  |
             public String getNamespaceForPrefix(final String prefix) {
273 | 96 |
                 return (String) namespaces.get(prefix);
274 |  |
             }
275 |  |
276 |  |
             /** {@inheritDoc} */
277 |  |
             public String getNamespaceForPrefix(final String prefix,
278 |  |
                     final Node nsContext) {
279 | 0 |
                 return (String) namespaces.get(prefix);
280 |  |
             }
281 |  |
282 |  |
             /** {@inheritDoc} */
283 |  |
             public String getBaseIdentifier() {
284 | 0 |
                 return null;
285 |  |
             }
286 |  |
287 |  |
             /** {@inheritDoc} */
288 |  |
             public boolean handlesNullPrefixes() {
289 | 43 |
                 return false;
290 |  |
             }
291 |  |
292 |  |
         }
293 |  |
294 |  |
     }
295 |  |