Added: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/EventLoop.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/EventLoop.java?rev=1642281&view=auto ============================================================================== --- sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/EventLoop.java (added) +++ sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/EventLoop.java Fri Nov 28 10:18:01 2014 @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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.sling.scripting.sightly.js.impl.loop; + +import java.util.LinkedList; +import java.util.Queue; + +import org.apache.sling.scripting.sightly.use.SightlyUseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Simulates an event loop for the Rhino JS engine. + */ +public class EventLoop { + + private static final Logger log = LoggerFactory.getLogger(EventLoop.class); + + private Queue<Task> taskQueue = new LinkedList<Task>(); + private boolean isRunning; + + /** + * Add a task to the queue. If the queue is empty, start running tasks. If it + * isn't empty, continue running the available tasks + * @param task the task to be added + */ + public void schedule(Task task) { + taskQueue.offer(task); + run(); + } + + private void run() { + if (isRunning) { + return; + } + isRunning = true; + try { + // Holds the first exception encountered. If there is such a first exception, it will be + // rethrown + Exception thrownException = null; + while (!taskQueue.isEmpty()) { + Task task = taskQueue.poll(); + try { + task.run(); + } catch (Exception e) { + if (thrownException == null) { + thrownException = e; //first exception + } else { + log.error("Additional error occurred while running JS script: ", e); + } + } + } + if (thrownException != null) { + throw new SightlyUseException(thrownException); + } + } finally { + isRunning = false; + } + } + +}
Propchange: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/EventLoop.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/EventLoopInterop.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/EventLoopInterop.java?rev=1642281&view=auto ============================================================================== --- sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/EventLoopInterop.java (added) +++ sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/EventLoopInterop.java Fri Nov 28 10:18:01 2014 @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.sling.scripting.sightly.js.impl.loop; + +import org.mozilla.javascript.Context; + +/** + * Event-loop utilities for interoperability with JS code + */ +public class EventLoopInterop { + + public static final String EVENT_LOOP_KEY = "EventLoop"; + + public static EventLoop obtainEventLoop(Context context) { + EventLoop eventLoop = getEventLoop(context); + if (eventLoop == null) { + eventLoop = new EventLoop(); + context.putThreadLocal(EVENT_LOOP_KEY, eventLoop); + } + return eventLoop; + } + + public static void cleanupEventLoop(Context context) { + context.removeThreadLocal(EVENT_LOOP_KEY); + } + + public static Task schedule(Context context, Runnable runnable) { + Task task = new Task(runnable); + obtainEventLoop(context).schedule(task); + return task; + } + + private static EventLoop getEventLoop(Context context) { + return (EventLoop) context.getThreadLocal(EVENT_LOOP_KEY); + } + +} Propchange: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/EventLoopInterop.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/Task.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/Task.java?rev=1642281&view=auto ============================================================================== --- sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/Task.java (added) +++ sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/Task.java Fri Nov 28 10:18:01 2014 @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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.sling.scripting.sightly.js.impl.loop; + +/** + * Task in an event loop + */ +public class Task { + + private final Runnable runnable; + private boolean active; + + public Task(Runnable runnable) { + this.runnable = runnable; + this.active = true; + } + + public void run() { + if (active) { + runnable.run(); + } + } + + public void deactivate() { + this.active = false; + } +} Propchange: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/loop/Task.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/HybridObject.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/HybridObject.java?rev=1642281&view=auto ============================================================================== --- sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/HybridObject.java (added) +++ sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/HybridObject.java Fri Nov 28 10:18:01 2014 @@ -0,0 +1,163 @@ +/******************************************************************************* + * 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.sling.scripting.sightly.js.impl.rhino; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.sling.scripting.sightly.Record; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + + +/** + * Instances of this class can be used in both Sightly & JS scripts + */ +public class HybridObject implements Scriptable, Record<Object> { + + private final Scriptable scriptable; + private final JsValueAdapter jsValueAdapter; + + public HybridObject(Scriptable scriptable, JsValueAdapter jsValueAdapter) { + this.scriptable = scriptable; + this.jsValueAdapter = jsValueAdapter; + } + + // Record implementation + + @Override + public Object get(String name) { + if (name == null) { + return null; + } + Context.enter(); + try { + return getAdapted(name); + } finally { + Context.exit(); + } + } + + @Override + public Set<String> properties() { + Object[] properties = scriptable.getIds(); + Set<String> keys = new HashSet<String>(); + for (Object property: properties) { + if (property instanceof String) { + keys.add((String) property); + } + } + return keys; + } + + private Object getAdapted(String key) { + Object obj = ScriptableObject.getProperty(scriptable, key); + if (obj == null) { + return null; + } + if (obj instanceof Function) { + return jsValueAdapter.adapt(JsUtils.callFn((Function) obj, null, scriptable, scriptable, new Object[0])); + } + return jsValueAdapter.adapt(obj); + } + + // Scriptable implementation + + @Override + public String getClassName() { + return scriptable.getClassName(); + } + + @Override + public Object get(String name, Scriptable start) { + return scriptable.get(name, start); + } + + @Override + public Object get(int index, Scriptable start) { + return scriptable.get(index, start); + } + + @Override + public boolean has(String name, Scriptable start) { + return scriptable.has(name, start); + } + + @Override + public boolean has(int index, Scriptable start) { + return scriptable.has(index, start); + } + + @Override + public void put(String name, Scriptable start, Object value) { + scriptable.put(name, start, value); + } + + @Override + public void put(int index, Scriptable start, Object value) { + scriptable.put(index, start, value); + } + + @Override + public void delete(String name) { + scriptable.delete(name); + } + + @Override + public void delete(int index) { + scriptable.delete(index); + } + + @Override + public Scriptable getPrototype() { + return scriptable.getPrototype(); + } + + @Override + public void setPrototype(Scriptable prototype) { + scriptable.setPrototype(prototype); + } + + @Override + public Scriptable getParentScope() { + return scriptable.getParentScope(); + } + + @Override + public void setParentScope(Scriptable parent) { + scriptable.setParentScope(parent); + } + + @Override + public Object[] getIds() { + return scriptable.getIds(); + } + + @Override + public Object getDefaultValue(Class hint) { + return scriptable.getDefaultValue(hint); + } + + @Override + public boolean hasInstance(Scriptable instance) { + return scriptable.hasInstance(instance); + } +} Propchange: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/HybridObject.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/JsUtils.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/JsUtils.java?rev=1642281&view=auto ============================================================================== --- sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/JsUtils.java (added) +++ sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/JsUtils.java Fri Nov 28 10:18:01 2014 @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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.sling.scripting.sightly.js.impl.rhino; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; + +/** + * Utilities when inter-operating with JS scripts + */ +public class JsUtils { + + public static Object callFn(Function function, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + boolean exitContext = false; + if (Context.getCurrentContext() == null) { + Context.enter(); + exitContext = true; + } + Context context = (cx == null) ? Context.getCurrentContext() : cx; + Object result = function.call(context, scope, thisObj, args); + if (exitContext) { + Context.exit(); + } + return result; + } + + + +} Propchange: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/JsUtils.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/JsValueAdapter.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/JsValueAdapter.java?rev=1642281&view=auto ============================================================================== --- sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/JsValueAdapter.java (added) +++ sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/JsValueAdapter.java Fri Nov 28 10:18:01 2014 @@ -0,0 +1,164 @@ +/******************************************************************************* + * 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.sling.scripting.sightly.js.impl.rhino; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.Wrapper; +import org.apache.sling.scripting.sightly.js.impl.async.AsyncContainer; +import org.apache.sling.scripting.sightly.js.impl.async.AsyncExtractor; + +/** + * Converts JS objects to Java objects + */ +public class JsValueAdapter { + + private static final Map<String, Class<?>> knownConversions = new HashMap<String, Class<?>>(); + + static { + knownConversions.put("String", String.class); + knownConversions.put("Date", Date.class); + } + + private final AsyncExtractor asyncExtractor; + + public JsValueAdapter(AsyncExtractor asyncExtractor) { + this.asyncExtractor = asyncExtractor; + } + + /** + * Convert a given JS value to a Java object + * @param jsValue the original JS value + * @return the Java correspondent + */ + @SuppressWarnings("unchecked") + public Object adapt(Object jsValue) { + if (jsValue == null || jsValue == Context.getUndefinedValue() || jsValue == ScriptableObject.NOT_FOUND) { + return null; + } + if (jsValue instanceof Wrapper) { + return adapt(((Wrapper) jsValue).unwrap()); + } + if (asyncExtractor.isPromise(jsValue)) { + return adapt(forceAsync(jsValue)); + } + if (jsValue instanceof ScriptableObject) { + return extractScriptable((ScriptableObject) jsValue); + } + if (jsValue instanceof CharSequence) { + //convert any string-like type to plain java strings + return jsValue.toString(); + } + if (jsValue instanceof Map) { + return convertMap((Map) jsValue); + } + if (jsValue instanceof Iterable) { + return convertIterable((Iterable) jsValue); + } + if (jsValue instanceof Number) { + return convertNumber((Number) jsValue); + } + if (jsValue instanceof Object[]) { + return convertIterable(Arrays.asList((Object[]) jsValue)); + } + return jsValue; + } + + private Object convertNumber(Number numValue) { + if (numValue instanceof Double) { + if (isLong((Double) numValue)) { + return numValue.longValue(); + } + } + if (numValue instanceof Float) { + if (isLong((Float) numValue)) { + return numValue.longValue(); + } + } + return numValue; + } + + private boolean isLong(double x) { + return x == Math.floor(x); + } + + private Object forceAsync(Object jsValue) { + AsyncContainer asyncContainer = new AsyncContainer(); + asyncExtractor.extract(jsValue, asyncContainer.createCompletionCallback()); + return asyncContainer.getResult(); + } + + private Object extractScriptable(ScriptableObject scriptableObject) { + Object obj = tryKnownConversion(scriptableObject); + if (obj != null) { return obj; } + if (scriptableObject instanceof NativeArray) { + return convertNativeArray((NativeArray) scriptableObject); + } + if (scriptableObject instanceof Function) { + return callFunction((Function) scriptableObject); + } + return new HybridObject(scriptableObject, this); + } + + private Object callFunction(Function function) { + Object result = JsUtils.callFn(function, null, function, function, new Object[0]); + return adapt(result); + } + + private Object[] convertNativeArray(NativeArray nativeArray) { + int length = (int) nativeArray.getLength(); + Object[] objects = new Object[length]; + for (int i = 0; i < length; i++) { + Object jsItem = nativeArray.get(i, nativeArray); + objects[i] = adapt(jsItem); + } + return objects; + } + + private Map<Object, Object> convertMap(Map<Object, Object> original) { + Map<Object, Object> map = new HashMap<Object, Object>(); + for (Map.Entry<Object, Object> entry : original.entrySet()) { + map.put(entry.getKey(), adapt(entry.getValue())); + } + return map; + } + + private List<Object> convertIterable(Iterable<Object> iterable) { + List<Object> objects = new ArrayList<Object>(); + for (Object obj : iterable) { + objects.add(adapt(obj)); + } + return objects; + } + + private static Object tryKnownConversion(ScriptableObject object) { + String className = object.getClassName(); + Class<?> cls = knownConversions.get(className); + return (cls == null) ? null : Context.jsToJava(object, cls); + } +} Propchange: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/rhino/JsValueAdapter.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/DependencyResolver.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/DependencyResolver.java?rev=1642281&view=auto ============================================================================== --- sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/DependencyResolver.java (added) +++ sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/DependencyResolver.java Fri Nov 28 10:18:01 2014 @@ -0,0 +1,57 @@ +/******************************************************************************* + * 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.sling.scripting.sightly.js.impl.use; + +import javax.script.Bindings; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.scripting.sightly.js.impl.JsEnvironment; +import org.apache.sling.scripting.sightly.js.impl.Utils; +import org.apache.sling.scripting.sightly.js.impl.async.UnaryCallback; +import org.apache.sling.scripting.sightly.use.SightlyUseException; + +/** + * Resolves dependencies specified by the Use function + */ +public class DependencyResolver { + + private final Resource caller; + private final JsEnvironment jsEnvironment; + private final Bindings globalBindings; + + public DependencyResolver(Resource resource, JsEnvironment jsEnvironment, Bindings globalBindings) { + this.caller = resource; + this.jsEnvironment = jsEnvironment; + this.globalBindings = globalBindings; + } + + /** + * Resolve a dependency + * @param dependency the dependency identifier + * @param callback the callback that will receive the resolved dependency + */ + public void resolve(String dependency, UnaryCallback callback) { + if (!Utils.isJsScript(dependency)) { + throw new SightlyUseException("Only JS scripts are allowed as dependencies. Invalid dependency: " + dependency); + } + jsEnvironment.run(caller, dependency, globalBindings, Utils.EMPTY_BINDINGS, callback); + } + +} Propchange: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/DependencyResolver.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/UseFunction.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/UseFunction.java?rev=1642281&view=auto ============================================================================== --- sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/UseFunction.java (added) +++ sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/UseFunction.java Fri Nov 28 10:18:01 2014 @@ -0,0 +1,140 @@ +/******************************************************************************* + * 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.sling.scripting.sightly.js.impl.use; + +import javax.script.Bindings; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.mozilla.javascript.BaseFunction; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.NativeObject; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; +import org.apache.sling.scripting.sightly.js.impl.async.AsyncContainer; +import org.apache.sling.scripting.sightly.js.impl.async.UnaryCallback; +import org.apache.sling.scripting.sightly.js.impl.loop.EventLoopInterop; +import org.apache.sling.scripting.sightly.js.impl.rhino.JsUtils; + +/** + * The JavaScript {@code use} function + */ +public class UseFunction extends BaseFunction { + + private final DependencyResolver dependencyResolver; + private final Scriptable thisObj; + + public UseFunction(DependencyResolver dependencyResolver, Bindings arguments) { + this.dependencyResolver = dependencyResolver; + this.thisObj = createThisBinding(arguments); + } + + @Override + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + Function function; + List<String> depNames; + if (args.length == 0) { + throw new IllegalArgumentException("Not enough arguments for use"); + } else if (args.length == 1) { + function = decodeCallback(args[0]); + depNames = Collections.emptyList(); + } else { + function = decodeCallback(args[1]); + depNames = decodeDepNames(args[0]); + } + return use(depNames, function, cx, scope); + } + + private Object use(List<String> depNames, final Function callback, final Context cx, final Scriptable scope) { + final AsyncContainer asyncContainer = new AsyncContainer(); + if (depNames.isEmpty()) { + callImmediate(callback, asyncContainer, cx, scope); + } else { + final int[] counter = {depNames.size()}; + final Object[] dependencies = new Object[depNames.size()]; + for (int i = 0; i < depNames.size(); i++) { + final int dependencyPos = i; + dependencyResolver.resolve(depNames.get(i), new UnaryCallback() { + @Override + public void invoke(Object arg) { + counter[0]--; + dependencies[dependencyPos] = arg; + if (counter[0] == 0) { + Object result = JsUtils.callFn(callback, cx, scope, thisObj, dependencies); + asyncContainer.complete(result); + } + } + }); + } + } + return asyncContainer; + } + + private void callImmediate(final Function callback, final AsyncContainer asyncContainer, final Context cx, final Scriptable scope) { + EventLoopInterop.schedule(cx, new Runnable() { + @Override + public void run() { + Object value = JsUtils.callFn(callback, cx, scope, thisObj, new Object[0]); + asyncContainer.complete(value); + } + }); + } + + private Function decodeCallback(Object obj) { + if (!(obj instanceof Function)) { + throw new IllegalArgumentException("No callback argument supplied"); + } + return (Function) obj; + } + + private List<String> decodeDepNames(Object obj) { + if (obj instanceof NativeArray) { + return decodeDepArray((NativeArray) obj); + } + return Collections.singletonList(jsToString(obj)); + } + + private List<String> decodeDepArray(NativeArray nativeArray) { + int depLength = (int) nativeArray.getLength(); + List<String> depNames = new ArrayList<String>(depLength); + for (int i = 0; i < depLength; i++) { + String depName = jsToString(nativeArray.get(i, nativeArray)); + depNames.add(depName); + } + return depNames; + } + + private String jsToString(Object obj) { + return (String) Context.jsToJava(obj, String.class); + } + + private Scriptable createThisBinding(Bindings arguments) { + NativeObject nativeObject = new NativeObject(); + for (Map.Entry<String, Object> entry : arguments.entrySet()) { + ScriptableObject.putProperty(nativeObject, entry.getKey(), entry.getValue()); + } + return nativeObject; + } +} Propchange: sling/trunk/contrib/scripting/sightly/js-use-provider/src/main/java/org/apache/sling/scripting/sightly/js/impl/use/UseFunction.java ------------------------------------------------------------------------------ svn:mime-type = text/plain