jaroslav@26: /** jaroslav@106: * Back 2 Browser Bytecode Translator jaroslav@106: * Copyright (C) 2012 Jaroslav Tulach jaroslav@26: * jaroslav@26: * This program is free software: you can redistribute it and/or modify jaroslav@26: * it under the terms of the GNU General Public License as published by jaroslav@26: * the Free Software Foundation, version 2 of the License. jaroslav@26: * jaroslav@26: * This program is distributed in the hope that it will be useful, jaroslav@26: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@26: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@26: * GNU General Public License for more details. jaroslav@26: * jaroslav@26: * You should have received a copy of the GNU General Public License jaroslav@26: * along with this program. Look for COPYING file in the top folder. jaroslav@26: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@26: */ jaroslav@26: package org.apidesign.bck2brwsr.htmlpage; jaroslav@26: jaroslav@26: import java.io.IOException; jaroslav@26: import java.io.InputStream; jaroslav@26: import java.io.OutputStreamWriter; jaroslav@26: import java.io.Writer; jaroslav@26: import java.util.Locale; jaroslav@26: import java.util.Set; jaroslav@26: import javax.annotation.processing.AbstractProcessor; jaroslav@26: import javax.annotation.processing.Processor; jaroslav@26: import javax.annotation.processing.RoundEnvironment; jaroslav@26: import javax.annotation.processing.SupportedAnnotationTypes; jaroslav@26: import javax.lang.model.element.Element; jaroslav@28: import javax.lang.model.element.ElementKind; jaroslav@28: import javax.lang.model.element.ExecutableElement; jaroslav@28: import javax.lang.model.element.Modifier; jaroslav@26: import javax.lang.model.element.PackageElement; jaroslav@26: import javax.lang.model.element.TypeElement; jaroslav@26: import javax.tools.Diagnostic; jaroslav@26: import javax.tools.FileObject; jaroslav@26: import javax.tools.StandardLocation; jaroslav@28: import org.apidesign.bck2brwsr.htmlpage.api.OnClick; jaroslav@26: import org.apidesign.bck2brwsr.htmlpage.api.Page; jaroslav@26: import org.openide.util.lookup.ServiceProvider; jaroslav@26: jaroslav@26: /** Annotation processor to process an XHTML page and generate appropriate jaroslav@26: * "id" file. jaroslav@26: * jaroslav@26: * @author Jaroslav Tulach jaroslav@26: */ jaroslav@26: @ServiceProvider(service=Processor.class) jaroslav@28: @SupportedAnnotationTypes({ jaroslav@28: "org.apidesign.bck2brwsr.htmlpage.api.Page", jaroslav@28: "org.apidesign.bck2brwsr.htmlpage.api.OnClick" jaroslav@28: }) jaroslav@26: public final class PageProcessor extends AbstractProcessor { jaroslav@26: @Override jaroslav@26: public boolean process(Set annotations, RoundEnvironment roundEnv) { jaroslav@26: for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) { jaroslav@26: Page p = e.getAnnotation(Page.class); jaroslav@100: PackageElement pe = (PackageElement)e.getEnclosingElement(); jaroslav@26: String pkg = pe.getQualifiedName().toString(); jaroslav@26: jaroslav@26: ProcessPage pp; jaroslav@26: try { jaroslav@26: InputStream is = openStream(pkg, p.xhtml()); jaroslav@26: pp = ProcessPage.readPage(is); jaroslav@26: is.close(); jaroslav@26: } catch (IOException iOException) { jaroslav@26: processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e); jaroslav@26: return false; jaroslav@26: } jaroslav@26: Writer w; jaroslav@26: try { jaroslav@26: FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + p.name(), e); jaroslav@26: w = new OutputStreamWriter(java.openOutputStream()); jaroslav@28: try { jaroslav@28: w.append("package " + pkg + ";\n"); jaroslav@28: w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); jaroslav@28: w.append("class ").append(p.name()).append(" {\n"); jaroslav@28: for (String id : pp.ids()) { jaroslav@28: String tag = pp.tagNameForId(id); jaroslav@28: String type = type(tag); jaroslav@28: w.append(" ").append("public static final "). jaroslav@28: append(type).append(' ').append(cnstnt(id)).append(" = new "). jaroslav@28: append(type).append("(\"").append(id).append("\");\n"); jaroslav@28: } jaroslav@28: w.append(" static {\n"); jaroslav@28: if (!initializeOnClick(pe, w, pp)) { jaroslav@28: return false; jaroslav@28: } jaroslav@28: w.append(" }\n"); jaroslav@28: w.append("}\n"); jaroslav@28: } finally { jaroslav@28: w.close(); jaroslav@26: } jaroslav@26: } catch (IOException ex) { jaroslav@26: processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + p.name() + ".java", e); jaroslav@26: return false; jaroslav@26: } jaroslav@26: } jaroslav@26: return true; jaroslav@26: } jaroslav@26: jaroslav@26: private InputStream openStream(String pkg, String name) throws IOException { jaroslav@26: FileObject fo = processingEnv.getFiler().getResource( jaroslav@26: StandardLocation.SOURCE_PATH, pkg, name); jaroslav@26: try { jaroslav@26: return fo.openInputStream(); jaroslav@26: } catch (IOException ex) { jaroslav@26: return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, pkg, name).openInputStream(); jaroslav@26: } jaroslav@26: } jaroslav@26: jaroslav@26: private static String type(String tag) { jaroslav@26: if (tag.equals("title")) { jaroslav@26: return "Title"; jaroslav@26: } jaroslav@26: if (tag.equals("button")) { jaroslav@26: return "Button"; jaroslav@26: } jaroslav@26: if (tag.equals("input")) { jaroslav@26: return "Input"; jaroslav@26: } jaroslav@26: return "Element"; jaroslav@26: } jaroslav@26: jaroslav@26: private static String cnstnt(String id) { jaroslav@26: return id.toUpperCase(Locale.ENGLISH).replace('.', '_'); jaroslav@26: } jaroslav@28: jaroslav@28: private boolean initializeOnClick(PackageElement pe, Writer w, ProcessPage pp) throws IOException { jaroslav@28: for (Element clazz : pe.getEnclosedElements()) { jaroslav@28: if (clazz.getKind() != ElementKind.CLASS) { jaroslav@28: continue; jaroslav@28: } jaroslav@28: TypeElement type = (TypeElement)clazz; jaroslav@28: for (Element method : clazz.getEnclosedElements()) { jaroslav@28: OnClick oc = method.getAnnotation(OnClick.class); jaroslav@28: if (oc != null) { jaroslav@28: if (pp.tagNameForId(oc.id()) == null) { jaroslav@28: processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + oc.id() + " does not exist in the HTML page. Found only " + pp.ids(), method); jaroslav@28: return false; jaroslav@28: } jaroslav@28: ExecutableElement ee = (ExecutableElement)method; jaroslav@28: if (!ee.getParameters().isEmpty()) { jaroslav@28: processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method can't take arguments", ee); jaroslav@28: return false; jaroslav@28: } jaroslav@28: if (!ee.getModifiers().contains(Modifier.STATIC)) { jaroslav@28: processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method has to be static", ee); jaroslav@28: return false; jaroslav@28: } jaroslav@28: if (ee.getModifiers().contains(Modifier.PRIVATE)) { jaroslav@28: processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClose method can't be private", ee); jaroslav@28: return false; jaroslav@28: } jaroslav@28: w.append(" ").append(cnstnt(oc.id())). jaroslav@28: append(".addOnClick(new Runnable() { public void run() {\n"); jaroslav@28: w.append(" ").append(type.getSimpleName().toString()). jaroslav@28: append('.').append(ee.getSimpleName()).append("();\n"); jaroslav@28: w.append(" }});\n"); jaroslav@28: } jaroslav@28: } jaroslav@28: } jaroslav@28: return true; jaroslav@28: } jaroslav@26: }