# HG changeset patch # User Jaroslav Tulach # Date 1352458020 -3600 # Node ID 590958fcb7d7756de54787326be57577083fdb8a # Parent 070fc584429503db3fcad6f37452fe984d27dd53 Moving the htmlpage API into a submodule in preparation of adding in an example of its usage diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/pom.xml --- a/htmlpage/pom.xml Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ - - - 4.0.0 - - org.apidesign - bck2brwsr - 1.0-SNAPSHOT - - org.apidesign.bck2brwsr - htmlpage - 1.0-SNAPSHOT - htmlpage - http://maven.apache.org - - UTF-8 - - - - org.testng - testng - test - - - junit - junit - - - - - org.netbeans.api - org-openide-util-lookup - - - org.apidesign.bck2brwsr - core - 1.0-SNAPSHOT - jar - - - org.apidesign.bck2brwsr - emul - 1.0-SNAPSHOT - jar - runtime - - - org.apidesign.bck2brwsr - vm4brwsr - 0.1-SNAPSHOT - jar - test - - - diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,233 +0,0 @@ -/** - * 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 java.io.IOException; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.Completion; -import javax.annotation.processing.Completions; -import javax.annotation.processing.Processor; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.TypeMirror; -import javax.tools.Diagnostic; -import javax.tools.FileObject; -import javax.tools.StandardLocation; -import org.apidesign.bck2brwsr.htmlpage.api.OnClick; -import org.apidesign.bck2brwsr.htmlpage.api.Page; -import org.openide.util.lookup.ServiceProvider; - -/** Annotation processor to process an XHTML page and generate appropriate - * "id" file. - * - * @author Jaroslav Tulach - */ -@ServiceProvider(service=Processor.class) -@SupportedAnnotationTypes({ - "org.apidesign.bck2brwsr.htmlpage.api.Page", - "org.apidesign.bck2brwsr.htmlpage.api.OnClick" -}) -public final class PageProcessor extends AbstractProcessor { - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) { - Page p = e.getAnnotation(Page.class); - PackageElement pe = (PackageElement)e.getEnclosingElement(); - String pkg = pe.getQualifiedName().toString(); - - ProcessPage pp; - try { - InputStream is = openStream(pkg, p.xhtml()); - pp = ProcessPage.readPage(is); - is.close(); - } catch (IOException iOException) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e); - return false; - } - Writer w; - String className = p.className(); - if (className.isEmpty()) { - int indx = p.xhtml().indexOf('.'); - className = p.xhtml().substring(0, indx); - } - try { - FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e); - w = new OutputStreamWriter(java.openOutputStream()); - try { - w.append("package " + pkg + ";\n"); - w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); - w.append("class ").append(className).append(" {\n"); - for (String id : pp.ids()) { - String tag = pp.tagNameForId(id); - String type = type(tag); - w.append(" ").append("public static final "). - append(type).append(' ').append(cnstnt(id)).append(" = new "). - append(type).append("(\"").append(id).append("\");\n"); - } - w.append(" static {\n"); - if (!initializeOnClick(pe, w, pp)) { - return false; - } - w.append(" }\n"); - w.append("}\n"); - } finally { - w.close(); - } - } catch (IOException ex) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e); - return false; - } - } - return true; - } - - private InputStream openStream(String pkg, String name) throws IOException { - try { - FileObject fo = processingEnv.getFiler().getResource( - StandardLocation.SOURCE_PATH, pkg, name); - return fo.openInputStream(); - } catch (IOException ex) { - return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, pkg, name).openInputStream(); - } - } - - private static String type(String tag) { - if (tag.equals("title")) { - return "Title"; - } - if (tag.equals("button")) { - return "Button"; - } - if (tag.equals("input")) { - return "Input"; - } - return "Element"; - } - - private static String cnstnt(String id) { - return id.toUpperCase(Locale.ENGLISH).replace('.', '_'); - } - - private boolean initializeOnClick(PackageElement pe, Writer w, ProcessPage pp) throws IOException { - TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType(); - for (Element clazz : pe.getEnclosedElements()) { - if (clazz.getKind() != ElementKind.CLASS) { - continue; - } - TypeElement type = (TypeElement)clazz; - for (Element method : clazz.getEnclosedElements()) { - OnClick oc = method.getAnnotation(OnClick.class); - if (oc != null) { - for (String id : oc.id()) { - if (pp.tagNameForId(id) == null) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + oc.id() + " does not exist in the HTML page. Found only " + pp.ids(), method); - return false; - } - ExecutableElement ee = (ExecutableElement)method; - boolean hasParam; - if (ee.getParameters().isEmpty()) { - hasParam = false; - } else { - if (ee.getParameters().size() != 1 || ee.getParameters().get(0).asType() != stringType) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClick method should either have no arguments or one String argument", ee); - return false; - } - hasParam = true; - } - if (!ee.getModifiers().contains(Modifier.STATIC)) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClick method has to be static", ee); - return false; - } - if (ee.getModifiers().contains(Modifier.PRIVATE)) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClick method can't be private", ee); - return false; - } - w.append(" ").append(cnstnt(id)). - append(".addOnClick(new Runnable() { public void run() {\n"); - w.append(" ").append(type.getSimpleName().toString()). - append('.').append(ee.getSimpleName()).append("("); - if (hasParam) { - w.append("\"").append(id).append("\""); - } - w.append(");\n"); - w.append(" }});\n"); - } - } - } - } - return true; - } - - @Override - public Iterable getCompletions( - Element element, AnnotationMirror annotation, - ExecutableElement member, String userText - ) { - if (!userText.startsWith("\"")) { - return Collections.emptyList(); - } - - Element cls = findClass(element); - Page p = cls.getAnnotation(Page.class); - PackageElement pe = (PackageElement) cls.getEnclosingElement(); - String pkg = pe.getQualifiedName().toString(); - ProcessPage pp; - try { - InputStream is = openStream(pkg, p.xhtml()); - pp = ProcessPage.readPage(is); - is.close(); - } catch (IOException iOException) { - return Collections.emptyList(); - } - - List cc = new ArrayList(); - userText = userText.substring(1); - for (String id : pp.ids()) { - if (id.startsWith(userText)) { - cc.add(Completions.of("\"" + id + "\"", id)); - } - } - return cc; - } - - private static Element findClass(Element e) { - if (e == null) { - return null; - } - Page p = e.getAnnotation(Page.class); - if (p != null) { - return e; - } - return e.getEnclosingElement(); - } -} diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/ProcessPage.java --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/ProcessPage.java Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/** - * 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 java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -class ProcessPage { - private final Map ids2Elems = new TreeMap(); - - public Set ids() { - return Collections.unmodifiableSet(ids2Elems.keySet()); - } - - public String tagNameForId(String id) { - return ids2Elems.get(id); - } - - public static ProcessPage readPage(InputStream is) throws IOException { - DocumentBuilderFactory f = DocumentBuilderFactory.newInstance(); - f.setValidating(false); - f.setIgnoringComments(true); - - Document doc = null; - try { - DocumentBuilder b = f.newDocumentBuilder(); - doc = b.parse(is); - } catch (IOException ex) { - throw ex; - } catch (Exception e) { - throw new IOException(e); - } - Element root = doc.getDocumentElement(); - - ProcessPage pp = new ProcessPage(); - pp.seekForIds(root); - return pp; - } - - private void seekForIds(Element e) { - String val = e.getAttribute("id"); - if (val != null && !val.isEmpty()) { - String prev = ids2Elems.put(val, e.getTagName()); - } - NodeList arr = e.getChildNodes(); - for (int i = 0; i < arr.getLength(); i++) { - final Node n = arr.item(i); - if (n instanceof Element) { - seekForIds((Element)n); - } - } - } -} diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Button.java --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Button.java Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/** - * 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; - -/** - * - * @author Jaroslav Tulach - */ -public final class Button extends Element { - public Button(String id) { - super(id); - } - - @Override - void dontSubclass() { - } - - public void setDisabled(boolean state) { - setAttribute(this, "disabled", state); - } -} diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/** - * 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; - -import org.apidesign.bck2brwsr.core.JavaScriptBody; - -/** Represents a generic HTML element. - * - * @author Jaroslav Tulach - */ -public abstract class Element { - private final String id; - - public Element(String id) { - this.id = id; - } - - abstract void dontSubclass(); - - @JavaScriptBody( - args={"el", "property", "value"}, - body="var e = window.document.getElementById(el.fld_id);\n" - + "e[property] = value;\n" - ) - static void setAttribute(Element el, String property, Object value) { - throw new UnsupportedOperationException("Needs JavaScript!"); - } - - @JavaScriptBody( - args={"el", "property"}, - body="var e = window.document.getElementById(el.fld_id);\n" - + "return e[property];\n" - ) - static Object getAttribute(Element el, String property) { - throw new UnsupportedOperationException("Needs JavaScript!"); - } - - /** Executes given runnable when user performs a "click" on the given - * element. - * @param r the runnable to execute, never null - */ - @JavaScriptBody( - args={"el", "r"}, - body="var e = window.document.getElementById(el.fld_id);\n" - + "e.onclick = function() { r.runV(); };\n" - ) - public final void addOnClick(Runnable r) { - throw new UnsupportedOperationException("Needs JavaScript!"); - } -} diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Input.java --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Input.java Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -/** - * 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; - -/** - * - * @author Jaroslav Tulach - */ -public final class Input extends Element { - public Input(String id) { - super(id); - } - - @Override - void dontSubclass() { - } - - public void setAutocomplete(boolean state) { - setAttribute(this, "autocomplete", state); - } - - public final String getValue() { - return (String)getAttribute(this, "value"); - } - - public final void setValue(String txt) { - setAttribute(this, "value", txt); - } -} diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnClick.java --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnClick.java Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/** - * 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; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** Adds an onClick handler to an element identified by given id. - * Apply on a public static void method with no arguments. - * - * @author Jaroslav Tulach - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.METHOD) -public @interface OnClick { - String[] id(); -} diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Page.java --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Page.java Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -/** - * 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; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** Converts an XHTML page into a Java class that contains - * references to all IDs. - * - * @author Jaroslav Tulach - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.TYPE) -public @interface Page { - /** Path to the XHTML page to read in */ - String xhtml(); - /** Name of a Java class to generate. It will contain constants for all - * found elements with IDs. - */ - String className() default ""; -} diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Title.java --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Title.java Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/** - * 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; - -/** - * - * @author Jaroslav Tulach - */ -public class Title extends Element { - public Title(String id) { - super(id); - } - - @Override - void dontSubclass() { - } - - public final void setText(String text) { - setAttribute(this, "innerHTML", text); - } -} diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java --- a/htmlpage/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * 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.htmlpage.api.OnClick; -import org.apidesign.bck2brwsr.htmlpage.api.Page; - -/** Trivial demo for the bck2brwsr project. First of all start - * with your XHTML page. Include there - * a script that will boot Java in your browser. - *

- * Then use @Page annotation to - * generate a Java representation of elements with IDs in that page. - * Depending on the type of the elements, they will have different - * methods (e.g. PG_TITLE has setText, etc.). - * Use @OnClick annotation to associate behavior - * with existing elements. Use the generated elements - * (PG_TITLE, PG_TEXT) to modify the page. - *

- * Everything is type-safe. As soon as somebody modifies the page and - * removes the IDs or re-assigns them to wrong elements. Java compiler - * will emit an error. - *

- * Welcome to the type-safe HTML5 world! - * - * @author Jaroslav Tulach - */ -@Page(xhtml="TestPage.html") -public class PageController { - @OnClick(id="pg.button") - static void updateTitle() { - TestPage.PG_TITLE.setText("You want this window to be named " + TestPage.PG_TEXT.getValue()); - } - - @OnClick(id={ "pg.title", "pg.text" }) - static void click(String id) { - if (!id.equals("pg.title")) { - throw new IllegalStateException(); - } - TestPage.PG_TITLE.setText(id); - } -} diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java --- a/htmlpage/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,148 +0,0 @@ -/** - * 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 java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Method; -import java.util.Set; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import org.testng.annotations.Test; -import static org.testng.Assert.*; - -public class ProcessPageTest { - - - @Test public void findsThreeIds() throws IOException { - InputStream is = ProcessPageTest.class.getResourceAsStream("TestPage.html"); - 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(res.tagNameForId("pg.title"), "title"); - assertEquals(res.tagNameForId("pg.button"), "button"); - assertEquals(res.tagNameForId("pg.text"), "input"); - } - - @Test public void testCompileAndRunPageController() throws Exception { - StringBuilder sb = new StringBuilder(); - sb.append( - "var window = new Object();\n" - + "var doc = new Object();\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.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" - + " }\n" - + " throw id;\n" - + " }\n" - + "\n" - + "function clickAndCheck() {\n" - + " doc.button.onclick();\n" - + " return doc.title.innerHTML.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, "You want this window to be named something", "We expect that the JavaCode performs all the wiring"); - } - - @Test public void clickWithArgumentCalled() throws Exception { - StringBuilder sb = new StringBuilder(); - sb.append( - "var window = new Object();\n" - + "var doc = new Object();\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.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" - + " }\n" - + " throw id;\n" - + " }\n" - + "\n" - + "function clickAndCheck() {\n" - + " doc.title.onclick();\n" - + " return doc.title.innerHTML.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, "pg.title", "Title has been passed to the method argument"); - } - - static Invocable compileClass(StringBuilder sb, String... names) throws ScriptException, IOException { - if (sb == null) { - sb = new StringBuilder(); - } - try { - Method m; - Class genJS = Class.forName("org.apidesign.vm4brwsr.GenJS"); - m = genJS.getDeclaredMethod("compile", Appendable.class, String[].class); - m.setAccessible(true); - m.invoke(null, sb, names); - } catch (Exception exception) { - throw new IOException(exception); - } - ScriptEngineManager sem = new ScriptEngineManager(); - ScriptEngine js = sem.getEngineByExtension("js"); - try { - Object res = js.eval(sb.toString()); - assertTrue(js instanceof Invocable, "It is invocable object: " + res); - return (Invocable) js; - } catch (ScriptException ex) { - fail("Could not compile:\n" + sb, ex); - return null; - } - } -} diff -r 070fc5844295 -r 590958fcb7d7 htmlpage/src/test/resources/org/apidesign/bck2brwsr/htmlpage/TestPage.html --- a/htmlpage/src/test/resources/org/apidesign/bck2brwsr/htmlpage/TestPage.html Fri Nov 09 09:00:46 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ - - - - - - Default Title - - - New title: - - - diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/pom.xml Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,55 @@ + + + 4.0.0 + + org.apidesign.bck2brwsr + javaquery + 1.0-SNAPSHOT + + org.apidesign.bck2brwsr + javaquery.api + 1.0-SNAPSHOT + JavaQuery API + http://maven.apache.org + + UTF-8 + + + + org.testng + testng + test + + + junit + junit + + + + + org.netbeans.api + org-openide-util-lookup + + + org.apidesign.bck2brwsr + core + 1.0-SNAPSHOT + jar + + + org.apidesign.bck2brwsr + emul + 1.0-SNAPSHOT + jar + runtime + + + org.apidesign.bck2brwsr + vm4brwsr + 0.1-SNAPSHOT + jar + test + + + diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,233 @@ +/** + * 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 java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Completion; +import javax.annotation.processing.Completions; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic; +import javax.tools.FileObject; +import javax.tools.StandardLocation; +import org.apidesign.bck2brwsr.htmlpage.api.OnClick; +import org.apidesign.bck2brwsr.htmlpage.api.Page; +import org.openide.util.lookup.ServiceProvider; + +/** Annotation processor to process an XHTML page and generate appropriate + * "id" file. + * + * @author Jaroslav Tulach + */ +@ServiceProvider(service=Processor.class) +@SupportedAnnotationTypes({ + "org.apidesign.bck2brwsr.htmlpage.api.Page", + "org.apidesign.bck2brwsr.htmlpage.api.OnClick" +}) +public final class PageProcessor extends AbstractProcessor { + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) { + Page p = e.getAnnotation(Page.class); + PackageElement pe = (PackageElement)e.getEnclosingElement(); + String pkg = pe.getQualifiedName().toString(); + + ProcessPage pp; + try { + InputStream is = openStream(pkg, p.xhtml()); + pp = ProcessPage.readPage(is); + is.close(); + } catch (IOException iOException) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e); + return false; + } + Writer w; + String className = p.className(); + if (className.isEmpty()) { + int indx = p.xhtml().indexOf('.'); + className = p.xhtml().substring(0, indx); + } + try { + FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e); + w = new OutputStreamWriter(java.openOutputStream()); + try { + w.append("package " + pkg + ";\n"); + w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); + w.append("class ").append(className).append(" {\n"); + for (String id : pp.ids()) { + String tag = pp.tagNameForId(id); + String type = type(tag); + w.append(" ").append("public static final "). + append(type).append(' ').append(cnstnt(id)).append(" = new "). + append(type).append("(\"").append(id).append("\");\n"); + } + w.append(" static {\n"); + if (!initializeOnClick(pe, w, pp)) { + return false; + } + w.append(" }\n"); + w.append("}\n"); + } finally { + w.close(); + } + } catch (IOException ex) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e); + return false; + } + } + return true; + } + + private InputStream openStream(String pkg, String name) throws IOException { + try { + FileObject fo = processingEnv.getFiler().getResource( + StandardLocation.SOURCE_PATH, pkg, name); + return fo.openInputStream(); + } catch (IOException ex) { + return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, pkg, name).openInputStream(); + } + } + + private static String type(String tag) { + if (tag.equals("title")) { + return "Title"; + } + if (tag.equals("button")) { + return "Button"; + } + if (tag.equals("input")) { + return "Input"; + } + return "Element"; + } + + private static String cnstnt(String id) { + return id.toUpperCase(Locale.ENGLISH).replace('.', '_'); + } + + private boolean initializeOnClick(PackageElement pe, Writer w, ProcessPage pp) throws IOException { + TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType(); + for (Element clazz : pe.getEnclosedElements()) { + if (clazz.getKind() != ElementKind.CLASS) { + continue; + } + TypeElement type = (TypeElement)clazz; + for (Element method : clazz.getEnclosedElements()) { + OnClick oc = method.getAnnotation(OnClick.class); + if (oc != null) { + for (String id : oc.id()) { + if (pp.tagNameForId(id) == null) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + oc.id() + " does not exist in the HTML page. Found only " + pp.ids(), method); + return false; + } + ExecutableElement ee = (ExecutableElement)method; + boolean hasParam; + if (ee.getParameters().isEmpty()) { + hasParam = false; + } else { + if (ee.getParameters().size() != 1 || ee.getParameters().get(0).asType() != stringType) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClick method should either have no arguments or one String argument", ee); + return false; + } + hasParam = true; + } + if (!ee.getModifiers().contains(Modifier.STATIC)) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClick method has to be static", ee); + return false; + } + if (ee.getModifiers().contains(Modifier.PRIVATE)) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClick method can't be private", ee); + return false; + } + w.append(" ").append(cnstnt(id)). + append(".addOnClick(new Runnable() { public void run() {\n"); + w.append(" ").append(type.getSimpleName().toString()). + append('.').append(ee.getSimpleName()).append("("); + if (hasParam) { + w.append("\"").append(id).append("\""); + } + w.append(");\n"); + w.append(" }});\n"); + } + } + } + } + return true; + } + + @Override + public Iterable getCompletions( + Element element, AnnotationMirror annotation, + ExecutableElement member, String userText + ) { + if (!userText.startsWith("\"")) { + return Collections.emptyList(); + } + + Element cls = findClass(element); + Page p = cls.getAnnotation(Page.class); + PackageElement pe = (PackageElement) cls.getEnclosingElement(); + String pkg = pe.getQualifiedName().toString(); + ProcessPage pp; + try { + InputStream is = openStream(pkg, p.xhtml()); + pp = ProcessPage.readPage(is); + is.close(); + } catch (IOException iOException) { + return Collections.emptyList(); + } + + List cc = new ArrayList(); + userText = userText.substring(1); + for (String id : pp.ids()) { + if (id.startsWith(userText)) { + cc.add(Completions.of("\"" + id + "\"", id)); + } + } + return cc; + } + + private static Element findClass(Element e) { + if (e == null) { + return null; + } + Page p = e.getAnnotation(Page.class); + if (p != null) { + return e; + } + return e.getEnclosingElement(); + } +} diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ProcessPage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ProcessPage.java Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,78 @@ +/** + * 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 java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +class ProcessPage { + private final Map ids2Elems = new TreeMap(); + + public Set ids() { + return Collections.unmodifiableSet(ids2Elems.keySet()); + } + + public String tagNameForId(String id) { + return ids2Elems.get(id); + } + + public static ProcessPage readPage(InputStream is) throws IOException { + DocumentBuilderFactory f = DocumentBuilderFactory.newInstance(); + f.setValidating(false); + f.setIgnoringComments(true); + + Document doc = null; + try { + DocumentBuilder b = f.newDocumentBuilder(); + doc = b.parse(is); + } catch (IOException ex) { + throw ex; + } catch (Exception e) { + throw new IOException(e); + } + Element root = doc.getDocumentElement(); + + ProcessPage pp = new ProcessPage(); + pp.seekForIds(root); + return pp; + } + + private void seekForIds(Element e) { + String val = e.getAttribute("id"); + if (val != null && !val.isEmpty()) { + String prev = ids2Elems.put(val, e.getTagName()); + } + NodeList arr = e.getChildNodes(); + for (int i = 0; i < arr.getLength(); i++) { + final Node n = arr.item(i); + if (n instanceof Element) { + seekForIds((Element)n); + } + } + } +} diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Button.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Button.java Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,36 @@ +/** + * 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; + +/** + * + * @author Jaroslav Tulach + */ +public final class Button extends Element { + public Button(String id) { + super(id); + } + + @Override + void dontSubclass() { + } + + public void setDisabled(boolean state) { + setAttribute(this, "disabled", state); + } +} diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,65 @@ +/** + * 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; + +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** Represents a generic HTML element. + * + * @author Jaroslav Tulach + */ +public abstract class Element { + private final String id; + + public Element(String id) { + this.id = id; + } + + abstract void dontSubclass(); + + @JavaScriptBody( + args={"el", "property", "value"}, + body="var e = window.document.getElementById(el.fld_id);\n" + + "e[property] = value;\n" + ) + static void setAttribute(Element el, String property, Object value) { + throw new UnsupportedOperationException("Needs JavaScript!"); + } + + @JavaScriptBody( + args={"el", "property"}, + body="var e = window.document.getElementById(el.fld_id);\n" + + "return e[property];\n" + ) + static Object getAttribute(Element el, String property) { + throw new UnsupportedOperationException("Needs JavaScript!"); + } + + /** Executes given runnable when user performs a "click" on the given + * element. + * @param r the runnable to execute, never null + */ + @JavaScriptBody( + args={"el", "r"}, + body="var e = window.document.getElementById(el.fld_id);\n" + + "e.onclick = function() { r.runV(); };\n" + ) + public final void addOnClick(Runnable r) { + throw new UnsupportedOperationException("Needs JavaScript!"); + } +} diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Input.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Input.java Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,44 @@ +/** + * 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; + +/** + * + * @author Jaroslav Tulach + */ +public final class Input extends Element { + public Input(String id) { + super(id); + } + + @Override + void dontSubclass() { + } + + public void setAutocomplete(boolean state) { + setAttribute(this, "autocomplete", state); + } + + public final String getValue() { + return (String)getAttribute(this, "value"); + } + + public final void setValue(String txt) { + setAttribute(this, "value", txt); + } +} diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnClick.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnClick.java Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,34 @@ +/** + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Adds an onClick handler to an element identified by given id. + * Apply on a public static void method with no arguments. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +public @interface OnClick { + String[] id(); +} diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Page.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Page.java Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,39 @@ +/** + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Converts an XHTML page into a Java class that contains + * references to all IDs. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface Page { + /** Path to the XHTML page to read in */ + String xhtml(); + /** Name of a Java class to generate. It will contain constants for all + * found elements with IDs. + */ + String className() default ""; +} diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Title.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Title.java Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,36 @@ +/** + * 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; + +/** + * + * @author Jaroslav Tulach + */ +public class Title extends Element { + public Title(String id) { + super(id); + } + + @Override + void dontSubclass() { + } + + public final void setText(String text) { + setAttribute(this, "innerHTML", text); + } +} diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,57 @@ +/** + * 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.htmlpage.api.OnClick; +import org.apidesign.bck2brwsr.htmlpage.api.Page; + +/** Trivial demo for the bck2brwsr project. First of all start + * with your XHTML page. Include there + * a script that will boot Java in your browser. + *

+ * Then use @Page annotation to + * generate a Java representation of elements with IDs in that page. + * Depending on the type of the elements, they will have different + * methods (e.g. PG_TITLE has setText, etc.). + * Use @OnClick annotation to associate behavior + * with existing elements. Use the generated elements + * (PG_TITLE, PG_TEXT) to modify the page. + *

+ * Everything is type-safe. As soon as somebody modifies the page and + * removes the IDs or re-assigns them to wrong elements. Java compiler + * will emit an error. + *

+ * Welcome to the type-safe HTML5 world! + * + * @author Jaroslav Tulach + */ +@Page(xhtml="TestPage.html") +public class PageController { + @OnClick(id="pg.button") + static void updateTitle() { + TestPage.PG_TITLE.setText("You want this window to be named " + TestPage.PG_TEXT.getValue()); + } + + @OnClick(id={ "pg.title", "pg.text" }) + static void click(String id) { + if (!id.equals("pg.title")) { + throw new IllegalStateException(); + } + TestPage.PG_TITLE.setText(id); + } +} diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,148 @@ +/** + * 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 java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.Set; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +public class ProcessPageTest { + + + @Test public void findsThreeIds() throws IOException { + InputStream is = ProcessPageTest.class.getResourceAsStream("TestPage.html"); + 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(res.tagNameForId("pg.title"), "title"); + assertEquals(res.tagNameForId("pg.button"), "button"); + assertEquals(res.tagNameForId("pg.text"), "input"); + } + + @Test public void testCompileAndRunPageController() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append( + "var window = new Object();\n" + + "var doc = new Object();\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.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" + + " }\n" + + " throw id;\n" + + " }\n" + + "\n" + + "function clickAndCheck() {\n" + + " doc.button.onclick();\n" + + " return doc.title.innerHTML.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, "You want this window to be named something", "We expect that the JavaCode performs all the wiring"); + } + + @Test public void clickWithArgumentCalled() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append( + "var window = new Object();\n" + + "var doc = new Object();\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.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" + + " }\n" + + " throw id;\n" + + " }\n" + + "\n" + + "function clickAndCheck() {\n" + + " doc.title.onclick();\n" + + " return doc.title.innerHTML.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, "pg.title", "Title has been passed to the method argument"); + } + + static Invocable compileClass(StringBuilder sb, String... names) throws ScriptException, IOException { + if (sb == null) { + sb = new StringBuilder(); + } + try { + Method m; + Class genJS = Class.forName("org.apidesign.vm4brwsr.GenJS"); + m = genJS.getDeclaredMethod("compile", Appendable.class, String[].class); + m.setAccessible(true); + m.invoke(null, sb, names); + } catch (Exception exception) { + throw new IOException(exception); + } + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine js = sem.getEngineByExtension("js"); + try { + Object res = js.eval(sb.toString()); + assertTrue(js instanceof Invocable, "It is invocable object: " + res); + return (Invocable) js; + } catch (ScriptException ex) { + fail("Could not compile:\n" + sb, ex); + return null; + } + } +} diff -r 070fc5844295 -r 590958fcb7d7 javaquery/api/src/test/resources/org/apidesign/bck2brwsr/htmlpage/TestPage.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/test/resources/org/apidesign/bck2brwsr/htmlpage/TestPage.html Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,30 @@ + + + + + + Default Title + + + New title: + + + diff -r 070fc5844295 -r 590958fcb7d7 javaquery/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/pom.xml Fri Nov 09 11:47:00 2012 +0100 @@ -0,0 +1,17 @@ + + + 4.0.0 + + bck2brwsr + org.apidesign + 1.0-SNAPSHOT + + org.apidesign.bck2brwsr + javaquery + 1.0-SNAPSHOT + pom + JavaQuery API and Demo + + api + + diff -r 070fc5844295 -r 590958fcb7d7 pom.xml --- a/pom.xml Fri Nov 09 09:00:46 2012 +0100 +++ b/pom.xml Fri Nov 09 11:47:00 2012 +0100 @@ -7,11 +7,11 @@ pom Back 2 Browser - vm - htmlpage + vm emul core mojo + javaquery