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

Classes in this File Line Coverage Branch Coverage Complexity
Builtin
41% 
32% 
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 "in" 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 <assign> 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 <assign> 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 <data> 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