You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by gb...@apache.org on 2009/06/12 19:00:08 UTC

svn commit: r784193 - in /incubator/pivot/trunk: demos/src/pivot/demos/scripting/ wtk/src/pivot/wtkx/

Author: gbrown
Date: Fri Jun 12 17:00:08 2009
New Revision: 784193

URL: http://svn.apache.org/viewvc?rev=784193&view=rev
Log:
Add support for inline scripts to WTKXSerializer; allow event handlers to be specified using a simplified function-based syntax.

Modified:
    incubator/pivot/trunk/demos/src/pivot/demos/scripting/ScriptingDemo.java
    incubator/pivot/trunk/demos/src/pivot/demos/scripting/demo.groovy
    incubator/pivot/trunk/demos/src/pivot/demos/scripting/demo.js
    incubator/pivot/trunk/demos/src/pivot/demos/scripting/scripting_demo.wtkx
    incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXSerializer.java

Modified: incubator/pivot/trunk/demos/src/pivot/demos/scripting/ScriptingDemo.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/demos/src/pivot/demos/scripting/ScriptingDemo.java?rev=784193&r1=784192&r2=784193&view=diff
==============================================================================
--- incubator/pivot/trunk/demos/src/pivot/demos/scripting/ScriptingDemo.java (original)
+++ incubator/pivot/trunk/demos/src/pivot/demos/scripting/ScriptingDemo.java Fri Jun 12 17:00:08 2009
@@ -17,6 +17,7 @@
 package pivot.demos.scripting;
 
 import pivot.collections.Dictionary;
+import pivot.collections.List;
 import pivot.wtk.Application;
 import pivot.wtk.Button;
 import pivot.wtk.ButtonPressListener;
@@ -36,6 +37,7 @@
     private Window window = null;
 
     @WTKX private String foo;
+    @WTKX private List<?> listData;
 
     public void startup(Display display, Dictionary<String, String> properties)
         throws Exception {
@@ -43,7 +45,9 @@
         window = (Window)wtkxSerializer.readObject(this, "scripting_demo.wtkx");
         wtkxSerializer.bind(this);
 
-        System.out.println("foo = " + foo);
+        System.out.println("foo = \"" + foo + "\"");
+        System.out.println("listData.getLength() = " + listData.getLength());
+
         window.open(display);
     }
 

Modified: incubator/pivot/trunk/demos/src/pivot/demos/scripting/demo.groovy
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/demos/src/pivot/demos/scripting/demo.groovy?rev=784193&r1=784192&r2=784193&view=diff
==============================================================================
--- incubator/pivot/trunk/demos/src/pivot/demos/scripting/demo.groovy (original)
+++ incubator/pivot/trunk/demos/src/pivot/demos/scripting/demo.groovy Fri Jun 12 17:00:08 2009
@@ -14,40 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import pivot.collections.adapter.*
 import pivot.wtk.*
 
-foo = "ABCDE"
-
-def doSomething(button) {
+def showAlert(button) {
     Alert.alert("You clicked me!", button.getWindow())
 }
-
-public class MyButtonPressListener1 implements ButtonPressListener {
-    private Script script;
-    
-    public MyButtonPressListener1(Script script) {
-        this.script = script
-    }
-    
-    public void buttonPressed(Button button) {
-        script.doSomething(button)
-    }
-}
-
-buttonPressListener1 = new MyButtonPressListener1(this)
-
-public class MyButtonPressListener2 implements ButtonPressListener {
-    public void buttonPressed(Button button) {
-        System.out.println("[Groovy] A button was clicked.");
-    }
-}
-
-buttonPressListener2 = new MyButtonPressListener2()
-
-listData = []
-listData << "One"
-listData << "Two"
-listData << "Three"
-
-listData = new ListAdapter(listData)

Modified: incubator/pivot/trunk/demos/src/pivot/demos/scripting/demo.js
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/demos/src/pivot/demos/scripting/demo.js?rev=784193&r1=784192&r2=784193&view=diff
==============================================================================
--- incubator/pivot/trunk/demos/src/pivot/demos/scripting/demo.js (original)
+++ incubator/pivot/trunk/demos/src/pivot/demos/scripting/demo.js Fri Jun 12 17:00:08 2009
@@ -14,29 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-importPackage(Packages.java.lang);
-importPackage(Packages.pivot.collections);
 importPackage(Packages.pivot.wtk);
 
-var foo = "Hello World";
-
-function doSomething(button) {
+function showAlert(button) {
     Alert.alert("You clicked me!", button.getWindow());
 }
-
-var buttonPressListener1 = new ButtonPressListener() {
-    buttonPressed: function(button) {
-        doSomething(button);
-    }
-};
-
-var buttonPressListener2 = new ButtonPressListener() {
-    buttonPressed: function(button) {
-        System.out.println("[JavaScript] A button was clicked.");
-    }
-};
-
-var listData = new ArrayList();
-listData.add("One");
-listData.add("Two");
-listData.add("Three");

Modified: incubator/pivot/trunk/demos/src/pivot/demos/scripting/scripting_demo.wtkx
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/demos/src/pivot/demos/scripting/scripting_demo.wtkx?rev=784193&r1=784192&r2=784193&view=diff
==============================================================================
--- incubator/pivot/trunk/demos/src/pivot/demos/scripting/scripting_demo.wtkx (original)
+++ incubator/pivot/trunk/demos/src/pivot/demos/scripting/scripting_demo.wtkx Fri Jun 12 17:00:08 2009
@@ -20,17 +20,75 @@
     xmlns:wtkx="http://incubator.apache.org/pivot/wtkx/1.1"
     xmlns:scripting="pivot.demos.scripting"
     xmlns="pivot.wtk">
+    <wtkx:script language="javascript">
+    <![CDATA[
+    importPackage(Packages.pivot.collections);
+
+    var foo = "Hello World";
+
+    var listData = new ArrayList();
+    listData.add("One");
+    listData.add("Two");
+    listData.add("Three");
+    ]]>
+    </wtkx:script>
+    <wtkx:script src="demo.js"/>
+
+    <!--
+    <wtkx:script language="groovy">
+    <![CDATA[
+    import pivot.collections.adapter.*
+
+    def foo = "ABCDE"
+
+    def listData = []
+    listData << "One"
+    listData << "Two"
+    listData << "Three"
+
+    listData = new ListAdapter(listData)
+    ]]>
+    </wtkx:script>
+    <wtkx:script src="demo.groovy"/>
+    -->
+
     <content>
         <Border>
             <content>
                 <FlowPane orientation="vertical" styles="{padding:6}">
-                    <wtkx:script src="demo.js"/>
-                    <!-- <wtkx:script src="demo.groovy"/> -->
-
-                    <PushButton wtkx:id="pushButton" buttonData="Click Me!"
-                        buttonPressListeners="buttonPressListener1, buttonPressListener2">
+                    <PushButton wtkx:id="pushButton" buttonData="Click Me!">
                         <buttonPressListeners>
-                            <scripting:ScriptingDemo.MyButtonPressListener/>
+                            <wtkx:script language="javascript">
+                            <![CDATA[
+                            function buttonPressed(button) {
+                                showAlert(button);
+                            }
+                            ]]>
+                            </wtkx:script>
+                            <wtkx:script language="javascript">
+                            <![CDATA[
+                            function buttonPressed(button) {
+                                java.lang.System.out.println("[JavaScript] A button was clicked.");
+                            }
+                            ]]>
+                            </wtkx:script>
+
+                            <!--
+                            <wtkx:script language="groovy">
+                            <![CDATA[
+                            def buttonPressed(button) {
+                                showAlert(button);
+                            }
+                            ]]>
+                            </wtkx:script>
+                            <wtkx:script language="groovy">
+                            <![CDATA[
+                            def buttonPressed(Button button) {
+                                System.out.println("[Groovy] A button was clicked.");
+                            }
+                            ]]>
+                            </wtkx:script>
+                            -->
                         </buttonPressListeners>
                     </PushButton>
 

Modified: incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXSerializer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXSerializer.java?rev=784193&r1=784192&r2=784193&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXSerializer.java (original)
+++ incubator/pivot/trunk/wtk/src/pivot/wtkx/WTKXSerializer.java Fri Jun 12 17:00:08 2009
@@ -23,9 +23,11 @@
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Collection;
@@ -33,9 +35,12 @@
 import java.util.Set;
 
 import javax.script.Bindings;
+import javax.script.Invocable;
 import javax.script.ScriptContext;
 import javax.script.ScriptEngine;
 import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
 import javax.xml.stream.XMLInputFactory;
 import javax.xml.stream.XMLStreamConstants;
 import javax.xml.stream.XMLStreamReader;
@@ -534,7 +539,7 @@
 
                                 // Apply remaining attributes
                                 if (element.value instanceof Dictionary) {
-                                    // The element is an untyped object
+                                    // The element is already a dictionary
                                     Dictionary<String, Object> dictionary = (Dictionary<String, Object>)element.value;
 
                                     for (Attribute attribute : attributes) {
@@ -547,7 +552,7 @@
                                         dictionary.put(attribute.localName, resolve(attribute.value, null));
                                     }
                                 } else {
-                                    // The element represents a typed object; apply the attributes
+                                    // The element is not a dictionary; wrap it in a bean dictionary
                                     BeanDictionary beanDictionary = new BeanDictionary(element.value);
 
                                     for (Attribute attribute : attributes) {
@@ -596,8 +601,170 @@
                             }
 
                             case SCRIPT: {
-                                // TODO Process attributes looking for src and language; create invocation
-                                // handler if parent element is a listener list
+                                // Load the script engine manager
+                                if (scriptEngineManager == null) {
+                                    scriptEngineManager = new javax.script.ScriptEngineManager();
+                                    scriptEngineManager.setBindings(new NamedObjectBindings());
+                                }
+
+                                // Process attributes looking for src and language
+                                String src = null;
+                                String language = null;
+                                for (Attribute attribute : element.attributes) {
+                                    if (attribute.prefix != null) {
+                                        throw new SerializationException(WTKX_PREFIX + ":" + attribute.localName
+                                            + " is not a valid attribute.");
+                                    } else {
+                                        if (attribute.prefix != null) {
+                                            throw new SerializationException(attribute.prefix + ":" +
+                                                attribute.localName + " is not a valid" + " attribute for the "
+                                                + WTKX_PREFIX + ":" + SCRIPT_TAG + " tag.");
+                                        }
+
+                                        if (attribute.localName.equals(SCRIPT_SRC_ATTRIBUTE)) {
+                                            src = attribute.value;
+                                        } else if (attribute.localName.equals(SCRIPT_LANGUAGE_ATTRIBUTE)) {
+                                            language = attribute.value;
+                                        } else {
+                                            throw new SerializationException(attribute.localName + " is not a valid"
+                                                + " attribute for the " + WTKX_PREFIX + ":" + SCRIPT_TAG + " tag.");
+                                        }
+                                    }
+                                }
+
+                                Bindings bindings;
+                                if (element.parent.value instanceof ListenerList<?>) {
+                                    // Don't pollute the engine namespace with the listener functions
+                                    bindings = new SimpleBindings();
+                                } else {
+                                    bindings = scriptEngineManager.getBindings();
+                                }
+
+                                // Execute script
+                                final ScriptEngine scriptEngine;
+
+                                if (src != null) {
+                                    // The script is located in an external file
+                                    if (language != null) {
+                                        throw new SerializationException("Cannot combine " + SCRIPT_SRC_ATTRIBUTE
+                                            + " and " + SCRIPT_LANGUAGE_ATTRIBUTE + " in a "
+                                            + WTKX_PREFIX + ":" + SCRIPT_TAG + " tag.");
+                                    }
+
+                                    int i = src.lastIndexOf(".");
+                                    if (i == -1) {
+                                        throw new SerializationException("Cannot determine type of script \""
+                                            + src + "\".");
+                                    }
+
+                                    String extension = src.substring(i + 1);
+                                    scriptEngine = scriptEngineManager.getEngineByExtension(extension);
+
+                                    if (scriptEngine == null) {
+                                        throw new SerializationException("Unable to find scripting engine for "
+                                            + " extension " + extension + ".");
+                                    }
+
+                                    scriptEngine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
+
+                                    try {
+                                        URL scriptLocation;
+                                        if (src.charAt(0) == '/') {
+                                            ClassLoader classLoader = ThreadUtilities.getClassLoader();
+                                            scriptLocation = classLoader.getResource(src);
+                                        } else {
+                                            scriptLocation = new URL(location, src);
+                                        }
+
+                                        BufferedReader scriptReader = null;
+                                        try {
+                                            scriptReader = new BufferedReader(new InputStreamReader(scriptLocation.openStream()));
+                                            scriptEngine.eval(scriptReader);
+                                        } catch(ScriptException exception) {
+                                            throw new SerializationException(exception);
+                                        } finally {
+                                            if (scriptReader != null) {
+                                                scriptReader.close();
+                                            }
+                                        }
+                                    } catch (IOException exception) {
+                                        throw new SerializationException(exception);
+                                    }
+                                } else if (language != null) {
+                                    // The script is inline
+                                    scriptEngine = scriptEngineManager.getEngineByName(language);
+
+                                    if (scriptEngine == null) {
+                                        throw new SerializationException("Unable to find scripting engine for "
+                                            + " language " + language + ".");
+                                    }
+
+                                    scriptEngine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
+
+                                    if (element.value != null) {
+                                        try {
+                                            scriptEngine.eval((String)element.value);
+                                        } catch (ScriptException exception) {
+                                            throw new SerializationException(exception);
+                                        }
+                                    }
+                                } else {
+                                    throw new SerializationException("Either " + SCRIPT_SRC_ATTRIBUTE + " or "
+                                        + SCRIPT_LANGUAGE_ATTRIBUTE + " is required for the "
+                                        + WTKX_PREFIX + ":" + SCRIPT_TAG + " tag.");
+                                }
+
+                                if (element.parent.value instanceof ListenerList<?>) {
+                                    // Create an invocation handler for this listener
+                                    Class<?> listenerListClass = element.parent.value.getClass();
+
+                                    Method addMethod;
+                                    try {
+                                        addMethod = listenerListClass.getMethod("add",
+                                            new Class<?>[] {Object.class});
+                                    } catch (NoSuchMethodException exception) {
+                                        throw new RuntimeException(exception);
+                                    }
+
+                                    InvocationHandler handler = new InvocationHandler() {
+                                        public Object invoke(Object proxy, Method method, Object[] args)
+                                            throws Throwable {
+                                            Object result = null;
+                                            String methodName = method.getName();
+
+                                            Bindings bindings = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
+                                            if (bindings.containsKey(methodName)) {
+                                                Invocable invocable;
+                                                try {
+                                                    invocable = (Invocable)scriptEngine;
+                                                } catch (ClassCastException exception) {
+                                                    throw new SerializationException(exception);
+                                                }
+
+                                                result = invocable.invokeFunction(methodName, args);
+                                            }
+
+                                            return result;
+                                        }
+                                    };
+
+                                    // Create the listener and add it to the list
+                                    java.lang.reflect.Type[] genericInterfaces = listenerListClass.getGenericInterfaces();
+                                    Class<?> listenerClass = (Class<?>)genericInterfaces[0];
+
+                                    Object listener =
+                                        Proxy.newProxyInstance(ThreadUtilities.getClassLoader(),
+                                            new Class[]{listenerClass}, handler);
+
+                                    try {
+                                        addMethod.invoke(element.parent.value, new Object[] {listener});
+                                    } catch (IllegalAccessException exception) {
+                                        throw new SerializationException(exception);
+                                    } catch (InvocationTargetException exception) {
+                                        throw new SerializationException(exception);
+                                    }
+                                }
+
                                 break;
                             }
                         }
@@ -719,10 +886,12 @@
     }
 
     public Object put(String name, Object value) {
+        // TODO
         throw new UnsupportedOperationException();
     }
 
     public Object remove(String name) {
+        // TODO
         throw new UnsupportedOperationException();
     }