# HG changeset patch # User Jaroslav Tulach # Date 1348559734 -7200 # Node ID 81ad7a739fed9657c30a8128153df8c8dbfc8c02 # Parent 2f19d1449fba929e950d538b89d099424c485f16 Support for @OnClick behavior diff -r 2f19d1449fba -r 81ad7a739fed htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue Sep 25 09:08:39 2012 +0200 +++ b/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue Sep 25 09:55:34 2012 +0200 @@ -28,11 +28,15 @@ import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; 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.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; @@ -42,7 +46,10 @@ * @author Jaroslav Tulach */ @ServiceProvider(service=Processor.class) -@SupportedAnnotationTypes("org.apidesign.bck2brwsr.htmlpage.api.Page") +@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) { @@ -64,18 +71,26 @@ try { FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + p.name(), e); w = new OutputStreamWriter(java.openOutputStream()); - w.append("package " + pkg + ";\n"); - w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); - w.append("class ").append(p.name()).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"); + try { + w.append("package " + pkg + ";\n"); + w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); + w.append("class ").append(p.name()).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(); } - w.append("}"); - w.close(); } catch (IOException ex) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + p.name() + ".java", e); return false; @@ -110,4 +125,41 @@ private static String cnstnt(String id) { return id.toUpperCase(Locale.ENGLISH).replace('.', '_'); } + + private boolean initializeOnClick(PackageElement pe, Writer w, ProcessPage pp) throws IOException { + 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) { + if (pp.tagNameForId(oc.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; + if (!ee.getParameters().isEmpty()) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method can't take arguments", ee); + return false; + } + if (!ee.getModifiers().contains(Modifier.STATIC)) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method has to be static", ee); + return false; + } + if (ee.getModifiers().contains(Modifier.PRIVATE)) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method can't be private", ee); + return false; + } + w.append(" ").append(cnstnt(oc.id())). + append(".addOnClick(new Runnable() { public void run() {\n"); + w.append(" ").append(type.getSimpleName().toString()). + append('.').append(ee.getSimpleName()).append("();\n"); + w.append(" }});\n"); + } + } + } + return true; + } } diff -r 2f19d1449fba -r 81ad7a739fed htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Tue Sep 25 09:08:39 2012 +0200 +++ b/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Tue Sep 25 09:55:34 2012 +0200 @@ -33,4 +33,12 @@ static void setAttribute(Element el, String property, Object value) { 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 + */ + public final void addOnClick(Runnable r) { + throw new UnsupportedOperationException("Needs JavaScript!"); + } } diff -r 2f19d1449fba -r 81ad7a739fed htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.js --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.js Tue Sep 25 09:08:39 2012 +0200 +++ b/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.js Tue Sep 25 09:55:34 2012 +0200 @@ -4,3 +4,7 @@ document.getElementById(self.id)[property] = value; } +function org_apidesign_bck2brwsr_htmlpage_api_Element_addOnClick_Ljava_lang_Runnable(self, run) { + document.getElementById(self.id).onClick = function() { run.runV(); }; +} + diff -r 2f19d1449fba -r 81ad7a739fed htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnClick.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnClick.java Tue Sep 25 09:55:34 2012 +0200 @@ -0,0 +1,21 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +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 2f19d1449fba -r 81ad7a739fed htmlpage/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java --- a/htmlpage/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Tue Sep 25 09:08:39 2012 +0200 +++ b/htmlpage/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Tue Sep 25 09:55:34 2012 +0200 @@ -43,4 +43,13 @@ void testWhetherWeCanCallTheGeneratedIdFields() { Title t = TestPage.PG_TITLE; } + + @OnClick(id="pg.button") + static void handleButtonClick() { + + } + + @Test public void testOnclickHandlerGenerated() { + + } }