Support for @OnClick behavior
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 25 Sep 2012 09:55:34 +0200
changeset 2881ad7a739fed
parent 27 2f19d1449fba
child 29 dcb98731b000
Support for @OnClick behavior
htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java
htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java
htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.js
htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnClick.java
htmlpage/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java
     1.1 --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java	Tue Sep 25 09:08:39 2012 +0200
     1.2 +++ b/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java	Tue Sep 25 09:55:34 2012 +0200
     1.3 @@ -28,11 +28,15 @@
     1.4  import javax.annotation.processing.RoundEnvironment;
     1.5  import javax.annotation.processing.SupportedAnnotationTypes;
     1.6  import javax.lang.model.element.Element;
     1.7 +import javax.lang.model.element.ElementKind;
     1.8 +import javax.lang.model.element.ExecutableElement;
     1.9 +import javax.lang.model.element.Modifier;
    1.10  import javax.lang.model.element.PackageElement;
    1.11  import javax.lang.model.element.TypeElement;
    1.12  import javax.tools.Diagnostic;
    1.13  import javax.tools.FileObject;
    1.14  import javax.tools.StandardLocation;
    1.15 +import org.apidesign.bck2brwsr.htmlpage.api.OnClick;
    1.16  import org.apidesign.bck2brwsr.htmlpage.api.Page;
    1.17  import org.openide.util.lookup.ServiceProvider;
    1.18  
    1.19 @@ -42,7 +46,10 @@
    1.20   * @author Jaroslav Tulach <jtulach@netbeans.org>
    1.21   */
    1.22  @ServiceProvider(service=Processor.class)
    1.23 -@SupportedAnnotationTypes("org.apidesign.bck2brwsr.htmlpage.api.Page")
    1.24 +@SupportedAnnotationTypes({
    1.25 +    "org.apidesign.bck2brwsr.htmlpage.api.Page",
    1.26 +    "org.apidesign.bck2brwsr.htmlpage.api.OnClick"
    1.27 +})
    1.28  public final class PageProcessor extends AbstractProcessor {
    1.29      @Override
    1.30      public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    1.31 @@ -64,18 +71,26 @@
    1.32              try {
    1.33                  FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + p.name(), e);
    1.34                  w = new OutputStreamWriter(java.openOutputStream());
    1.35 -                w.append("package " + pkg + ";\n");
    1.36 -                w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
    1.37 -                w.append("class ").append(p.name()).append(" {\n");
    1.38 -                for (String id : pp.ids()) {
    1.39 -                    String tag = pp.tagNameForId(id);
    1.40 -                    String type = type(tag);
    1.41 -                    w.append("  ").append("public static final ").
    1.42 -                        append(type).append(' ').append(cnstnt(id)).append(" = new ").
    1.43 -                        append(type).append("(\"").append(id).append("\");\n");
    1.44 +                try {
    1.45 +                    w.append("package " + pkg + ";\n");
    1.46 +                    w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
    1.47 +                    w.append("class ").append(p.name()).append(" {\n");
    1.48 +                    for (String id : pp.ids()) {
    1.49 +                        String tag = pp.tagNameForId(id);
    1.50 +                        String type = type(tag);
    1.51 +                        w.append("  ").append("public static final ").
    1.52 +                            append(type).append(' ').append(cnstnt(id)).append(" = new ").
    1.53 +                            append(type).append("(\"").append(id).append("\");\n");
    1.54 +                    }
    1.55 +                    w.append("  static {\n");
    1.56 +                    if (!initializeOnClick(pe, w, pp)) {
    1.57 +                        return false;
    1.58 +                    }
    1.59 +                    w.append("  }\n");
    1.60 +                    w.append("}\n");
    1.61 +                } finally {
    1.62 +                    w.close();
    1.63                  }
    1.64 -                w.append("}");
    1.65 -                w.close();
    1.66              } catch (IOException ex) {
    1.67                  processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + p.name() + ".java", e);
    1.68                  return false;
    1.69 @@ -110,4 +125,41 @@
    1.70      private static String cnstnt(String id) {
    1.71          return id.toUpperCase(Locale.ENGLISH).replace('.', '_');
    1.72      }
    1.73 +
    1.74 +    private boolean initializeOnClick(PackageElement pe, Writer w, ProcessPage pp) throws IOException {
    1.75 +        for (Element clazz : pe.getEnclosedElements()) {
    1.76 +            if (clazz.getKind() != ElementKind.CLASS) {
    1.77 +                continue;
    1.78 +            }
    1.79 +            TypeElement type = (TypeElement)clazz;
    1.80 +            for (Element method : clazz.getEnclosedElements()) {
    1.81 +                OnClick oc = method.getAnnotation(OnClick.class);
    1.82 +                if (oc != null) {
    1.83 +                    if (pp.tagNameForId(oc.id()) == null) {
    1.84 +                        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + oc.id() + " does not exist in the HTML page. Found only " + pp.ids(), method);
    1.85 +                        return false;
    1.86 +                    }
    1.87 +                    ExecutableElement ee = (ExecutableElement)method;
    1.88 +                    if (!ee.getParameters().isEmpty()) {
    1.89 +                        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method can't take arguments", ee);
    1.90 +                        return false;
    1.91 +                    }
    1.92 +                    if (!ee.getModifiers().contains(Modifier.STATIC)) {
    1.93 +                        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method has to be static", ee);
    1.94 +                        return false;
    1.95 +                    }
    1.96 +                    if (ee.getModifiers().contains(Modifier.PRIVATE)) {
    1.97 +                        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method can't be private", ee);
    1.98 +                        return false;
    1.99 +                    }
   1.100 +                    w.append("  ").append(cnstnt(oc.id())).
   1.101 +                        append(".addOnClick(new Runnable() { public void run() {\n");
   1.102 +                    w.append("    ").append(type.getSimpleName().toString()).
   1.103 +                        append('.').append(ee.getSimpleName()).append("();\n");
   1.104 +                    w.append("  }});\n");
   1.105 +                }
   1.106 +            }
   1.107 +        }
   1.108 +        return true;
   1.109 +    }
   1.110  }
     2.1 --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java	Tue Sep 25 09:08:39 2012 +0200
     2.2 +++ b/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java	Tue Sep 25 09:55:34 2012 +0200
     2.3 @@ -33,4 +33,12 @@
     2.4      static void setAttribute(Element el, String property, Object value) {
     2.5          throw new UnsupportedOperationException("Needs JavaScript!");
     2.6      }
     2.7 +    
     2.8 +    /** Executes given runnable when user performs a "click" on the given
     2.9 +     * element.
    2.10 +     * @param r the runnable to execute, never null
    2.11 +     */
    2.12 +    public final void addOnClick(Runnable r) {
    2.13 +        throw new UnsupportedOperationException("Needs JavaScript!");
    2.14 +    }
    2.15  }
     3.1 --- a/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.js	Tue Sep 25 09:08:39 2012 +0200
     3.2 +++ b/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.js	Tue Sep 25 09:55:34 2012 +0200
     3.3 @@ -4,3 +4,7 @@
     3.4      document.getElementById(self.id)[property] = value;
     3.5  }
     3.6  
     3.7 +function org_apidesign_bck2brwsr_htmlpage_api_Element_addOnClick_Ljava_lang_Runnable(self, run) {
     3.8 +    document.getElementById(self.id).onClick = function() { run.runV(); };
     3.9 +}
    3.10 +
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnClick.java	Tue Sep 25 09:55:34 2012 +0200
     4.3 @@ -0,0 +1,21 @@
     4.4 +/*
     4.5 + * To change this template, choose Tools | Templates
     4.6 + * and open the template in the editor.
     4.7 + */
     4.8 +package org.apidesign.bck2brwsr.htmlpage.api;
     4.9 +
    4.10 +import java.lang.annotation.ElementType;
    4.11 +import java.lang.annotation.Retention;
    4.12 +import java.lang.annotation.RetentionPolicy;
    4.13 +import java.lang.annotation.Target;
    4.14 +
    4.15 +/** Adds an onClick handler to an element identified by given <em>id</em>.
    4.16 + * Apply on a <code>public static void</code> method with no arguments.
    4.17 + *
    4.18 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.19 + */
    4.20 +@Retention(RetentionPolicy.SOURCE)
    4.21 +@Target(ElementType.METHOD)
    4.22 +public @interface OnClick {
    4.23 +    String id();
    4.24 +}
     5.1 --- a/htmlpage/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java	Tue Sep 25 09:08:39 2012 +0200
     5.2 +++ b/htmlpage/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java	Tue Sep 25 09:55:34 2012 +0200
     5.3 @@ -43,4 +43,13 @@
     5.4      void testWhetherWeCanCallTheGeneratedIdFields() {
     5.5          Title t = TestPage.PG_TITLE;
     5.6      }
     5.7 +    
     5.8 +    @OnClick(id="pg.button")
     5.9 +    static void handleButtonClick() {
    5.10 +        
    5.11 +    }
    5.12 +    
    5.13 +    @Test public void testOnclickHandlerGenerated() {
    5.14 +        
    5.15 +    }
    5.16  }