# HG changeset patch # User Jaroslav Tulach # Date 1362506436 -3600 # Node ID 2fa85847ccf72444b9bc5bcda2a9abfb2e78be1e # Parent 2108bb8770a56a76e8c141500612a50b8ac592aa Support for event parameters for methods annotated by @On(event). Basic idea by me, tests provided by Toni. Thanks a lot. diff -r 2108bb8770a5 -r 2fa85847ccf7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Tue Mar 05 19:00:36 2013 +0100 @@ -0,0 +1,51 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage; + +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** + * + * @author Jaroslav Tulach + */ +public final class ConvertTypes { + ConvertTypes() { + } + + public static String toString(Object object, String property) { + Object ret = getProperty(object, property); + return ret == null ? null : ret.toString(); + } + + public static double toDouble(Object object, String property) { + Object ret = getProperty(object, property); + return ret instanceof Number ? ((Number)ret).doubleValue() : Double.NaN; + } + + public static int toInt(Object object, String property) { + Object ret = getProperty(object, property); + return ret instanceof Number ? ((Number)ret).intValue() : Integer.MIN_VALUE; + } + + @JavaScriptBody(args = { "object", "property" }, + body = "var p = object[property]; return p ? p : null;" + ) + private static Object getProperty(Object object, String property) { + return null; + } +} diff -r 2108bb8770a5 -r 2fa85847ccf7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue Mar 05 18:47:29 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue Mar 05 19:00:36 2013 +0100 @@ -45,6 +45,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.MirroredTypeException; +import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; import javax.tools.FileObject; @@ -209,7 +210,19 @@ } first = false; if (ve.asType() == stringType) { - params.append('"').append(id).append('"'); + if (ve.getSimpleName().contentEquals("id")) { + params.append('"').append(id).append('"'); + continue; + } + params.append("org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString(ev, \""); + params.append(ve.getSimpleName().toString()); + params.append("\")"); + continue; + } + if (processingEnv.getTypeUtils().getPrimitiveType(TypeKind.DOUBLE) == ve.asType()) { + params.append("org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble(ev, \""); + params.append(ve.getSimpleName().toString()); + params.append("\")"); continue; } String rn = ve.asType().toString(); @@ -222,7 +235,7 @@ continue; } processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, - "@On method can only accept String or " + className + " arguments", + "@On method can only accept String named 'id' or " + className + " arguments", ee ); return false; @@ -252,10 +265,10 @@ } w.append(" }\n"); if (dispatchCnt > 0) { - w.append("class OnDispatch implements Runnable {\n"); + w.append("class OnDispatch implements OnHandler {\n"); w.append(" private final int dispatch;\n"); w.append(" OnDispatch(int d) { dispatch = d; }\n"); - w.append(" public void run() {\n"); + w.append(" public void onEvent(Object ev) {\n"); w.append(" switch (dispatch) {\n"); w.append(dispatch); w.append(" }\n"); diff -r 2108bb8770a5 -r 2fa85847ccf7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Tue Mar 05 18:47:29 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Tue Mar 05 19:00:36 2013 +0100 @@ -61,14 +61,18 @@ /** Executes given runnable when user performs a "click" on the given * element. + * @param data an array of one element to fill with event parameter (if any) * @param r the runnable to execute, never null */ @JavaScriptBody( args={ "ev", "r" }, body="var e = window.document.getElementById(this._id());\n" - + "e[ev._id()] = function() { r.run__V(); };\n" + + "e[ev._id()] = function(ev) {\n" + + " var d = ev ? ev : null;\n" + + " r.onEvent__VLjava_lang_Object_2(d);\n" + + "};\n" ) - final void on(OnEvent ev, Runnable r) { + final void on(OnEvent ev, OnHandler r) { } /** Shows alert message dialog in a browser. diff -r 2108bb8770a5 -r 2fa85847ccf7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnController.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnController.java Tue Mar 05 18:47:29 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnController.java Tue Mar 05 19:00:36 2013 +0100 @@ -30,14 +30,33 @@ this.arr = arr; } + /** Registers an event handler on associated {@link OnEvent} + * and {@link Element} + * + * @param handler the handler to be called when the event occurs + */ + public void perform(final OnHandler handler) { + for (Element e : arr) { + e.on(event, handler); + } + } + /** Registers a runnable to be performed on associated {@link OnEvent} * and {@link Element}. * * @see OnEvent#of */ - public void perform(Runnable r) { + public void perform(final Runnable r) { + class W implements OnHandler { + @Override + public void onEvent(Object event) throws Exception { + r.run(); + } + } + perform(new W()); + OnHandler w = new W(); for (Element e : arr) { - e.on(event, r); + e.on(event, w); } } } diff -r 2108bb8770a5 -r 2fa85847ccf7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnHandler.java Tue Mar 05 19:00:36 2013 +0100 @@ -0,0 +1,33 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +/** Handler to be called when an event in an HTML {@link Page} appears. + * @see OnEvent + * @see OnController + * + * @author Jaroslav Tulach + */ +public interface OnHandler { + /** Called when a DOM event appears + * + * @param event the event as produced by the browser + * @throws Exception execution can throw exception + */ + public void onEvent(Object event) throws Exception; +} diff -r 2108bb8770a5 -r 2fa85847ccf7 javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Tue Mar 05 18:47:29 2013 +0100 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Tue Mar 05 19:00:36 2013 +0100 @@ -60,4 +60,9 @@ } PAGE.PG_TITLE.setText(id); } + + @On(event = CLICK, id={ "pg.canvas" }) + static void clickCanvas(String id, double layerX) { + PAGE.PG_CANVAS.setWidth((int) layerX); + } } diff -r 2108bb8770a5 -r 2fa85847ccf7 javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Tue Mar 05 18:47:29 2013 +0100 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Tue Mar 05 19:00:36 2013 +0100 @@ -36,11 +36,12 @@ assertNotNull(is, "Sample HTML page found"); ProcessPage res = ProcessPage.readPage(is); final Set ids = res.ids(); - assertEquals(ids.size(), 3, "Three ids found: " + ids); + assertEquals(ids.size(), 4, "Four ids found: " + ids); assertEquals(res.tagNameForId("pg.title"), "title"); assertEquals(res.tagNameForId("pg.button"), "button"); assertEquals(res.tagNameForId("pg.text"), "input"); + assertEquals(res.tagNameForId("pg.canvas"), "canvas"); } @Test public void testCompileAndRunPageController() throws Exception { @@ -53,11 +54,13 @@ + "doc.title.innerHTML = 'nothing';\n" + "doc.text = new Object();\n" + "doc.text.value = 'something';\n" + + "doc.canvas = new Object();\n" + "doc.getElementById = function(id) {\n" + " switch(id) {\n" + " case 'pg.button': return doc.button;\n" + " case 'pg.title': return doc.title;\n" + " case 'pg.text': return doc.text;\n" + + " case 'pg.canvas': return doc.canvas;\n" + " }\n" + " throw id;\n" + " }\n" @@ -92,11 +95,13 @@ + "doc.title.innerHTML = 'nothing';\n" + "doc.text = new Object();\n" + "doc.text.value = 'something';\n" + + "doc.canvas = new Object();\n" + "doc.getElementById = function(id) {\n" + " switch(id) {\n" + " case 'pg.button': return doc.button;\n" + " case 'pg.title': return doc.title;\n" + " case 'pg.text': return doc.text;\n" + + " case 'pg.canvas': return doc.canvas;\n" + " }\n" + " throw id;\n" + " }\n" @@ -123,6 +128,52 @@ assertEquals(ret, "pg.title", "Title has been passed to the method argument"); } + @Test public void clickWithArgumentAndParameterCalled() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append( + "var window = new Object();\n" + + "var doc = new Object();\n" + + "var eventObject = new Object();\n" + + "eventObject.layerX = 100;\n" + + "doc.button = new Object();\n" + + "doc.title = new Object();\n" + + "doc.title.innerHTML = 'nothing';\n" + + "doc.text = new Object();\n" + + "doc.text.value = 'something';\n" + + "doc.canvas = new Object();\n" + + "doc.canvas.width = 200;\n" + + "doc.getElementById = function(id) {\n" + + " switch(id) {\n" + + " case 'pg.button': return doc.button;\n" + + " case 'pg.title': return doc.title;\n" + + " case 'pg.text': return doc.text;\n" + + " case 'pg.canvas': return doc.canvas;\n" + + " }\n" + + " throw id;\n" + + " }\n" + + "\n" + + "function clickAndCheck() {\n" + + " doc.canvas.onclick(eventObject);\n" + + " return doc.canvas.width.toString();\n" + + "};\n" + + "\n" + + "window.document = doc;\n" + ); + Invocable i = compileClass(sb, + "org/apidesign/bck2brwsr/htmlpage/PageController" + ); + + Object ret = null; + try { + ret = i.invokeFunction("clickAndCheck"); + } catch (ScriptException ex) { + fail("Execution failed in " + sb, ex); + } catch (NoSuchMethodException ex) { + fail("Cannot find method in " + sb, ex); + } + assertEquals(ret, "100", "layerX has been passed to the method argument"); + } + static Invocable compileClass(StringBuilder sb, String... names) throws ScriptException, IOException { if (sb == null) { sb = new StringBuilder(); diff -r 2108bb8770a5 -r 2fa85847ccf7 javaquery/api/src/test/resources/org/apidesign/bck2brwsr/htmlpage/TestPage.html --- a/javaquery/api/src/test/resources/org/apidesign/bck2brwsr/htmlpage/TestPage.html Tue Mar 05 18:47:29 2013 +0100 +++ b/javaquery/api/src/test/resources/org/apidesign/bck2brwsr/htmlpage/TestPage.html Tue Mar 05 19:00:36 2013 +0100 @@ -26,5 +26,6 @@ New title: +