1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Wed Feb 20 18:14:59 2013 +0100
1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Fri Feb 22 08:59:40 2013 +0100
1.3 @@ -20,7 +20,9 @@
1.4 import java.io.IOException;
1.5 import java.io.InputStream;
1.6 import java.io.OutputStreamWriter;
1.7 +import java.io.StringWriter;
1.8 import java.io.Writer;
1.9 +import java.lang.annotation.Annotation;
1.10 import java.util.ArrayList;
1.11 import java.util.Collection;
1.12 import java.util.Collections;
1.13 @@ -37,6 +39,7 @@
1.14 import javax.annotation.processing.RoundEnvironment;
1.15 import javax.annotation.processing.SupportedAnnotationTypes;
1.16 import javax.lang.model.element.AnnotationMirror;
1.17 +import javax.lang.model.element.AnnotationValue;
1.18 import javax.lang.model.element.Element;
1.19 import javax.lang.model.element.ElementKind;
1.20 import javax.lang.model.element.ExecutableElement;
1.21 @@ -51,6 +54,7 @@
1.22 import javax.tools.FileObject;
1.23 import javax.tools.StandardLocation;
1.24 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
1.25 +import org.apidesign.bck2brwsr.htmlpage.api.Model;
1.26 import org.apidesign.bck2brwsr.htmlpage.api.On;
1.27 import org.apidesign.bck2brwsr.htmlpage.api.Page;
1.28 import org.apidesign.bck2brwsr.htmlpage.api.Property;
1.29 @@ -63,6 +67,7 @@
1.30 */
1.31 @ServiceProvider(service=Processor.class)
1.32 @SupportedAnnotationTypes({
1.33 + "org.apidesign.bck2brwsr.htmlpage.api.Model",
1.34 "org.apidesign.bck2brwsr.htmlpage.api.Page",
1.35 "org.apidesign.bck2brwsr.htmlpage.api.On"
1.36 })
1.37 @@ -70,85 +75,14 @@
1.38 @Override
1.39 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1.40 boolean ok = true;
1.41 + for (Element e : roundEnv.getElementsAnnotatedWith(Model.class)) {
1.42 + if (!processModel(e)) {
1.43 + ok = false;
1.44 + }
1.45 + }
1.46 for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) {
1.47 - Page p = e.getAnnotation(Page.class);
1.48 - if (p == null) {
1.49 - continue;
1.50 - }
1.51 - PackageElement pe = (PackageElement)e.getEnclosingElement();
1.52 - String pkg = pe.getQualifiedName().toString();
1.53 -
1.54 - ProcessPage pp;
1.55 - try {
1.56 - InputStream is = openStream(pkg, p.xhtml());
1.57 - pp = ProcessPage.readPage(is);
1.58 - is.close();
1.59 - } catch (IOException iOException) {
1.60 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e);
1.61 + if (!processPage(e)) {
1.62 ok = false;
1.63 - pp = null;
1.64 - }
1.65 - Writer w;
1.66 - String className = p.className();
1.67 - if (className.isEmpty()) {
1.68 - int indx = p.xhtml().indexOf('.');
1.69 - className = p.xhtml().substring(0, indx);
1.70 - }
1.71 - try {
1.72 - FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
1.73 - w = new OutputStreamWriter(java.openOutputStream());
1.74 - try {
1.75 - w.append("package " + pkg + ";\n");
1.76 - w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
1.77 - w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
1.78 - w.append("final class ").append(className).append(" {\n");
1.79 - w.append(" private boolean locked;\n");
1.80 - if (!initializeOnClick(className, (TypeElement) e, w, pp)) {
1.81 - ok = false;
1.82 - } else {
1.83 - for (String id : pp.ids()) {
1.84 - String tag = pp.tagNameForId(id);
1.85 - String type = type(tag);
1.86 - w.append(" ").append("public final ").
1.87 - append(type).append(' ').append(cnstnt(id)).append(" = new ").
1.88 - append(type).append("(\"").append(id).append("\");\n");
1.89 - }
1.90 - }
1.91 - List<String> propsGetSet = new ArrayList<String>();
1.92 - Map<String,Collection<String>> propsDeps = new HashMap<String, Collection<String>>();
1.93 - if (!generateComputedProperties(w, p.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
1.94 - ok = false;
1.95 - }
1.96 - generateProperties(w, p.properties(), propsGetSet, propsDeps);
1.97 - w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
1.98 - if (!propsGetSet.isEmpty()) {
1.99 - w.write("public " + className + " applyBindings() {\n");
1.100 - w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(");
1.101 - w.write(className + ".class, this, ");
1.102 - w.write("new String[] {\n");
1.103 - String sep = "";
1.104 - for (String n : propsGetSet) {
1.105 - w.write(sep);
1.106 - if (n == null) {
1.107 - w.write(" null");
1.108 - } else {
1.109 - w.write(" \"" + n + "\"");
1.110 - }
1.111 - sep = ",\n";
1.112 - }
1.113 - w.write("\n });\n return this;\n}\n");
1.114 -
1.115 - w.write("public void triggerEvent(Element e, OnEvent ev) {\n");
1.116 - w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n");
1.117 - w.write("}\n");
1.118 - }
1.119 - w.append("}\n");
1.120 - } finally {
1.121 - w.close();
1.122 - }
1.123 - } catch (IOException ex) {
1.124 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
1.125 - return false;
1.126 }
1.127 }
1.128 return ok;
1.129 @@ -163,6 +97,134 @@
1.130 return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, pkg, name).openInputStream();
1.131 }
1.132 }
1.133 +
1.134 + private boolean processModel(Element e) {
1.135 + boolean ok = true;
1.136 + Model m = e.getAnnotation(Model.class);
1.137 + if (m == null) {
1.138 + return true;
1.139 + }
1.140 + String pkg = findPkgName(e);
1.141 + Writer w;
1.142 + String className = m.className();
1.143 + try {
1.144 + StringWriter body = new StringWriter();
1.145 + List<String> propsGetSet = new ArrayList<>();
1.146 + Map<String, Collection<String>> propsDeps = new HashMap<>();
1.147 + if (!generateComputedProperties(body, m.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
1.148 + ok = false;
1.149 + }
1.150 + if (!generateProperties(body, m.properties(), propsGetSet, propsDeps)) {
1.151 + ok = false;
1.152 + }
1.153 + FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
1.154 + w = new OutputStreamWriter(java.openOutputStream());
1.155 + try {
1.156 + w.append("package " + pkg + ";\n");
1.157 + w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
1.158 + w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
1.159 + w.append("final class ").append(className).append(" {\n");
1.160 + w.append(" private Object json;\n");
1.161 + w.append(" private boolean locked;\n");
1.162 + w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
1.163 + w.append(body.toString());
1.164 + w.append("}\n");
1.165 + } finally {
1.166 + w.close();
1.167 + }
1.168 + } catch (IOException ex) {
1.169 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
1.170 + return false;
1.171 + }
1.172 + return ok;
1.173 + }
1.174 +
1.175 + private boolean processPage(Element e) {
1.176 + boolean ok = true;
1.177 + Page p = e.getAnnotation(Page.class);
1.178 + if (p == null) {
1.179 + return true;
1.180 + }
1.181 + String pkg = findPkgName(e);
1.182 +
1.183 + ProcessPage pp;
1.184 + try (InputStream is = openStream(pkg, p.xhtml())) {
1.185 + pp = ProcessPage.readPage(is);
1.186 + is.close();
1.187 + } catch (IOException iOException) {
1.188 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml(), e);
1.189 + ok = false;
1.190 + pp = null;
1.191 + }
1.192 + Writer w;
1.193 + String className = p.className();
1.194 + if (className.isEmpty()) {
1.195 + int indx = p.xhtml().indexOf('.');
1.196 + className = p.xhtml().substring(0, indx);
1.197 + }
1.198 + try {
1.199 + StringWriter body = new StringWriter();
1.200 + List<String> propsGetSet = new ArrayList<>();
1.201 + Map<String, Collection<String>> propsDeps = new HashMap<>();
1.202 + if (!generateComputedProperties(body, p.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
1.203 + ok = false;
1.204 + }
1.205 + if (!generateProperties(body, p.properties(), propsGetSet, propsDeps)) {
1.206 + ok = false;
1.207 + }
1.208 +
1.209 + FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
1.210 + w = new OutputStreamWriter(java.openOutputStream());
1.211 + try {
1.212 + w.append("package " + pkg + ";\n");
1.213 + w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
1.214 + w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
1.215 + w.append("final class ").append(className).append(" {\n");
1.216 + w.append(" private boolean locked;\n");
1.217 + if (!initializeOnClick(className, (TypeElement) e, w, pp)) {
1.218 + ok = false;
1.219 + } else {
1.220 + for (String id : pp.ids()) {
1.221 + String tag = pp.tagNameForId(id);
1.222 + String type = type(tag);
1.223 + w.append(" ").append("public final ").
1.224 + append(type).append(' ').append(cnstnt(id)).append(" = new ").
1.225 + append(type).append("(\"").append(id).append("\");\n");
1.226 + }
1.227 + }
1.228 + w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
1.229 + w.append(body.toString());
1.230 + if (!propsGetSet.isEmpty()) {
1.231 + w.write("public " + className + " applyBindings() {\n");
1.232 + w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(");
1.233 + w.write(className + ".class, this, ");
1.234 + w.write("new String[] {\n");
1.235 + String sep = "";
1.236 + for (String n : propsGetSet) {
1.237 + w.write(sep);
1.238 + if (n == null) {
1.239 + w.write(" null");
1.240 + } else {
1.241 + w.write(" \"" + n + "\"");
1.242 + }
1.243 + sep = ",\n";
1.244 + }
1.245 + w.write("\n });\n return this;\n}\n");
1.246 +
1.247 + w.write("public void triggerEvent(Element e, OnEvent ev) {\n");
1.248 + w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n");
1.249 + w.write("}\n");
1.250 + }
1.251 + w.append("}\n");
1.252 + } finally {
1.253 + w.close();
1.254 + }
1.255 + } catch (IOException ex) {
1.256 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
1.257 + return false;
1.258 + }
1.259 + return ok;
1.260 + }
1.261
1.262 private static String type(String tag) {
1.263 if (tag.equals("title")) {
1.264 @@ -295,8 +357,7 @@
1.265
1.266 Element cls = findClass(element);
1.267 Page p = cls.getAnnotation(Page.class);
1.268 - PackageElement pe = (PackageElement) cls.getEnclosingElement();
1.269 - String pkg = pe.getQualifiedName().toString();
1.270 + String pkg = findPkgName(cls);
1.271 ProcessPage pp;
1.272 try {
1.273 InputStream is = openStream(pkg, p.xhtml());
1.274 @@ -306,7 +367,7 @@
1.275 return Collections.emptyList();
1.276 }
1.277
1.278 - List<Completion> cc = new ArrayList<Completion>();
1.279 + List<Completion> cc = new ArrayList<>();
1.280 userText = userText.substring(1);
1.281 for (String id : pp.ids()) {
1.282 if (id.startsWith(userText)) {
1.283 @@ -327,12 +388,14 @@
1.284 return e.getEnclosingElement();
1.285 }
1.286
1.287 - private void generateProperties(
1.288 - Writer w, Property[] properties, Collection<String> props,
1.289 - Map<String,Collection<String>> deps
1.290 + private boolean generateProperties(
1.291 + Writer w, Property[] properties,
1.292 + Collection<String> props, Map<String,Collection<String>> deps
1.293 ) throws IOException {
1.294 + boolean ok = true;
1.295 for (Property p : properties) {
1.296 - final String tn = typeName(p);
1.297 + final String tn;
1.298 + tn = typeName(p);
1.299 String[] gs = toGetSet(p.name(), tn, p.array());
1.300
1.301 if (p.array()) {
1.302 @@ -382,6 +445,7 @@
1.303 props.add(gs[3]);
1.304 props.add(gs[0]);
1.305 }
1.306 + return ok;
1.307 }
1.308
1.309 private boolean generateComputedProperties(
1.310 @@ -427,7 +491,7 @@
1.311
1.312 Collection<String> depends = deps.get(dn);
1.313 if (depends == null) {
1.314 - depends = new LinkedHashSet<String>();
1.315 + depends = new LinkedHashSet<>();
1.316 deps.put(dn, depends);
1.317 }
1.318 depends.add(sn);
1.319 @@ -494,11 +558,19 @@
1.320
1.321 private String typeName(Property p) {
1.322 String ret;
1.323 + boolean isModel = false;
1.324 try {
1.325 ret = p.type().getName();
1.326 } catch (MirroredTypeException ex) {
1.327 TypeMirror tm = processingEnv.getTypeUtils().erasure(ex.getTypeMirror());
1.328 - ret = tm.toString();
1.329 + final Element e = processingEnv.getTypeUtils().asElement(tm);
1.330 + final Model m = e == null ? null : e.getAnnotation(Model.class);
1.331 + if (m != null) {
1.332 + ret = findPkgName(e) + '.' + m.className();
1.333 + isModel = true;
1.334 + } else {
1.335 + ret = tm.toString();
1.336 + }
1.337 }
1.338 if (p.array()) {
1.339 String bt = findBoxedType(ret);
1.340 @@ -506,14 +578,12 @@
1.341 return bt;
1.342 }
1.343 }
1.344 - if ("java.lang.String".equals(ret)) {
1.345 - return ret;
1.346 + if (!isModel && !"java.lang.String".equals(ret)) {
1.347 + String bt = findBoxedType(ret);
1.348 + if (bt == null) {
1.349 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Only primitive types supported in the mapping. Not " + ret);
1.350 + }
1.351 }
1.352 - String bt = findBoxedType(ret);
1.353 - if (bt != null) {
1.354 - return ret;
1.355 - }
1.356 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Only primitive types supported in the mapping. Not " + ret);
1.357 return ret;
1.358 }
1.359
1.360 @@ -561,4 +631,13 @@
1.361 );
1.362 return false;
1.363 }
1.364 +
1.365 + private static String findPkgName(Element e) {
1.366 + for (;;) {
1.367 + if (e.getKind() == ElementKind.PACKAGE) {
1.368 + return ((PackageElement)e).getQualifiedName().toString();
1.369 + }
1.370 + e = e.getEnclosingElement();
1.371 + }
1.372 + }
1.373 }