1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue Apr 09 10:06:19 2013 +0200
1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Wed Apr 10 09:55:26 2013 +0200
1.3 @@ -22,6 +22,8 @@
1.4 import java.io.OutputStreamWriter;
1.5 import java.io.StringWriter;
1.6 import java.io.Writer;
1.7 +import java.lang.annotation.AnnotationTypeMismatchException;
1.8 +import java.lang.reflect.Method;
1.9 import java.util.ArrayList;
1.10 import java.util.Collection;
1.11 import java.util.Collections;
1.12 @@ -35,11 +37,12 @@
1.13 import javax.annotation.processing.AbstractProcessor;
1.14 import javax.annotation.processing.Completion;
1.15 import javax.annotation.processing.Completions;
1.16 -import javax.annotation.processing.Messager;
1.17 +import javax.annotation.processing.ProcessingEnvironment;
1.18 import javax.annotation.processing.Processor;
1.19 import javax.annotation.processing.RoundEnvironment;
1.20 import javax.annotation.processing.SupportedAnnotationTypes;
1.21 import javax.lang.model.element.AnnotationMirror;
1.22 +import javax.lang.model.element.AnnotationValue;
1.23 import javax.lang.model.element.Element;
1.24 import javax.lang.model.element.ElementKind;
1.25 import javax.lang.model.element.ExecutableElement;
1.26 @@ -78,10 +81,12 @@
1.27 "org.apidesign.bck2brwsr.htmlpage.api.OnFunction",
1.28 "org.apidesign.bck2brwsr.htmlpage.api.OnReceive",
1.29 "org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange",
1.30 + "org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty",
1.31 "org.apidesign.bck2brwsr.htmlpage.api.On"
1.32 })
1.33 public final class PageProcessor extends AbstractProcessor {
1.34 private final Map<Element,String> models = new WeakHashMap<>();
1.35 + private final Map<Element,Prprt[]> verify = new WeakHashMap<>();
1.36 @Override
1.37 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1.38 boolean ok = true;
1.39 @@ -97,6 +102,42 @@
1.40 }
1.41 if (roundEnv.processingOver()) {
1.42 models.clear();
1.43 + for (Map.Entry<Element, Prprt[]> entry : verify.entrySet()) {
1.44 + TypeElement te = (TypeElement)entry.getKey();
1.45 + String fqn = processingEnv.getElementUtils().getBinaryName(te).toString();
1.46 + Element finalElem = processingEnv.getElementUtils().getTypeElement(fqn);
1.47 + if (finalElem == null) {
1.48 + continue;
1.49 + }
1.50 + Prprt[] props;
1.51 + Model m = finalElem.getAnnotation(Model.class);
1.52 + if (m != null) {
1.53 + props = Prprt.wrap(processingEnv, finalElem, m.properties());
1.54 + } else {
1.55 + Page p = finalElem.getAnnotation(Page.class);
1.56 + props = Prprt.wrap(processingEnv, finalElem, p.properties());
1.57 + }
1.58 + for (Prprt p : props) {
1.59 + boolean[] isModel = { false };
1.60 + boolean[] isEnum = { false };
1.61 + boolean[] isPrimitive = { false };
1.62 + String t = checkType(p, isModel, isEnum, isPrimitive);
1.63 + if (isEnum[0]) {
1.64 + continue;
1.65 + }
1.66 + if (isPrimitive[0]) {
1.67 + continue;
1.68 + }
1.69 + if (isModel[0]) {
1.70 + continue;
1.71 + }
1.72 + if ("java.lang.String".equals(t)) {
1.73 + continue;
1.74 + }
1.75 + error("The type " + t + " should be defined by @Model annotation", entry.getKey());
1.76 + }
1.77 + }
1.78 + verify.clear();
1.79 }
1.80 return ok;
1.81 }
1.82 @@ -111,8 +152,8 @@
1.83 }
1.84 }
1.85
1.86 - private Messager err() {
1.87 - return processingEnv.getMessager();
1.88 + private void error(String msg, Element e) {
1.89 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
1.90 }
1.91
1.92 private boolean processModel(Element e) {
1.93 @@ -131,13 +172,15 @@
1.94 List<String> functions = new ArrayList<>();
1.95 Map<String, Collection<String>> propsDeps = new HashMap<>();
1.96 Map<String, Collection<String>> functionDeps = new HashMap<>();
1.97 - if (!generateComputedProperties(body, m.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
1.98 + Prprt[] props = createProps(e, m.properties());
1.99 +
1.100 + if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
1.101 ok = false;
1.102 }
1.103 - if (!generateOnChange(e, propsDeps, m.properties(), className, functionDeps)) {
1.104 + if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
1.105 ok = false;
1.106 }
1.107 - if (!generateProperties(e, body, m.properties(), propsGetSet, propsDeps, functionDeps)) {
1.108 + if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) {
1.109 ok = false;
1.110 }
1.111 if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
1.112 @@ -168,7 +211,7 @@
1.113 w.append(" ").append(className).append("(Object json) {\n");
1.114 int values = 0;
1.115 for (int i = 0; i < propsGetSet.size(); i += 4) {
1.116 - Property p = findProperty(m.properties(), propsGetSet.get(i));
1.117 + Prprt p = findPrprt(props, propsGetSet.get(i));
1.118 if (p == null) {
1.119 continue;
1.120 }
1.121 @@ -177,7 +220,7 @@
1.122 w.append(" Object[] ret = new Object[" + values + "];\n");
1.123 w.append(" org.apidesign.bck2brwsr.htmlpage.ConvertTypes.extractJSON(json, new String[] {\n");
1.124 for (int i = 0; i < propsGetSet.size(); i += 4) {
1.125 - Property p = findProperty(m.properties(), propsGetSet.get(i));
1.126 + Prprt p = findPrprt(props, propsGetSet.get(i));
1.127 if (p == null) {
1.128 continue;
1.129 }
1.130 @@ -186,13 +229,14 @@
1.131 w.append(" }, ret);\n");
1.132 for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i += 4) {
1.133 final String pn = propsGetSet.get(i);
1.134 - Property p = findProperty(m.properties(), pn);
1.135 + Prprt p = findPrprt(props, pn);
1.136 if (p == null) {
1.137 continue;
1.138 }
1.139 boolean[] isModel = { false };
1.140 boolean[] isEnum = { false };
1.141 - String type = checkType(m.properties()[prop++], isModel, isEnum);
1.142 + boolean isPrimitive[] = { false };
1.143 + String type = checkType(props[prop++], isModel, isEnum, isPrimitive);
1.144 if (p.array()) {
1.145 w.append("if (ret[" + cnt + "] instanceof Object[]) {\n");
1.146 w.append(" for (Object e : ((Object[])ret[" + cnt + "])) {\n");
1.147 @@ -233,14 +277,14 @@
1.148 }
1.149 w.append(" intKnckt();\n");
1.150 w.append(" };\n");
1.151 - writeToString(m.properties(), w);
1.152 - writeClone(className, m.properties(), w);
1.153 + writeToString(props, w);
1.154 + writeClone(className, props, w);
1.155 w.append("}\n");
1.156 } finally {
1.157 w.close();
1.158 }
1.159 } catch (IOException ex) {
1.160 - err().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
1.161 + error("Can't create " + className + ".java", e);
1.162 return false;
1.163 }
1.164 return ok;
1.165 @@ -259,7 +303,7 @@
1.166 pp = ProcessPage.readPage(is);
1.167 is.close();
1.168 } catch (IOException iOException) {
1.169 - err().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml() + " as " + iOException.getMessage(), e);
1.170 + error("Can't read " + p.xhtml() + " as " + iOException.getMessage(), e);
1.171 ok = false;
1.172 pp = null;
1.173 }
1.174 @@ -275,13 +319,15 @@
1.175 List<String> functions = new ArrayList<>();
1.176 Map<String, Collection<String>> propsDeps = new HashMap<>();
1.177 Map<String, Collection<String>> functionDeps = new HashMap<>();
1.178 - if (!generateComputedProperties(body, p.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
1.179 +
1.180 + Prprt[] props = createProps(e, p.properties());
1.181 + if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
1.182 ok = false;
1.183 }
1.184 - if (!generateOnChange(e, propsDeps, p.properties(), className, functionDeps)) {
1.185 + if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
1.186 ok = false;
1.187 }
1.188 - if (!generateProperties(e, body, p.properties(), propsGetSet, propsDeps, functionDeps)) {
1.189 + if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) {
1.190 ok = false;
1.191 }
1.192 if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
1.193 @@ -330,7 +376,7 @@
1.194 w.close();
1.195 }
1.196 } catch (IOException ex) {
1.197 - err().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
1.198 + error("Can't create " + className + ".java", e);
1.199 return false;
1.200 }
1.201 return ok;
1.202 @@ -376,24 +422,24 @@
1.203 if (oc != null) {
1.204 for (String id : oc.id()) {
1.205 if (pp == null) {
1.206 - err().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " not found in HTML page.");
1.207 + error("id = " + id + " not found in HTML page.", method);
1.208 ok = false;
1.209 continue;
1.210 }
1.211 if (pp.tagNameForId(id) == null) {
1.212 - err().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
1.213 + error("id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
1.214 ok = false;
1.215 continue;
1.216 }
1.217 ExecutableElement ee = (ExecutableElement)method;
1.218 CharSequence params = wrapParams(ee, id, className, "ev", null);
1.219 if (!ee.getModifiers().contains(Modifier.STATIC)) {
1.220 - err().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee);
1.221 + error("@On method has to be static", ee);
1.222 ok = false;
1.223 continue;
1.224 }
1.225 if (ee.getModifiers().contains(Modifier.PRIVATE)) {
1.226 - err().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee);
1.227 + error("@On method can't be private", ee);
1.228 ok = false;
1.229 continue;
1.230 }
1.231 @@ -473,13 +519,13 @@
1.232
1.233 private boolean generateProperties(
1.234 Element where,
1.235 - Writer w, Property[] properties,
1.236 + Writer w, Prprt[] properties,
1.237 Collection<String> props,
1.238 Map<String,Collection<String>> deps,
1.239 Map<String,Collection<String>> functionDeps
1.240 ) throws IOException {
1.241 boolean ok = true;
1.242 - for (Property p : properties) {
1.243 + for (Prprt p : properties) {
1.244 final String tn;
1.245 tn = typeName(where, p);
1.246 String[] gs = toGetSet(p.name(), tn, p.array());
1.247 @@ -549,7 +595,7 @@
1.248 }
1.249
1.250 private boolean generateComputedProperties(
1.251 - Writer w, Property[] fixedProps,
1.252 + Writer w, Prprt[] fixedProps,
1.253 Collection<? extends Element> arr, Collection<String> props,
1.254 Map<String,Collection<String>> deps
1.255 ) throws IOException {
1.256 @@ -651,27 +697,18 @@
1.257 };
1.258 }
1.259
1.260 - private String typeName(Element where, Property p) {
1.261 + private String typeName(Element where, Prprt p) {
1.262 String ret;
1.263 boolean[] isModel = { false };
1.264 boolean[] isEnum = { false };
1.265 - ret = checkType(p, isModel, isEnum);
1.266 + boolean isPrimitive[] = { false };
1.267 + ret = checkType(p, isModel, isEnum, isPrimitive);
1.268 if (p.array()) {
1.269 String bt = findBoxedType(ret);
1.270 if (bt != null) {
1.271 return bt;
1.272 }
1.273 }
1.274 - if (!isModel[0] && !"java.lang.String".equals(ret) && !isEnum[0]) {
1.275 - String bt = findBoxedType(ret);
1.276 - if (bt == null) {
1.277 - err().printMessage(
1.278 - Diagnostic.Kind.ERROR,
1.279 - "Only primitive types supported in the mapping. Not " + ret,
1.280 - where
1.281 - );
1.282 - }
1.283 - }
1.284 return ret;
1.285 }
1.286
1.287 @@ -703,20 +740,20 @@
1.288 return null;
1.289 }
1.290
1.291 - private boolean verifyPropName(Element e, String propName, Property[] existingProps) {
1.292 + private boolean verifyPropName(Element e, String propName, Prprt[] existingProps) {
1.293 StringBuilder sb = new StringBuilder();
1.294 String sep = "";
1.295 - for (Property property : existingProps) {
1.296 - if (property.name().equals(propName)) {
1.297 + for (Prprt Prprt : existingProps) {
1.298 + if (Prprt.name().equals(propName)) {
1.299 return true;
1.300 }
1.301 sb.append(sep);
1.302 sb.append('"');
1.303 - sb.append(property.name());
1.304 + sb.append(Prprt.name());
1.305 sb.append('"');
1.306 sep = ", ";
1.307 }
1.308 - err().printMessage(Diagnostic.Kind.ERROR,
1.309 + error(
1.310 propName + " is not one of known properties: " + sb
1.311 , e
1.312 );
1.313 @@ -746,21 +783,15 @@
1.314 continue;
1.315 }
1.316 if (!e.getModifiers().contains(Modifier.STATIC)) {
1.317 - err().printMessage(
1.318 - Diagnostic.Kind.ERROR, "@OnFunction method needs to be static", e
1.319 - );
1.320 + error("@OnFunction method needs to be static", e);
1.321 return false;
1.322 }
1.323 if (e.getModifiers().contains(Modifier.PRIVATE)) {
1.324 - err().printMessage(
1.325 - Diagnostic.Kind.ERROR, "@OnFunction method cannot be private", e
1.326 - );
1.327 + error("@OnFunction method cannot be private", e);
1.328 return false;
1.329 }
1.330 if (e.getReturnType().getKind() != TypeKind.VOID) {
1.331 - err().printMessage(
1.332 - Diagnostic.Kind.ERROR, "@OnFunction method should return void", e
1.333 - );
1.334 + error("@OnFunction method should return void", e);
1.335 return false;
1.336 }
1.337 String n = e.getSimpleName().toString();
1.338 @@ -777,7 +808,7 @@
1.339 }
1.340
1.341 private boolean generateOnChange(Element clazz, Map<String,Collection<String>> propDeps,
1.342 - Property[] properties, String className,
1.343 + Prprt[] properties, String className,
1.344 Map<String, Collection<String>> functionDeps
1.345 ) {
1.346 for (Element m : clazz.getEnclosedElements()) {
1.347 @@ -790,24 +821,21 @@
1.348 continue;
1.349 }
1.350 for (String pn : onPC.value()) {
1.351 - if (findProperty(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
1.352 - err().printMessage(Diagnostic.Kind.ERROR, "No property named '" + pn + "' in the model");
1.353 + if (findPrprt(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
1.354 + error("No Prprt named '" + pn + "' in the model", clazz);
1.355 return false;
1.356 }
1.357 }
1.358 if (!e.getModifiers().contains(Modifier.STATIC)) {
1.359 - err().printMessage(
1.360 - Diagnostic.Kind.ERROR, "@OnPropertyChange method needs to be static", e);
1.361 + error("@OnPrprtChange method needs to be static", e);
1.362 return false;
1.363 }
1.364 if (e.getModifiers().contains(Modifier.PRIVATE)) {
1.365 - err().printMessage(
1.366 - Diagnostic.Kind.ERROR, "@OnPropertyChange method cannot be private", e);
1.367 + error("@OnPrprtChange method cannot be private", e);
1.368 return false;
1.369 }
1.370 if (e.getReturnType().getKind() != TypeKind.VOID) {
1.371 - err().printMessage(
1.372 - Diagnostic.Kind.ERROR, "@OnPropertyChange method should return void", e);
1.373 + error("@OnPrprtChange method should return void", e);
1.374 return false;
1.375 }
1.376 String n = e.getSimpleName().toString();
1.377 @@ -852,21 +880,15 @@
1.378 continue;
1.379 }
1.380 if (!e.getModifiers().contains(Modifier.STATIC)) {
1.381 - err().printMessage(
1.382 - Diagnostic.Kind.ERROR, "@OnReceive method needs to be static", e
1.383 - );
1.384 + error("@OnReceive method needs to be static", e);
1.385 return false;
1.386 }
1.387 if (e.getModifiers().contains(Modifier.PRIVATE)) {
1.388 - err().printMessage(
1.389 - Diagnostic.Kind.ERROR, "@OnReceive method cannot be private", e
1.390 - );
1.391 + error("@OnReceive method cannot be private", e);
1.392 return false;
1.393 }
1.394 if (e.getReturnType().getKind() != TypeKind.VOID) {
1.395 - err().printMessage(
1.396 - Diagnostic.Kind.ERROR, "@OnReceive method should return void", e
1.397 - );
1.398 + error("@OnReceive method should return void", e);
1.399 return false;
1.400 }
1.401 String modelClass = null;
1.402 @@ -885,7 +907,7 @@
1.403 }
1.404 if (modelType != null) {
1.405 if (modelClass != null) {
1.406 - err().printMessage(Diagnostic.Kind.ERROR, "There can be only one model class among arguments", e);
1.407 + error("There can be only one model class among arguments", e);
1.408 } else {
1.409 modelClass = modelType.toString();
1.410 if (expectsList) {
1.411 @@ -898,7 +920,7 @@
1.412 }
1.413 }
1.414 if (modelClass == null) {
1.415 - err().printMessage(Diagnostic.Kind.ERROR, "The method needs to have one @Model class as parameter", e);
1.416 + error("The method needs to have one @Model class as parameter", e);
1.417 }
1.418 String n = e.getSimpleName().toString();
1.419 body.append("public void ").append(n).append("(");
1.420 @@ -918,9 +940,9 @@
1.421 sep = ", ";
1.422 }
1.423 if (!skipJSONP) {
1.424 - err().printMessage(Diagnostic.Kind.ERROR,
1.425 + error(
1.426 "Name of jsonp attribute ('" + onR.jsonp() +
1.427 - "') is not used in url attribute '" + onR.url() + "'"
1.428 + "') is not used in url attribute '" + onR.url() + "'", e
1.429 );
1.430 }
1.431 }
1.432 @@ -1016,7 +1038,7 @@
1.433 if (dataName != null) {
1.434 sb.append(" Try \"").append(dataName).append("\"");
1.435 }
1.436 - err().printMessage(Diagnostic.Kind.ERROR, sb.toString(), ee);
1.437 + error(sb.toString(), ee);
1.438 }
1.439 params.append(evName);
1.440 params.append(", \"");
1.441 @@ -1035,7 +1057,7 @@
1.442 params.append(className).append(".this");
1.443 continue;
1.444 }
1.445 - err().printMessage(Diagnostic.Kind.ERROR,
1.446 + error(
1.447 "@On method can only accept String named 'id' or " + className + " arguments",
1.448 ee
1.449 );
1.450 @@ -1059,7 +1081,7 @@
1.451 if (propName != null && ve.getSimpleName().contentEquals(propName)) {
1.452 params.append('"').append(propValue).append('"');
1.453 } else {
1.454 - err().printMessage(Diagnostic.Kind.ERROR, "Unexpected string parameter name. Try \"" + propName + "\".");
1.455 + error("Unexpected string parameter name. Try \"" + propName + "\".", ee);
1.456 }
1.457 continue;
1.458 }
1.459 @@ -1072,8 +1094,8 @@
1.460 params.append(className).append(".this");
1.461 continue;
1.462 }
1.463 - err().printMessage(Diagnostic.Kind.ERROR,
1.464 - "@OnPropertyChange method can only accept String or " + className + " arguments",
1.465 + error(
1.466 + "@OnPrprtChange method can only accept String or " + className + " arguments",
1.467 ee);
1.468 }
1.469 return params;
1.470 @@ -1110,12 +1132,12 @@
1.471 w.write("\n }");
1.472 }
1.473
1.474 - private void writeToString(Property[] props, Writer w) throws IOException {
1.475 + private void writeToString(Prprt[] props, Writer w) throws IOException {
1.476 w.write(" public String toString() {\n");
1.477 w.write(" StringBuilder sb = new StringBuilder();\n");
1.478 w.write(" sb.append('{');\n");
1.479 String sep = "";
1.480 - for (Property p : props) {
1.481 + for (Prprt p : props) {
1.482 w.write(sep);
1.483 w.append(" sb.append(\"" + p.name() + ": \");\n");
1.484 w.append(" sb.append(org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toJSON(prop_");
1.485 @@ -1126,14 +1148,15 @@
1.486 w.write(" return sb.toString();\n");
1.487 w.write(" }\n");
1.488 }
1.489 - private void writeClone(String className, Property[] props, Writer w) throws IOException {
1.490 + private void writeClone(String className, Prprt[] props, Writer w) throws IOException {
1.491 w.write(" public " + className + " clone() {\n");
1.492 w.write(" " + className + " ret = new " + className + "();\n");
1.493 - for (Property p : props) {
1.494 + for (Prprt p : props) {
1.495 if (!p.array()) {
1.496 boolean isModel[] = { false };
1.497 boolean isEnum[] = { false };
1.498 - checkType(p, isModel, isEnum);
1.499 + boolean isPrimitive[] = { false };
1.500 + checkType(p, isModel, isEnum, isPrimitive);
1.501 if (!isModel[0]) {
1.502 w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ";\n");
1.503 continue;
1.504 @@ -1171,26 +1194,59 @@
1.505 return pt.toString();
1.506 }
1.507
1.508 - private String checkType(Property p, boolean[] isModel, boolean[] isEnum) {
1.509 + private String checkType(Prprt p, boolean[] isModel, boolean[] isEnum, boolean[] isPrimitive) {
1.510 + TypeMirror tm;
1.511 + try {
1.512 + String ret = p.typeName(processingEnv);
1.513 + TypeElement e = processingEnv.getElementUtils().getTypeElement(ret);
1.514 + if (e == null) {
1.515 + isModel[0] = true;
1.516 + isEnum[0] = false;
1.517 + isPrimitive[0] = false;
1.518 + return ret;
1.519 + }
1.520 + tm = e.asType();
1.521 + } catch (MirroredTypeException ex) {
1.522 + tm = ex.getTypeMirror();
1.523 + }
1.524 + tm = processingEnv.getTypeUtils().erasure(tm);
1.525 + isPrimitive[0] = tm.getKind().isPrimitive();
1.526 + final Element e = processingEnv.getTypeUtils().asElement(tm);
1.527 + final Model m = e == null ? null : e.getAnnotation(Model.class);
1.528 +
1.529 String ret;
1.530 - try {
1.531 - ret = p.type().getName();
1.532 - } catch (MirroredTypeException ex) {
1.533 - TypeMirror tm = processingEnv.getTypeUtils().erasure(ex.getTypeMirror());
1.534 - final Element e = processingEnv.getTypeUtils().asElement(tm);
1.535 - final Model m = e == null ? null : e.getAnnotation(Model.class);
1.536 - if (m != null) {
1.537 - ret = findPkgName(e) + '.' + m.className();
1.538 - isModel[0] = true;
1.539 - models.put(e, m.className());
1.540 - } else {
1.541 - ret = tm.toString();
1.542 + if (m != null) {
1.543 + ret = findPkgName(e) + '.' + m.className();
1.544 + isModel[0] = true;
1.545 + models.put(e, m.className());
1.546 + } else if (findModelForMthd(e)) {
1.547 + ret = ((TypeElement)e).getQualifiedName().toString();
1.548 + isModel[0] = true;
1.549 + } else {
1.550 + ret = tm.toString();
1.551 + }
1.552 + TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
1.553 + enm = processingEnv.getTypeUtils().erasure(enm);
1.554 + isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
1.555 + return ret;
1.556 + }
1.557 +
1.558 + private static boolean findModelForMthd(Element clazz) {
1.559 + if (clazz == null) {
1.560 + return false;
1.561 + }
1.562 + for (Element e : clazz.getEnclosedElements()) {
1.563 + if (e.getKind() == ElementKind.METHOD) {
1.564 + ExecutableElement ee = (ExecutableElement)e;
1.565 + if (
1.566 + ee.getSimpleName().contentEquals("modelFor") &&
1.567 + ee.getParameters().isEmpty()
1.568 + ) {
1.569 + return true;
1.570 + }
1.571 }
1.572 - TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
1.573 - enm = processingEnv.getTypeUtils().erasure(enm);
1.574 - isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
1.575 }
1.576 - return ret;
1.577 + return false;
1.578 }
1.579
1.580 private Iterable<String> findParamNames(Element e, String url, StringBuilder assembleURL) {
1.581 @@ -1206,7 +1262,7 @@
1.582 }
1.583 int close = url.indexOf('}', next);
1.584 if (close == -1) {
1.585 - err().printMessage(Diagnostic.Kind.ERROR, "Unbalanced '{' and '}' in " + url, e);
1.586 + error("Unbalanced '{' and '}' in " + url, e);
1.587 return params;
1.588 }
1.589 final String paramName = url.substring(next + 1, close);
1.590 @@ -1218,8 +1274,8 @@
1.591 }
1.592 }
1.593
1.594 - private static Property findProperty(Property[] properties, String propName) {
1.595 - for (Property p : properties) {
1.596 + private static Prprt findPrprt(Prprt[] properties, String propName) {
1.597 + for (Prprt p : properties) {
1.598 if (propName.equals(p.name())) {
1.599 return p;
1.600 }
1.601 @@ -1246,4 +1302,96 @@
1.602 }
1.603 return names;
1.604 }
1.605 +
1.606 + private Prprt[] createProps(Element e, Property[] arr) {
1.607 + Prprt[] ret = Prprt.wrap(processingEnv, e, arr);
1.608 + Prprt[] prev = verify.put(e, ret);
1.609 + if (prev != null) {
1.610 + error("Two sets of properties for ", e);
1.611 + }
1.612 + return ret;
1.613 + }
1.614 +
1.615 + private static class Prprt {
1.616 + private final Element e;
1.617 + private final AnnotationMirror tm;
1.618 + private final Property p;
1.619 +
1.620 + public Prprt(Element e, AnnotationMirror tm, Property p) {
1.621 + this.e = e;
1.622 + this.tm = tm;
1.623 + this.p = p;
1.624 + }
1.625 +
1.626 + String name() {
1.627 + return p.name();
1.628 + }
1.629 +
1.630 + boolean array() {
1.631 + return p.array();
1.632 + }
1.633 +
1.634 + String typeName(ProcessingEnvironment env) {
1.635 + try {
1.636 + return p.type().getName();
1.637 + } catch (AnnotationTypeMismatchException ex) {
1.638 + for (Object v : getAnnoValues(env)) {
1.639 + String s = v.toString().replace(" ", "");
1.640 + if (s.startsWith("type=") && s.endsWith(".class")) {
1.641 + return s.substring(5, s.length() - 6);
1.642 + }
1.643 + }
1.644 + throw ex;
1.645 + }
1.646 + }
1.647 +
1.648 +
1.649 + static Prprt[] wrap(ProcessingEnvironment pe, Element e, Property[] arr) {
1.650 + if (arr.length == 0) {
1.651 + return new Prprt[0];
1.652 + }
1.653 +
1.654 + if (e.getKind() != ElementKind.CLASS) {
1.655 + throw new IllegalStateException("" + e.getKind());
1.656 + }
1.657 + TypeElement te = (TypeElement)e;
1.658 + List<? extends AnnotationValue> val = null;
1.659 + for (AnnotationMirror an : te.getAnnotationMirrors()) {
1.660 + for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : an.getElementValues().entrySet()) {
1.661 + if (entry.getKey().getSimpleName().contentEquals("properties")) {
1.662 + val = (List)entry.getValue().getValue();
1.663 + break;
1.664 + }
1.665 + }
1.666 + }
1.667 + if (val == null || val.size() != arr.length) {
1.668 + pe.getMessager().printMessage(Diagnostic.Kind.ERROR, "" + val, e);
1.669 + return new Prprt[0];
1.670 + }
1.671 + Prprt[] ret = new Prprt[arr.length];
1.672 + BIG: for (int i = 0; i < ret.length; i++) {
1.673 + AnnotationMirror am = (AnnotationMirror)val.get(i).getValue();
1.674 + ret[i] = new Prprt(e, am, arr[i]);
1.675 +
1.676 + }
1.677 + return ret;
1.678 + }
1.679 +
1.680 + private List<? extends Object> getAnnoValues(ProcessingEnvironment pe) {
1.681 + try {
1.682 + Class<?> trees = Class.forName("com.sun.tools.javac.api.JavacTrees");
1.683 + Method m = trees.getMethod("instance", ProcessingEnvironment.class);
1.684 + Object instance = m.invoke(null, pe);
1.685 + m = instance.getClass().getMethod("getPath", Element.class, AnnotationMirror.class);
1.686 + Object path = m.invoke(instance, e, tm);
1.687 + m = path.getClass().getMethod("getLeaf");
1.688 + Object leaf = m.invoke(path);
1.689 + m = leaf.getClass().getMethod("getArguments");
1.690 + return (List)m.invoke(leaf);
1.691 + } catch (Exception ex) {
1.692 + return Collections.emptyList();
1.693 + }
1.694 + }
1.695 + }
1.696 +
1.697 }
2.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java Tue Apr 09 10:06:19 2013 +0200
2.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java Wed Apr 10 09:55:26 2013 +0200
2.3 @@ -19,6 +19,8 @@
2.4
2.5 import java.io.IOException;
2.6 import java.util.Locale;
2.7 +import javax.tools.Diagnostic;
2.8 +import javax.tools.JavaFileObject;
2.9 import static org.testng.Assert.*;
2.10 import org.testng.annotations.Test;
2.11
2.12 @@ -39,11 +41,12 @@
2.13 + "}\n";
2.14
2.15 Compile c = Compile.create(html, code);
2.16 - assertEquals(c.getErrors().size(), 1, "One error: " + c.getErrors());
2.17 -
2.18 - String msg = c.getErrors().get(0).getMessage(Locale.ENGLISH);
2.19 - if (!msg.contains("Runnable")) {
2.20 - fail("Should contain warning about Runnable: " + msg);
2.21 + assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
2.22 + for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
2.23 + String msg = e.getMessage(Locale.ENGLISH);
2.24 + if (!msg.contains("Runnable")) {
2.25 + fail("Should contain warning about Runnable: " + msg);
2.26 + }
2.27 }
2.28 }
2.29
3.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java Tue Apr 09 10:06:19 2013 +0200
3.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java Wed Apr 10 09:55:26 2013 +0200
3.3 @@ -52,7 +52,7 @@
3.4 }
3.5
3.6 @Model(className = "People", properties = {
3.7 - @Property(array = true, name = "info", type = PersonImpl.class),
3.8 + @Property(array = true, name = "info", type = Person.class),
3.9 @Property(array = true, name = "nicknames", type = String.class),
3.10 @Property(array = true, name = "age", type = int.class),
3.11 @Property(array = true, name = "sex", type = Sex.class)
4.1 --- a/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java Tue Apr 09 10:06:19 2013 +0200
4.2 +++ b/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java Wed Apr 10 09:55:26 2013 +0200
4.3 @@ -29,11 +29,11 @@
4.4 * @author Jaroslav Tulach
4.5 */
4.6 @Page(xhtml="index.html", className="TwitterModel", properties={
4.7 - @Property(name="savedLists", type=TwitterClient.Twttrs.class, array = true),
4.8 + @Property(name="savedLists", type=Tweeters.class, array = true),
4.9 @Property(name="activeTweetersName", type=String.class),
4.10 @Property(name="activeTweeters", type=String.class, array = true),
4.11 @Property(name="userNameToAdd", type=String.class),
4.12 - @Property(name="currentTweets", type=TwitterClient.Twt.class, array = true)
4.13 + @Property(name="currentTweets", type=Tweet.class, array = true)
4.14 })
4.15 public class TwitterClient {
4.16 @Model(className = "Tweeters", properties = {