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.model;
18
19 import java.io.IOException;
20
21 import javax.xml.parsers.DocumentBuilderFactory;
22 import javax.xml.parsers.FactoryConfigurationError;
23 import javax.xml.parsers.ParserConfigurationException;
24
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.commons.scxml2.ActionExecutionContext;
27 import org.apache.commons.scxml2.Context;
28 import org.apache.commons.scxml2.Evaluator;
29 import org.apache.commons.scxml2.PathResolver;
30 import org.apache.commons.scxml2.SCXMLExpressionException;
31 import org.w3c.dom.*;
32 import org.xml.sax.SAXException;
33
34 /**
35 * The class in this SCXML object model that corresponds to the
36 * <assign> SCXML element.
37 *
38 */
39 public class Assign extends Action implements PathResolverHolder {
40
41 /**
42 * Serial version UID.
43 */
44 private static final long serialVersionUID = 1L;
45
46 /**
47 * Left hand side expression evaluating to a location within
48 * a previously defined XML data tree.
49 */
50 private String location;
51
52 /**
53 * The source where the new XML instance for this location exists.
54 */
55 private String src;
56
57 /**
58 * Expression evaluating to the new value of the variable.
59 */
60 private String expr;
61
62 /**
63 * Defines the nature of the insertion to be performed, default {@link Evaluator.AssignType#REPLACE_CHILDREN}
64 */
65 private Evaluator.AssignType type;
66
67 /**
68 * The attribute name to add at the specified location when using {@link Evaluator.AssignType#ADD_ATTRIBUTE}
69 */
70 private String attr;
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 expr that will evaluate to the new value.
86 *
87 * @return Returns the expr.
88 */
89 public String getExpr() {
90 return expr;
91 }
92
93 /**
94 * Set the expr that will evaluate to the new value.
95 *
96 * @param expr The expr to set.
97 */
98 public void setExpr(final String expr) {
99 this.expr = expr;
100 }
101
102 /**
103 * Get the location for a previously defined XML data tree.
104 *
105 * @return Returns the location.
106 */
107 public String getLocation() {
108 return location;
109 }
110
111 /**
112 * Set the location for a previously defined XML data tree.
113 *
114 * @param location The location.
115 */
116 public void setLocation(final String location) {
117 this.location = location;
118 }
119
120 /**
121 * Get the source where the new XML instance for this location exists.
122 *
123 * @return Returns the source.
124 */
125 public String getSrc() {
126 return src;
127 }
128
129 /**
130 * Set the source where the new XML instance for this location exists.
131 *
132 * @param src The source.
133 */
134 public void setSrc(final String src) {
135 this.src = src;
136 }
137
138 /**
139 * Get the {@link PathResolver}.
140 *
141 * @return Returns the pathResolver.
142 */
143 public PathResolver getPathResolver() {
144 return pathResolver;
145 }
146
147 /**
148 * Set the {@link PathResolver}.
149 *
150 * @param pathResolver The pathResolver to set.
151 */
152 public void setPathResolver(final PathResolver pathResolver) {
153 this.pathResolver = pathResolver;
154 }
155
156 public Evaluator.AssignType getType() {
157 return type;
158 }
159
160 public void setType(final Evaluator.AssignType type) {
161 this.type = type;
162 }
163
164 public String getAttr() {
165 return attr;
166 }
167
168 public void setAttr(final String attr) {
169 this.attr = attr;
170 }
171
172 /**
173 * {@inheritDoc}
174 */
175 @Override
176 public void execute(ActionExecutionContext exctx) throws ModelException, SCXMLExpressionException {
177 EnterableState parentState = getParentEnterableState();
178 Context ctx = exctx.getContext(parentState);
179 Evaluator evaluator = exctx.getEvaluator();
180 ctx.setLocal(getNamespacesKey(), getNamespaces());
181 Object data;
182 if (src != null && src.trim().length() > 0) {
183 data = getSrcNode();
184 } else {
185 data = evaluator.eval(ctx, expr);
186 }
187
188 evaluator.evalAssign(ctx, location, data, type, attr);
189 if (exctx.getAppLog().isDebugEnabled()) {
190 exctx.getAppLog().debug("<assign>: '" + location + "' updated");
191 }
192 // TODO: introduce a optional 'trace.change' setting or something alike to enable .change events,
193 // but don't do this by default as it can interfere with transitions not expecting such events
194 /*
195 if ((Evaluator.XPATH_DATA_MODEL.equals(evaluator.getSupportedDatamodel()) && location.startsWith("$") && ctx.has(location.substring(1))
196 || ctx.has(location))) {
197 TriggerEvent ev = new TriggerEvent(location + ".change", TriggerEvent.CHANGE_EVENT);
198 exctx.getInternalIOProcessor().addEvent(ev);
199 }
200 */
201 ctx.setLocal(getNamespacesKey(), null);
202 }
203
204 /**
205 * Get the {@link Node} the "src" attribute points to.
206 *
207 * @return The node the "src" attribute points to.
208 */
209 private Node getSrcNode() {
210 String resolvedSrc = src;
211 if (pathResolver != null) {
212 resolvedSrc = pathResolver.resolvePath(src);
213 }
214 Document doc = null;
215 try {
216 doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(resolvedSrc);
217 } catch (FactoryConfigurationError t) {
218 logError(t);
219 } catch (SAXException e) {
220 logError(e);
221 } catch (IOException e) {
222 logError(e);
223 } catch (ParserConfigurationException e) {
224 logError(e);
225 }
226 if (doc == null) {
227 return null;
228 }
229 return doc.getDocumentElement();
230 }
231
232 /**
233 * @param throwable The throwable to log about
234 */
235 private void logError(Throwable throwable) {
236 org.apache.commons.logging.Log log = LogFactory.
237 getLog(Assign.class);
238 log.error(throwable.getMessage(), throwable);
239 }
240 }