001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.scxml.model;
018:
019: import java.util.Collection;
020:
021: import javax.xml.parsers.DocumentBuilderFactory;
022:
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025: import org.apache.commons.scxml.Context;
026: import org.apache.commons.scxml.ErrorReporter;
027: import org.apache.commons.scxml.Evaluator;
028: import org.apache.commons.scxml.EventDispatcher;
029: import org.apache.commons.scxml.PathResolver;
030: import org.apache.commons.scxml.SCInstance;
031: import org.apache.commons.scxml.SCXMLExpressionException;
032: import org.apache.commons.scxml.SCXMLHelper;
033: import org.apache.commons.scxml.TriggerEvent;
034: import org.apache.commons.scxml.semantics.ErrorConstants;
035: import org.w3c.dom.Document;
036: import org.w3c.dom.Node;
037:
038: /**
039: * The class in this SCXML object model that corresponds to the
040: * <assign> SCXML element.
041: *
042: */
043: public class Assign extends Action implements PathResolverHolder {
044:
045: /**
046: * Serial version UID.
047: */
048: private static final long serialVersionUID = 1L;
049:
050: /**
051: * Left hand side expression evaluating to a previously
052: * defined variable.
053: */
054: private String name;
055:
056: /**
057: * Left hand side expression evaluating to a location within
058: * a previously defined XML data tree.
059: */
060: private String location;
061:
062: /**
063: * The source where the new XML instance for this location exists.
064: */
065: private String src;
066:
067: /**
068: * Expression evaluating to the new value of the variable.
069: */
070: private String expr;
071:
072: /**
073: * {@link PathResolver} for resolving the "src" result.
074: */
075: private PathResolver pathResolver;
076:
077: /**
078: * Constructor.
079: */
080: public Assign() {
081: super ();
082: }
083:
084: /**
085: * Get the variable to be assigned a new value.
086: *
087: * @return Returns the name.
088: */
089: public String getName() {
090: return name;
091: }
092:
093: /**
094: * Get the variable to be assigned a new value.
095: *
096: * @param name The name to set.
097: */
098: public void setName(final String name) {
099: 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(); child != null; child = child
201: .getNextSibling()) {
202: Node importedNode = oldNode
203: .getOwnerDocument().importNode(
204: 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
212: .toString());
213: }
214: if (appLog.isDebugEnabled()) {
215: appLog.debug("<assign>: data node '"
216: + oldNode.getNodeName() + "' updated");
217: }
218: TriggerEvent ev = new TriggerEvent(name + ".change",
219: TriggerEvent.CHANGE_EVENT);
220: derivedEvents.add(ev);
221: } else {
222: appLog.error("<assign>: location does not point to"
223: + " a <data> node");
224: }
225: } else {
226: // lets try "name" (usage as in Sep '05 WD, useful with <var>)
227: if (!ctx.has(name)) {
228: errRep.onError(ErrorConstants.UNDEFINED_VARIABLE, name
229: + " = null", parentState);
230: } else {
231: Object varObj = null;
232: if (src != null && src.trim().length() > 0) {
233: varObj = getSrcNode();
234: } else {
235: varObj = eval.eval(ctx, expr);
236: }
237: ctx.set(name, varObj);
238: if (appLog.isDebugEnabled()) {
239: appLog.debug("<assign>: Set variable '" + name
240: + "' to '" + String.valueOf(varObj) + "'");
241: }
242: TriggerEvent ev = new TriggerEvent(name + ".change",
243: TriggerEvent.CHANGE_EVENT);
244: derivedEvents.add(ev);
245: }
246: }
247: ctx.setLocal(getNamespacesKey(), null);
248: }
249:
250: /**
251: * Get the {@link Node} the "src" attribute points to.
252: *
253: * @return The node the "src" attribute points to.
254: */
255: private Node getSrcNode() {
256: String resolvedSrc = src;
257: if (pathResolver != null) {
258: resolvedSrc = pathResolver.resolvePath(src);
259: }
260: Document doc = null;
261: try {
262: doc = DocumentBuilderFactory.newInstance()
263: .newDocumentBuilder().parse(resolvedSrc);
264: } catch (Throwable t) {
265: org.apache.commons.logging.Log log = LogFactory
266: .getLog(Assign.class);
267: log.error(t.getMessage(), t);
268: }
269: if (doc == null) {
270: return null;
271: }
272: return doc.getDocumentElement();
273: }
274:
275: }
|