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