View Javadoc

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.model;
18  
19  import java.util.Collection;
20  
21  import javax.xml.parsers.DocumentBuilderFactory;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.commons.scxml.Context;
26  import org.apache.commons.scxml.ErrorReporter;
27  import org.apache.commons.scxml.Evaluator;
28  import org.apache.commons.scxml.EventDispatcher;
29  import org.apache.commons.scxml.PathResolver;
30  import org.apache.commons.scxml.SCInstance;
31  import org.apache.commons.scxml.SCXMLExpressionException;
32  import org.apache.commons.scxml.SCXMLHelper;
33  import org.apache.commons.scxml.TriggerEvent;
34  import org.apache.commons.scxml.semantics.ErrorConstants;
35  import org.w3c.dom.Document;
36  import org.w3c.dom.Node;
37  
38  /***
39   * The class in this SCXML object model that corresponds to the
40   * <assign> SCXML element.
41   *
42   */
43  public class Assign extends Action implements PathResolverHolder {
44  
45      /***
46       * Serial version UID.
47       */
48      private static final long serialVersionUID = 1L;
49  
50      /***
51       * Left hand side expression evaluating to a previously
52       * defined variable.
53       */
54      private String name;
55  
56      /***
57       * Left hand side expression evaluating to a location within
58       * a previously defined XML data tree.
59       */
60      private String location;
61  
62      /***
63       * The source where the new XML instance for this location exists.
64       */
65      private String src;
66  
67      /***
68       * Expression evaluating to the new value of the variable.
69       */
70      private String expr;
71  
72      /***
73       * {@link PathResolver} for resolving the "src" result.
74       */
75      private PathResolver pathResolver;
76  
77      /***
78       * Constructor.
79       */
80      public Assign() {
81          super();
82      }
83  
84      /***
85       * Get the variable to be assigned a new value.
86       *
87       * @return Returns the name.
88       */
89      public String getName() {
90          return name;
91      }
92  
93      /***
94       * Get the variable to be assigned a new value.
95       *
96       * @param name The name to set.
97       */
98      public void setName(final String name) {
99          this.name = name;
100     }
101 
102     /***
103      * Get the expr that will evaluate to the new value.
104      *
105      * @return Returns the expr.
106      */
107     public String getExpr() {
108         return expr;
109     }
110 
111     /***
112      * Set the expr that will evaluate to the new value.
113      *
114      * @param expr The expr to set.
115      */
116     public void setExpr(final String expr) {
117         this.expr = expr;
118     }
119 
120     /***
121      * Get the location for a previously defined XML data tree.
122      *
123      * @return Returns the location.
124      */
125     public String getLocation() {
126         return location;
127     }
128 
129     /***
130      * Set the location for a previously defined XML data tree.
131      *
132      * @param location The location.
133      */
134     public void setLocation(final String location) {
135         this.location = location;
136     }
137 
138     /***
139      * Get the source where the new XML instance for this location exists.
140      *
141      * @return Returns the source.
142      */
143     public String getSrc() {
144         return src;
145     }
146 
147     /***
148      * Set the source where the new XML instance for this location exists.
149      *
150      * @param src The source.
151      */
152     public void setSrc(final String src) {
153         this.src = src;
154     }
155 
156     /***
157      * Get the {@link PathResolver}.
158      *
159      * @return Returns the pathResolver.
160      */
161     public PathResolver getPathResolver() {
162         return pathResolver;
163     }
164 
165     /***
166      * Set the {@link PathResolver}.
167      *
168      * @param pathResolver The pathResolver to set.
169      */
170     public void setPathResolver(final PathResolver pathResolver) {
171         this.pathResolver = pathResolver;
172     }
173 
174     /***
175      * {@inheritDoc}
176      */
177     public void execute(final EventDispatcher evtDispatcher,
178             final ErrorReporter errRep, final SCInstance scInstance,
179             final Log appLog, final Collection derivedEvents)
180     throws ModelException, SCXMLExpressionException {
181         State parentState = getParentState();
182         Context ctx = scInstance.getContext(parentState);
183         Evaluator eval = scInstance.getEvaluator();
184         ctx.setLocal(getNamespacesKey(), getNamespaces());
185         // "location" gets preference over "name"
186         if (!SCXMLHelper.isStringEmpty(location)) {
187             Node oldNode = eval.evalLocation(ctx, location);
188             if (oldNode != null) {
189                 //// rvalue may be ...
190                 // a Node, if so, import it at location
191                 Node newNode = null;
192                 try {
193                     if (src != null && src.trim().length() > 0) {
194                         newNode = getSrcNode();
195                     } else {
196                         newNode = eval.evalLocation(ctx, expr);
197                     }
198                     if (newNode != null) {
199                         // adopt children, possible spec clarification needed
200                         for (Node child = newNode.getFirstChild();
201                                 child != null;
202                                 child = child.getNextSibling()) {
203                             Node importedNode = oldNode.getOwnerDocument().
204                                 importNode(child, true);
205                             oldNode.appendChild(importedNode);
206                         }
207                     }
208                 } catch (SCXMLExpressionException see) {
209                     // or something else, stuff toString() into lvalue
210                     Object valueObject = eval.eval(ctx, expr);
211                     SCXMLHelper.setNodeValue(oldNode, valueObject.toString());
212                 }
213                 if (appLog.isDebugEnabled()) {
214                     appLog.debug("<assign>: data node '" + oldNode.getNodeName()
215                         + "' updated");
216                 }
217                 TriggerEvent ev = new TriggerEvent(name + ".change",
218                     TriggerEvent.CHANGE_EVENT);
219                 derivedEvents.add(ev);
220             } else {
221                 appLog.error("<assign>: location does not point to"
222                     + " a <data> node");
223             }
224         } else {
225             // lets try "name" (usage as in Sep '05 WD, useful with <var>)
226             if (!ctx.has(name)) {
227                 errRep.onError(ErrorConstants.UNDEFINED_VARIABLE, name
228                     + " = null", parentState);
229             } else {
230                 Object varObj = null;
231                 if (src != null && src.trim().length() > 0) {
232                     varObj = getSrcNode();
233                 } else {
234                     varObj = eval.eval(ctx, expr);
235                 }
236                 ctx.set(name, varObj);
237                 if (appLog.isDebugEnabled()) {
238                     appLog.debug("<assign>: Set variable '" + name + "' to '"
239                         + String.valueOf(varObj) + "'");
240                 }
241                 TriggerEvent ev = new TriggerEvent(name + ".change",
242                     TriggerEvent.CHANGE_EVENT);
243                 derivedEvents.add(ev);
244             }
245         }
246         ctx.setLocal(getNamespacesKey(), null);
247     }
248 
249     /***
250      * Get the {@link Node} the "src" attribute points to.
251      *
252      * @return The node the "src" attribute points to.
253      */
254     private Node getSrcNode() {
255         String resolvedSrc = src;
256         if (pathResolver != null) {
257             resolvedSrc = pathResolver.resolvePath(src);
258         }
259         Document doc = null;
260         try {
261             doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().
262                 parse(resolvedSrc);
263         } catch (Throwable t) {
264             org.apache.commons.logging.Log log = LogFactory.
265                 getLog(Assign.class);
266             log.error(t.getMessage(), t);
267         }
268         if (doc == null) {
269             return null;
270         }
271         return doc.getDocumentElement();
272     }
273 
274 }