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.scxml2.env.groovy; 18 19 import java.lang.reflect.Array; 20 import java.util.Collection; 21 import java.util.Map; 22 23 import org.apache.commons.scxml2.Builtin; 24 import org.apache.commons.scxml2.SCXMLExpressionException; 25 import org.apache.commons.scxml2.XPathBuiltin; 26 27 import groovy.lang.Binding; 28 import groovy.lang.MissingPropertyException; 29 import groovy.lang.Script; 30 31 /** 32 * Groovy {@link Script} base class for SCXML, providing the standard 'builtin' functions {@link #In(String)}, 33 * {@link #Data(String)} and {@link #Location(String)}, as well as JEXL like convenience functions 34 * {@link #empty(Object)} and {@link #var(String)}. 35 */ 36 public abstract class GroovySCXMLScript extends Script { 37 38 GroovyContext context; 39 GroovyContextBinding binding; 40 41 protected GroovySCXMLScript() { 42 super(null); 43 } 44 45 @Override 46 public void setBinding(final Binding binding) { 47 super.setBinding(binding); 48 this.binding = (GroovyContextBinding)binding; 49 this.context = this.binding.getContext(); 50 } 51 52 /** 53 * Implements the In() predicate for SCXML documents ( see Builtin#isMember ) 54 * @param state The State ID to compare with 55 * @return Whether this State belongs to this Set 56 */ 57 public boolean In(final String state) { 58 return Builtin.isMember(context, state); 59 } 60 61 /** 62 * Implements the Data() predicate for SCXML documents. 63 * @param expression the XPath expression 64 * @return the data matching the expression 65 */ 66 public Object Data(final String expression) throws SCXMLExpressionException { 67 return XPathBuiltin.eval(context, expression); 68 } 69 70 /** 71 * Implements the Location() predicate for SCXML documents. 72 * @param location the XPath expression 73 * @return the location list for the location expression 74 */ 75 public Object Location(final String location) throws SCXMLExpressionException { 76 return XPathBuiltin.evalLocation(context, location); 77 } 78 79 /** 80 * The var function can be used to check if a variable is defined, 81 * <p> 82 * In the Groovy language (implementation) you cannot check for an undefined variable directly: 83 * Groovy will raise a MissingPropertyException before you get the chance. 84 * </p> 85 * <p> 86 * The var function works around this by indirectly looking up the variable, which you therefore have to specify as a String. 87 * </p> 88 * <p> 89 * So, use <code>var('name')</code>, not <code>var(name)</code> 90 * </p> 91 * <p> 92 * Note: this function doesn't support object navigation, like <code>var('name.property')</code>.<br/> 93 * Instead, once you established a variable 'name' exists, you <em>thereafter</em> can use the standard Groovy 94 * Safe Navigation operator (?.), like so: <code>name?.property</code>.<br/> 95 * See for more information: <a href="http://docs.codehaus.org/display/GROOVY/Operators#Operators-SafeNavigationOperator(?.)">Groovy SafeNavigationOperator</a> 96 * </p> 97 */ 98 public boolean var(String property) { 99 if (!context.has(property)) { 100 try { 101 getMetaClass().getProperty(this, property); 102 } catch (MissingPropertyException e) { 103 return false; 104 } 105 } 106 return true; 107 } 108 109 /** 110 * The empty function mimics the behavior of the JEXL empty function, in that it returns true if the parameter is: 111 * <ul> 112 * <li>null, or</li> 113 * <li>an empty String, or</li> 114 * <li>an zero length Array, or</li> 115 * <li>an empty Collection, or</li> 116 * <li>an empty Map</li> 117 * </ul> 118 * <p> 119 * Note: one difference with the JEXL language is that Groovy doesn't allow checking for undefined variables.<br/> 120 * Before being able to check, Groovy will already have raised an MissingPropertyException if the variable cannot be found.<br/> 121 * To work around this, the custom {@link #var(String)} function is available. 122 * </p> 123 */ 124 public boolean empty(Object obj) { 125 return obj == null || 126 (obj instanceof String && ((String)obj).isEmpty()) || 127 ((obj.getClass().isArray() && Array.getLength(obj)==0)) || 128 (obj instanceof Collection && ((Collection)obj).size()==0) || 129 (obj instanceof Map && ((Map)obj).isEmpty()); 130 } 131 }