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                     // Remove all children
199                     for (Node child = oldNode.getFirstChild();
200                             child != null;
201                             child = child.getNextSibling()) {
202                         oldNode.removeChild(child);
203                     }
204                     if (newNode != null) {
205                         // Adopt new children
206                         for (Node child = newNode.getFirstChild();
207                                 child != null;
208                                 child = child.getNextSibling()) {
209                             Node importedNode = oldNode.getOwnerDocument().
210                                 importNode(child, true);
211                             oldNode.appendChild(importedNode);
212                         }
213                     }
214                 } catch (SCXMLExpressionException see) {
215                     // or something else, stuff toString() into lvalue
216                     Object valueObject = eval.eval(ctx, expr);
217                     SCXMLHelper.setNodeValue(oldNode, valueObject.toString());
218                 }
219                 if (appLog.isDebugEnabled()) {
220                     appLog.debug("<assign>: data node '" + oldNode.getNodeName()
221                         + "' updated");
222                 }
223                 TriggerEvent ev = new TriggerEvent(name + ".change",
224                     TriggerEvent.CHANGE_EVENT);
225                 derivedEvents.add(ev);
226             } else {
227                 appLog.error("<assign>: location does not point to"
228                     + " a <data> node");
229             }
230         } else {
231             // lets try "name" (usage as in Sep '05 WD, useful with <var>)
232             if (!ctx.has(name)) {
233                 errRep.onError(ErrorConstants.UNDEFINED_VARIABLE, name
234                     + " = null", parentState);
235             } else {
236                 Object varObj = null;
237                 if (src != null && src.trim().length() > 0) {
238                     varObj = getSrcNode();
239                 } else {
240                     varObj = eval.eval(ctx, expr);
241                 }
242                 ctx.set(name, varObj);
243                 if (appLog.isDebugEnabled()) {
244                     appLog.debug("<assign>: Set variable '" + name + "' to '"
245                         + String.valueOf(varObj) + "'");
246                 }
247                 TriggerEvent ev = new TriggerEvent(name + ".change",
248                     TriggerEvent.CHANGE_EVENT);
249                 derivedEvents.add(ev);
250             }
251         }
252         ctx.setLocal(getNamespacesKey(), null);
253     }
254 
255     /***
256      * Get the {@link Node} the "src" attribute points to.
257      *
258      * @return The node the "src" attribute points to.
259      */
260     private Node getSrcNode() {
261         String resolvedSrc = src;
262         if (pathResolver != null) {
263             resolvedSrc = pathResolver.resolvePath(src);
264         }
265         Document doc = null;
266         try {
267             doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().
268                 parse(resolvedSrc);
269         } catch (Throwable t) {
270             org.apache.commons.logging.Log log = LogFactory.
271                 getLog(Assign.class);
272             log.error(t.getMessage(), t);
273         }
274         if (doc == null) {
275             return null;
276         }
277         return doc.getDocumentElement();
278     }
279 
280 }