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