package org.alfresco.repo.jscript; import java.io.Serializable; import java.util.Map; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionDefinition; import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.namespace.QName; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Wrapper; /** * Scriptable Action * * @author davidc */ public final class ScriptAction implements Serializable, Scopeable { private static final long serialVersionUID = 5794161358406531996L; /** Root scope for this object */ private Scriptable scope; /** Converter with knowledge of action parameter values */ private ActionValueConverter converter; private ServiceRegistry services; /** Action state */ private Action action; private ActionDefinition actionDef; private ScriptableParameterMap parameters = null; /** * Construct * * @param action * Alfresco action */ public ScriptAction(ServiceRegistry services, Action action, ActionDefinition actionDef) { this.services = services; this.action = action; this.actionDef = actionDef; this.converter = new ActionValueConverter(); } /** * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) */ public void setScope(Scriptable scope) { this.scope = scope; } /** * Returns the action name * * @return action name */ public String getName() { return this.actionDef.getName(); } public String jsGet_name() { return getName(); } /** * Return all the properties known about this node. The Map returned implements the Scriptable interface to allow access to the properties via JavaScript associative array * access. This means properties of a node can be access thus: node.properties["name"] * * @return Map of properties for this Node. */ @SuppressWarnings("synthetic-access") public Map getParameters() { if (this.parameters == null) { // this Map implements the Scriptable interface for native JS syntax property access this.parameters = new ScriptableParameterMap(); Map actionParams = this.action.getParameterValues(); for (Map.Entry entry : actionParams.entrySet()) { String name = entry.getKey(); this.parameters.put(name, converter.convertActionParamForScript(name, entry.getValue())); } this.parameters.setModified(false); } return this.parameters; } public Map jsGet_parameters() { return getParameters(); } /** * Execute action * * @param node * the node to execute action upon */ @SuppressWarnings("synthetic-access") public void execute(Node node) { if (this.parameters != null && this.parameters.isModified()) { Map actionParams = action.getParameterValues(); actionParams.clear(); for (Map.Entry entry : this.parameters.entrySet()) { // perform the conversion from script wrapper object to repo serializable values String name = entry.getKey(); Serializable value = converter.convertActionParamForRepo(name, entry.getValue()); actionParams.put(name, value); } } services.getActionService().executeAction(action, node.getNodeRef()); // Reset the actioned upon node node.reset(); } /** * Value converter with specific knowledge of action parameters * * @author davidc */ private class ActionValueConverter extends ValueConverter { /** * Convert Action Parameter for Script usage * * @param paramName * parameter name * @param value * value to convert * @return converted value */ @SuppressWarnings("synthetic-access") public Serializable convertActionParamForScript(String paramName, Serializable value) { ParameterDefinition paramDef = actionDef.getParameterDefintion(paramName); if (paramDef != null && paramDef.getType().equals(DataTypeDefinition.QNAME)) { return ((QName) value).toPrefixString(services.getNamespaceService()); } else { return convertValueForScript(services, scope, null, value); } } /** * Convert Action Parameter for Java usage * * @param paramName * parameter name * @param value * value to convert * @return converted value */ @SuppressWarnings("synthetic-access") public Serializable convertActionParamForRepo(String paramName, Serializable value) { ParameterDefinition paramDef = actionDef.getParameterDefintion(paramName); if (paramDef != null && paramDef.getType().equals(DataTypeDefinition.QNAME)) { if (value instanceof Wrapper) { // unwrap a Java object from a JavaScript wrapper // recursively call this method to convert the unwrapped value return convertActionParamForRepo(paramName, (Serializable) ((Wrapper) value).unwrap()); } else { if (value instanceof String) { String stringQName = (String) value; if (stringQName.startsWith("{")) { return QName.createQName(stringQName); } else { return QName.createQName(stringQName, services.getNamespaceService()); } } else { return value; } } } else { return convertValueForRepo(value); } } } /** * Scripted Parameter map with modified flag. * * @author davidc */ public static final class ScriptableParameterMap extends ScriptableHashMap { private static final long serialVersionUID = 574661815973241554L; private boolean modified = false; /** * Is this a modified parameter map? * * @return true => modified */ /* package */boolean isModified() { return modified; } /** * Set explicitly whether this map is modified * * @param modified * true => modified, false => not modified */ /* package */void setModified(boolean modified) { this.modified = modified; } /* * (non-Javadoc) * * @see org.mozilla.javascript.Scriptable#getClassName() */ @Override public String getClassName() { return "ScriptableParameterMap"; } /* * (non-Javadoc) * * @see org.mozilla.javascript.Scriptable#delete(java.lang.String) */ @Override public void delete(String name) { super.delete(name); setModified(true); } /* * (non-Javadoc) * * @see org.mozilla.javascript.Scriptable#put(java.lang.String, org.mozilla.javascript.Scriptable, java.lang.Object) */ @Override public void put(String name, Scriptable start, Object value) { super.put(name, start, value); setModified(true); } } }