| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.forms.util; |
| |
| import java.io.IOException; |
| import java.io.StringReader; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import org.apache.avalon.framework.CascadingRuntimeException; |
| import org.apache.cocoon.components.flow.FlowHelper; |
| import org.apache.cocoon.components.flow.javascript.JavaScriptFlowHelper; |
| import org.apache.cocoon.components.flow.javascript.fom.FOM_JavaScriptFlowHelper; |
| import org.mozilla.javascript.Context; |
| import org.mozilla.javascript.Function; |
| import org.mozilla.javascript.JavaScriptException; |
| import org.mozilla.javascript.Script; |
| import org.mozilla.javascript.Scriptable; |
| import org.mozilla.javascript.ScriptableObject; |
| import org.w3c.dom.Element; |
| |
| /** |
| * Helper methods to use JavaScript in various locations of the Cocoon Forms configuration files |
| * such as event listeners and bindings. |
| * |
| * @version $Id$ |
| */ |
| public class JavaScriptHelper { |
| |
| /** |
| * A shared root scope, avoiding to recreate a new one each time. |
| */ |
| private static Scriptable _rootScope; |
| |
| /** |
| * Build a script with the content of a DOM element. |
| * |
| * @param element the element containing the script |
| * @return the compiled script |
| * @throws IOException |
| */ |
| public static Script buildScript(Element element) throws IOException { |
| String jsText = DomHelper.getElementText(element); |
| String sourceName = DomHelper.getSystemIdLocation(element); |
| |
| Context ctx = Context.enter(); |
| Script script; |
| try { |
| |
| script = ctx.compileReader( |
| // To use rhino1.5r4-continuations-R26.jar as a workaround for COCOON-1579: Uncomment the next line. |
| // getRootScope(null), //scope |
| new StringReader(jsText), // in |
| sourceName == null ? "<unknown>" : sourceName, // sourceName |
| DomHelper.getLineLocation(element), // lineNo |
| null // securityDomain |
| ); |
| } finally { |
| Context.exit(); |
| } |
| return script; |
| } |
| |
| /** |
| * Build a function with the content of a DOM element. |
| * |
| * @param element the element containing the function body |
| * @param name the name of the function |
| * @param argumentNames names of the function arguments |
| * @return the compiled function |
| * @throws IOException |
| */ |
| public static Function buildFunction(Element element, String name, String[] argumentNames) throws IOException { |
| // Enclose the script text with a function declaration |
| StringBuffer buffer = new StringBuffer("function ").append(name).append("("); |
| for (int i = 0; i < argumentNames.length; i++) { |
| if (i > 0) { |
| buffer.append(','); |
| } |
| buffer.append(argumentNames[i]); |
| } |
| buffer.append(") {\n").append(DomHelper.getElementText(element)).append("\n}"); |
| |
| String jsText = buffer.toString(); |
| String sourceName = DomHelper.getSystemIdLocation(element); |
| |
| Context ctx = Context.enter(); |
| Function func; |
| try { |
| func = ctx.compileFunction( |
| getRootScope(null), //scope |
| jsText, // in |
| sourceName == null ? "<unknown>" : sourceName, // sourceName |
| DomHelper.getLineLocation(element) - 1, // lineNo, "-1" because we added "function..." |
| null // securityDomain |
| ); |
| } finally { |
| Context.exit(); |
| } |
| return func; |
| } |
| |
| /** |
| * Get a root scope for building child scopes. |
| * |
| * @return an appropriate root scope |
| */ |
| public static Scriptable getRootScope(Map objectModel) { |
| // FIXME: TemplateOMH should be used in 2.2 |
| //return TemplateObjectModelHelper.getScope(); |
| |
| |
| if (_rootScope == null) { |
| // Create it if never used up to now |
| Context ctx = Context.enter(); |
| try { |
| _rootScope = ctx.initStandardObjects(null); |
| try { |
| ScriptableObject.defineClass(_rootScope, FOM_SimpleCocoon.class); |
| } catch (Exception e) { |
| throw new CascadingRuntimeException("Cannot setup a root context with a cocoon object for javascript", e); |
| } |
| } finally { |
| Context.exit(); |
| } |
| } |
| if (objectModel == null) { |
| return _rootScope; |
| } else { |
| Context ctx = Context.enter(); |
| try { |
| Scriptable scope = ctx.newObject(_rootScope); |
| FOM_SimpleCocoon cocoon = (FOM_SimpleCocoon) ctx.newObject(scope, "FOM_SimpleCocoon", new Object[] { }); |
| cocoon.setObjectModel(objectModel); |
| cocoon.setParentScope(scope); |
| scope.put("cocoon", scope, cocoon); |
| return scope; |
| } catch (Exception e) { |
| throw new CascadingRuntimeException("Cannot setup a root context with a cocoon object for javascript", e); |
| } finally { |
| Context.exit(); |
| } |
| } |
| } |
| |
| /** |
| * Get a parent scope for building a child scope. The request is searched for an existing scope |
| * that can be provided by a flowscript higher in the call stack, giving visibility to flowscript |
| * functions and global (session) variables. |
| * |
| * @param objectModel the object model where the flowscript scope will be searched (can be <code>null</code>). |
| * @return an appropriate parent scope. |
| */ |
| public static Scriptable getParentScope(Map objectModel) { |
| // Try to get the flowscript scope |
| Scriptable parentScope = null; |
| if (objectModel != null) { |
| parentScope = FOM_JavaScriptFlowHelper.getFOM_FlowScope(objectModel); |
| } |
| |
| if (parentScope != null) { |
| return parentScope; |
| } else { |
| return getRootScope(objectModel); |
| } |
| } |
| |
| public static Object execScript(Script script, Map values, Map objectModel) throws JavaScriptException { |
| Context ctx = Context.enter(); |
| try { |
| Scriptable parentScope = getParentScope(objectModel); |
| |
| // Create a new local scope |
| Scriptable scope; |
| try { |
| scope = ctx.newObject(parentScope); |
| } catch (Exception e) { |
| // Should normally not happen |
| throw new CascadingRuntimeException("Cannot create script scope", e); |
| } |
| scope.setParentScope(parentScope); |
| |
| // Populate the scope |
| Iterator iter = values.entrySet().iterator(); |
| while(iter.hasNext()) { |
| Map.Entry entry = (Map.Entry)iter.next(); |
| String key = (String)entry.getKey(); |
| Object value = entry.getValue(); |
| scope.put(key, scope, Context.toObject(value, scope)); |
| } |
| |
| if (objectModel != null) { |
| Object viewData = FlowHelper.getContextObject(objectModel); |
| if (viewData != null) { |
| scope.put("viewData", scope, Context.toObject(viewData, scope)); |
| } |
| } |
| |
| Object result = script.exec(ctx, scope); |
| return JavaScriptFlowHelper.unwrap(result); |
| } finally { |
| Context.exit(); |
| } |
| } |
| |
| public static Object callFunction(Function func, Object thisObject, Object[] arguments, Map objectModel) throws JavaScriptException { |
| Context ctx = Context.enter(); |
| try { |
| Scriptable scope = getParentScope(objectModel); |
| |
| if (objectModel != null) { |
| // we always add the viewData even it is null (see bug COCOON-1916) |
| final Object viewData = FlowHelper.getContextObject(objectModel); |
| // Create a new local scope to hold the view data |
| final Scriptable newScope; |
| try { |
| newScope = ctx.newObject(scope); |
| } catch (Exception e) { |
| // Should normally not happen |
| throw new CascadingRuntimeException("Cannot create function scope", e); |
| } |
| newScope.setParentScope(scope); |
| scope = newScope; |
| |
| if ( viewData != null ) { |
| scope.put("viewData", scope, Context.toObject(viewData, scope)); |
| } else { |
| scope.put("viewData", scope, null); |
| } |
| } |
| func.setParentScope(scope); |
| Object result = func.call(ctx, scope, thisObject == null? null: Context.toObject(thisObject, scope), arguments); |
| return JavaScriptFlowHelper.unwrap(result); |
| } finally { |
| Context.exit(); |
| } |
| } |
| } |