htmlpage/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 16 Oct 2012 13:23:04 +0200
changeset 108 7e3f72897a83
parent 107 0195ef3415ba
child 117 02618d8bec44
permissions -rw-r--r--
Deduce name of the class to generate from the name of the HTML page
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. Look for COPYING file in the top folder.
    16  * If not, see http://opensource.org/licenses/GPL-2.0.
    17  */
    18 package org.apidesign.bck2brwsr.htmlpage;
    19 
    20 import java.io.IOException;
    21 import java.io.InputStream;
    22 import java.io.OutputStreamWriter;
    23 import java.io.Writer;
    24 import java.util.Locale;
    25 import java.util.Set;
    26 import javax.annotation.processing.AbstractProcessor;
    27 import javax.annotation.processing.Processor;
    28 import javax.annotation.processing.RoundEnvironment;
    29 import javax.annotation.processing.SupportedAnnotationTypes;
    30 import javax.lang.model.element.Element;
    31 import javax.lang.model.element.ElementKind;
    32 import javax.lang.model.element.ExecutableElement;
    33 import javax.lang.model.element.Modifier;
    34 import javax.lang.model.element.PackageElement;
    35 import javax.lang.model.element.TypeElement;
    36 import javax.tools.Diagnostic;
    37 import javax.tools.FileObject;
    38 import javax.tools.StandardLocation;
    39 import org.apidesign.bck2brwsr.htmlpage.api.OnClick;
    40 import org.apidesign.bck2brwsr.htmlpage.api.Page;
    41 import org.openide.util.lookup.ServiceProvider;
    42 
    43 /** Annotation processor to process an XHTML page and generate appropriate 
    44  * "id" file.
    45  *
    46  * @author Jaroslav Tulach <jtulach@netbeans.org>
    47  */
    48 @ServiceProvider(service=Processor.class)
    49 @SupportedAnnotationTypes({
    50     "org.apidesign.bck2brwsr.htmlpage.api.Page",
    51     "org.apidesign.bck2brwsr.htmlpage.api.OnClick"
    52 })
    53 public final class PageProcessor extends AbstractProcessor {
    54     @Override
    55     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    56         for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) {
    57             Page p = e.getAnnotation(Page.class);
    58             PackageElement pe = (PackageElement)e.getEnclosingElement();
    59             String pkg = pe.getQualifiedName().toString();
    60             
    61             ProcessPage pp;
    62             try {
    63                 InputStream is = openStream(pkg, p.xhtml());
    64                 pp = ProcessPage.readPage(is);
    65                 is.close();
    66             } catch (IOException iOException) {
    67                 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e);
    68                 return false;
    69             }
    70             Writer w;
    71             String className = p.className();
    72             if (className.isEmpty()) {
    73                 int indx = p.xhtml().indexOf('.');
    74                 className = p.xhtml().substring(0, indx);
    75             }
    76             try {
    77                 FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
    78                 w = new OutputStreamWriter(java.openOutputStream());
    79                 try {
    80                     w.append("package " + pkg + ";\n");
    81                     w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
    82                     w.append("class ").append(className).append(" {\n");
    83                     for (String id : pp.ids()) {
    84                         String tag = pp.tagNameForId(id);
    85                         String type = type(tag);
    86                         w.append("  ").append("public static final ").
    87                             append(type).append(' ').append(cnstnt(id)).append(" = new ").
    88                             append(type).append("(\"").append(id).append("\");\n");
    89                     }
    90                     w.append("  static {\n");
    91                     if (!initializeOnClick(pe, w, pp)) {
    92                         return false;
    93                     }
    94                     w.append("  }\n");
    95                     w.append("}\n");
    96                 } finally {
    97                     w.close();
    98                 }
    99             } catch (IOException ex) {
   100                 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
   101                 return false;
   102             }
   103         }
   104         return true;
   105     }
   106 
   107     private InputStream openStream(String pkg, String name) throws IOException {
   108         try {
   109             FileObject fo = processingEnv.getFiler().getResource(
   110                 StandardLocation.SOURCE_PATH, pkg, name);
   111             return fo.openInputStream();
   112         } catch (IOException ex) {
   113             return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, pkg, name).openInputStream();
   114         }
   115     }
   116 
   117     private static String type(String tag) {
   118         if (tag.equals("title")) {
   119             return "Title";
   120         }
   121         if (tag.equals("button")) {
   122             return "Button";
   123         }
   124         if (tag.equals("input")) {
   125             return "Input";
   126         }
   127         return "Element";
   128     }
   129 
   130     private static String cnstnt(String id) {
   131         return id.toUpperCase(Locale.ENGLISH).replace('.', '_');
   132     }
   133 
   134     private boolean initializeOnClick(PackageElement pe, Writer w, ProcessPage pp) throws IOException {
   135         for (Element clazz : pe.getEnclosedElements()) {
   136             if (clazz.getKind() != ElementKind.CLASS) {
   137                 continue;
   138             }
   139             TypeElement type = (TypeElement)clazz;
   140             for (Element method : clazz.getEnclosedElements()) {
   141                 OnClick oc = method.getAnnotation(OnClick.class);
   142                 if (oc != null) {
   143                     if (pp.tagNameForId(oc.id()) == null) {
   144                         processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + oc.id() + " does not exist in the HTML page. Found only " + pp.ids(), method);
   145                         return false;
   146                     }
   147                     ExecutableElement ee = (ExecutableElement)method;
   148                     if (!ee.getParameters().isEmpty()) {
   149                         processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method can't take arguments", ee);
   150                         return false;
   151                     }
   152                     if (!ee.getModifiers().contains(Modifier.STATIC)) {
   153                         processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method has to be static", ee);
   154                         return false;
   155                     }
   156                     if (ee.getModifiers().contains(Modifier.PRIVATE)) {
   157                         processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method can't be private", ee);
   158                         return false;
   159                     }
   160                     w.append("  ").append(cnstnt(oc.id())).
   161                         append(".addOnClick(new Runnable() { public void run() {\n");
   162                     w.append("    ").append(type.getSimpleName().toString()).
   163                         append('.').append(ee.getSimpleName()).append("();\n");
   164                     w.append("  }});\n");
   165                 }
   166             }
   167         }
   168         return true;
   169     }
   170 }