Merge of improved model which allows us to refer to @Model classes
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Wed, 10 Apr 2013 12:23:17 +0200
changeset 9655c7cdd2b3f8f
parent 958 c75bd6823179
parent 964 df60ba2aeb87
child 966 fe7ff18eae8d
child 969 df08556c5c7c
child 982 23692e3faa73
child 1001 748dc75f709a
Merge of improved model which allows us to refer to @Model classes
     1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java	Mon Apr 08 19:33:08 2013 +0200
     1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java	Wed Apr 10 12:23:17 2013 +0200
     1.3 @@ -89,10 +89,12 @@
     1.4      @JavaScriptBody(args = { "name", "arr", "run" }, body = 
     1.5          "if (window[name]) return false;\n "
     1.6        + "window[name] = function(data) {\n "
     1.7 +      + "  delete window[name];\n"
     1.8 +      + "  var el = window.document.getElementById(name);\n"
     1.9 +      + "  el.parentNode.removeChild(el);\n"
    1.10        + "  arr[0] = data;\n"
    1.11        + "  run.run__V();\n"
    1.12 -      + "  delete window[name];\n"
    1.13 -      + "};"
    1.14 +      + "};\n"
    1.15        + "return true;\n"
    1.16      )
    1.17      private static boolean defineIfUnused(String name, Object[] arr, Runnable run) {
     2.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java	Mon Apr 08 19:33:08 2013 +0200
     2.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java	Wed Apr 10 12:23:17 2013 +0200
     2.3 @@ -22,6 +22,8 @@
     2.4  import java.io.OutputStreamWriter;
     2.5  import java.io.StringWriter;
     2.6  import java.io.Writer;
     2.7 +import java.lang.annotation.AnnotationTypeMismatchException;
     2.8 +import java.lang.reflect.Method;
     2.9  import java.util.ArrayList;
    2.10  import java.util.Collection;
    2.11  import java.util.Collections;
    2.12 @@ -35,11 +37,12 @@
    2.13  import javax.annotation.processing.AbstractProcessor;
    2.14  import javax.annotation.processing.Completion;
    2.15  import javax.annotation.processing.Completions;
    2.16 -import javax.annotation.processing.Messager;
    2.17 +import javax.annotation.processing.ProcessingEnvironment;
    2.18  import javax.annotation.processing.Processor;
    2.19  import javax.annotation.processing.RoundEnvironment;
    2.20  import javax.annotation.processing.SupportedAnnotationTypes;
    2.21  import javax.lang.model.element.AnnotationMirror;
    2.22 +import javax.lang.model.element.AnnotationValue;
    2.23  import javax.lang.model.element.Element;
    2.24  import javax.lang.model.element.ElementKind;
    2.25  import javax.lang.model.element.ExecutableElement;
    2.26 @@ -78,10 +81,12 @@
    2.27      "org.apidesign.bck2brwsr.htmlpage.api.OnFunction",
    2.28      "org.apidesign.bck2brwsr.htmlpage.api.OnReceive",
    2.29      "org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange",
    2.30 +    "org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty",
    2.31      "org.apidesign.bck2brwsr.htmlpage.api.On"
    2.32  })
    2.33  public final class PageProcessor extends AbstractProcessor {
    2.34      private final Map<Element,String> models = new WeakHashMap<>();
    2.35 +    private final Map<Element,Prprt[]> verify = new WeakHashMap<>();
    2.36      @Override
    2.37      public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    2.38          boolean ok = true;
    2.39 @@ -97,6 +102,42 @@
    2.40          }
    2.41          if (roundEnv.processingOver()) {
    2.42              models.clear();
    2.43 +            for (Map.Entry<Element, Prprt[]> entry : verify.entrySet()) {
    2.44 +                TypeElement te = (TypeElement)entry.getKey();
    2.45 +                String fqn = processingEnv.getElementUtils().getBinaryName(te).toString();
    2.46 +                Element finalElem = processingEnv.getElementUtils().getTypeElement(fqn);
    2.47 +                if (finalElem == null) {
    2.48 +                    continue;
    2.49 +                }
    2.50 +                Prprt[] props;
    2.51 +                Model m = finalElem.getAnnotation(Model.class);
    2.52 +                if (m != null) {
    2.53 +                    props = Prprt.wrap(processingEnv, finalElem, m.properties());
    2.54 +                } else {
    2.55 +                    Page p = finalElem.getAnnotation(Page.class);
    2.56 +                    props = Prprt.wrap(processingEnv, finalElem, p.properties());
    2.57 +                }
    2.58 +                for (Prprt p : props) {
    2.59 +                    boolean[] isModel = { false };
    2.60 +                    boolean[] isEnum = { false };
    2.61 +                    boolean[] isPrimitive = { false };
    2.62 +                    String t = checkType(p, isModel, isEnum, isPrimitive);
    2.63 +                    if (isEnum[0]) {
    2.64 +                        continue;
    2.65 +                    }
    2.66 +                    if (isPrimitive[0]) {
    2.67 +                        continue;
    2.68 +                    }
    2.69 +                    if (isModel[0]) {
    2.70 +                        continue;
    2.71 +                    }
    2.72 +                    if ("java.lang.String".equals(t)) {
    2.73 +                        continue;
    2.74 +                    }
    2.75 +                    error("The type " + t + " should be defined by @Model annotation", entry.getKey());
    2.76 +                }
    2.77 +            }
    2.78 +            verify.clear();
    2.79          }
    2.80          return ok;
    2.81      }
    2.82 @@ -111,8 +152,8 @@
    2.83          }
    2.84      }
    2.85  
    2.86 -    private  Messager err() {
    2.87 -        return processingEnv.getMessager();
    2.88 +    private void error(String msg, Element e) {
    2.89 +        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
    2.90      }
    2.91      
    2.92      private boolean processModel(Element e) {
    2.93 @@ -131,13 +172,15 @@
    2.94              List<String> functions = new ArrayList<>();
    2.95              Map<String, Collection<String>> propsDeps = new HashMap<>();
    2.96              Map<String, Collection<String>> functionDeps = new HashMap<>();
    2.97 -            if (!generateComputedProperties(body, m.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
    2.98 +            Prprt[] props = createProps(e, m.properties());
    2.99 +            
   2.100 +            if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
   2.101                  ok = false;
   2.102              }
   2.103 -            if (!generateOnChange(e, propsDeps, m.properties(), className, functionDeps)) {
   2.104 +            if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
   2.105                  ok = false;
   2.106              }
   2.107 -            if (!generateProperties(e, body, m.properties(), propsGetSet, propsDeps, functionDeps)) {
   2.108 +            if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) {
   2.109                  ok = false;
   2.110              }
   2.111              if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
   2.112 @@ -168,7 +211,7 @@
   2.113                  w.append("  ").append(className).append("(Object json) {\n");
   2.114                  int values = 0;
   2.115                  for (int i = 0; i < propsGetSet.size(); i += 4) {
   2.116 -                    Property p = findProperty(m.properties(), propsGetSet.get(i));
   2.117 +                    Prprt p = findPrprt(props, propsGetSet.get(i));
   2.118                      if (p == null) {
   2.119                          continue;
   2.120                      }
   2.121 @@ -177,7 +220,7 @@
   2.122                  w.append("    Object[] ret = new Object[" + values + "];\n");
   2.123                  w.append("    org.apidesign.bck2brwsr.htmlpage.ConvertTypes.extractJSON(json, new String[] {\n");
   2.124                  for (int i = 0; i < propsGetSet.size(); i += 4) {
   2.125 -                    Property p = findProperty(m.properties(), propsGetSet.get(i));
   2.126 +                    Prprt p = findPrprt(props, propsGetSet.get(i));
   2.127                      if (p == null) {
   2.128                          continue;
   2.129                      }
   2.130 @@ -186,24 +229,24 @@
   2.131                  w.append("    }, ret);\n");
   2.132                  for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i += 4) {
   2.133                      final String pn = propsGetSet.get(i);
   2.134 -                    Property p = findProperty(m.properties(), pn);
   2.135 +                    Prprt p = findPrprt(props, pn);
   2.136                      if (p == null) {
   2.137                          continue;
   2.138                      }
   2.139                      boolean[] isModel = { false };
   2.140                      boolean[] isEnum = { false };
   2.141 -                    String type = checkType(m.properties()[prop++], isModel, isEnum);
   2.142 -                    if (isEnum[0]) {
   2.143 -//                        w.append(type).append(".valueOf((String)");
   2.144 -//                        close = true;
   2.145 -                        w.append("    this.prop_").append(pn);
   2.146 -                        w.append(" = null;\n");
   2.147 -                    } else if (p.array()) {
   2.148 +                    boolean isPrimitive[] = { false };
   2.149 +                    String type = checkType(props[prop++], isModel, isEnum, isPrimitive);
   2.150 +                    if (p.array()) {
   2.151                          w.append("if (ret[" + cnt + "] instanceof Object[]) {\n");
   2.152                          w.append("  for (Object e : ((Object[])ret[" + cnt + "])) {\n");
   2.153                          if (isModel[0]) {
   2.154                              w.append("    this.prop_").append(pn).append(".add(new ");
   2.155                              w.append(type).append("(e));\n");
   2.156 +                        } else if (isEnum[0]) {
   2.157 +                            w.append("    this.prop_").append(pn);
   2.158 +                            w.append(".add(");
   2.159 +                            w.append(type).append(".valueOf((String)e));\n");
   2.160                          } else {
   2.161                              if (isPrimitive(type)) {
   2.162                                  w.append("    this.prop_").append(pn).append(".add(((Number)e).");
   2.163 @@ -216,7 +259,11 @@
   2.164                          w.append("  }\n");
   2.165                          w.append("}\n");
   2.166                      } else {
   2.167 -                        if (isPrimitive(type)) {
   2.168 +                        if (isEnum[0]) {
   2.169 +                            w.append("    this.prop_").append(pn);
   2.170 +                            w.append(" = ");
   2.171 +                            w.append(type).append(".valueOf((String)ret[" + cnt + "]);\n");
   2.172 +                        } else if (isPrimitive(type)) {
   2.173                              w.append("    this.prop_").append(pn);
   2.174                              w.append(" = ((Number)").append("ret[" + cnt + "]).");
   2.175                              w.append(type).append("Value();\n");
   2.176 @@ -230,14 +277,14 @@
   2.177                  }
   2.178                  w.append("    intKnckt();\n");
   2.179                  w.append("  };\n");
   2.180 -                writeToString(m.properties(), w);
   2.181 -                writeClone(className, m.properties(), w);
   2.182 +                writeToString(props, w);
   2.183 +                writeClone(className, props, w);
   2.184                  w.append("}\n");
   2.185              } finally {
   2.186                  w.close();
   2.187              }
   2.188          } catch (IOException ex) {
   2.189 -            err().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
   2.190 +            error("Can't create " + className + ".java", e);
   2.191              return false;
   2.192          }
   2.193          return ok;
   2.194 @@ -256,7 +303,7 @@
   2.195              pp = ProcessPage.readPage(is);
   2.196              is.close();
   2.197          } catch (IOException iOException) {
   2.198 -            err().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml() + " as " + iOException.getMessage(), e);
   2.199 +            error("Can't read " + p.xhtml() + " as " + iOException.getMessage(), e);
   2.200              ok = false;
   2.201              pp = null;
   2.202          }
   2.203 @@ -272,13 +319,15 @@
   2.204              List<String> functions = new ArrayList<>();
   2.205              Map<String, Collection<String>> propsDeps = new HashMap<>();
   2.206              Map<String, Collection<String>> functionDeps = new HashMap<>();
   2.207 -            if (!generateComputedProperties(body, p.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
   2.208 +            
   2.209 +            Prprt[] props = createProps(e, p.properties());
   2.210 +            if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
   2.211                  ok = false;
   2.212              }
   2.213 -            if (!generateOnChange(e, propsDeps, p.properties(), className, functionDeps)) {
   2.214 +            if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
   2.215                  ok = false;
   2.216              }
   2.217 -            if (!generateProperties(e, body, p.properties(), propsGetSet, propsDeps, functionDeps)) {
   2.218 +            if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) {
   2.219                  ok = false;
   2.220              }
   2.221              if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
   2.222 @@ -327,7 +376,7 @@
   2.223                  w.close();
   2.224              }
   2.225          } catch (IOException ex) {
   2.226 -            err().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
   2.227 +            error("Can't create " + className + ".java", e);
   2.228              return false;
   2.229          }
   2.230          return ok;
   2.231 @@ -373,24 +422,24 @@
   2.232                  if (oc != null) {
   2.233                      for (String id : oc.id()) {
   2.234                          if (pp == null) {
   2.235 -                            err().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " not found in HTML page.");
   2.236 +                            error("id = " + id + " not found in HTML page.", method);
   2.237                              ok = false;
   2.238                              continue;
   2.239                          }
   2.240                          if (pp.tagNameForId(id) == null) {
   2.241 -                            err().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
   2.242 +                            error("id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
   2.243                              ok = false;
   2.244                              continue;
   2.245                          }
   2.246                          ExecutableElement ee = (ExecutableElement)method;
   2.247                          CharSequence params = wrapParams(ee, id, className, "ev", null);
   2.248                          if (!ee.getModifiers().contains(Modifier.STATIC)) {
   2.249 -                            err().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee);
   2.250 +                            error("@On method has to be static", ee);
   2.251                              ok = false;
   2.252                              continue;
   2.253                          }
   2.254                          if (ee.getModifiers().contains(Modifier.PRIVATE)) {
   2.255 -                            err().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee);
   2.256 +                            error("@On method can't be private", ee);
   2.257                              ok = false;
   2.258                              continue;
   2.259                          }
   2.260 @@ -470,13 +519,13 @@
   2.261  
   2.262      private boolean generateProperties(
   2.263          Element where,
   2.264 -        Writer w, Property[] properties,
   2.265 +        Writer w, Prprt[] properties,
   2.266          Collection<String> props, 
   2.267          Map<String,Collection<String>> deps,
   2.268          Map<String,Collection<String>> functionDeps
   2.269      ) throws IOException {
   2.270          boolean ok = true;
   2.271 -        for (Property p : properties) {
   2.272 +        for (Prprt p : properties) {
   2.273              final String tn;
   2.274              tn = typeName(where, p);
   2.275              String[] gs = toGetSet(p.name(), tn, p.array());
   2.276 @@ -546,7 +595,7 @@
   2.277      }
   2.278  
   2.279      private boolean generateComputedProperties(
   2.280 -        Writer w, Property[] fixedProps,
   2.281 +        Writer w, Prprt[] fixedProps,
   2.282          Collection<? extends Element> arr, Collection<String> props,
   2.283          Map<String,Collection<String>> deps
   2.284      ) throws IOException {
   2.285 @@ -648,27 +697,18 @@
   2.286          };
   2.287      }
   2.288  
   2.289 -    private String typeName(Element where, Property p) {
   2.290 +    private String typeName(Element where, Prprt p) {
   2.291          String ret;
   2.292          boolean[] isModel = { false };
   2.293          boolean[] isEnum = { false };
   2.294 -        ret = checkType(p, isModel, isEnum);
   2.295 +        boolean isPrimitive[] = { false };
   2.296 +        ret = checkType(p, isModel, isEnum, isPrimitive);
   2.297          if (p.array()) {
   2.298              String bt = findBoxedType(ret);
   2.299              if (bt != null) {
   2.300                  return bt;
   2.301              }
   2.302          }
   2.303 -        if (!isModel[0] && !"java.lang.String".equals(ret) && !isEnum[0]) {
   2.304 -            String bt = findBoxedType(ret);
   2.305 -            if (bt == null) {
   2.306 -                err().printMessage(
   2.307 -                    Diagnostic.Kind.ERROR, 
   2.308 -                    "Only primitive types supported in the mapping. Not " + ret,
   2.309 -                    where
   2.310 -                );
   2.311 -            }
   2.312 -        }
   2.313          return ret;
   2.314      }
   2.315      
   2.316 @@ -700,20 +740,20 @@
   2.317          return null;
   2.318      }
   2.319  
   2.320 -    private boolean verifyPropName(Element e, String propName, Property[] existingProps) {
   2.321 +    private boolean verifyPropName(Element e, String propName, Prprt[] existingProps) {
   2.322          StringBuilder sb = new StringBuilder();
   2.323          String sep = "";
   2.324 -        for (Property property : existingProps) {
   2.325 -            if (property.name().equals(propName)) {
   2.326 +        for (Prprt Prprt : existingProps) {
   2.327 +            if (Prprt.name().equals(propName)) {
   2.328                  return true;
   2.329              }
   2.330              sb.append(sep);
   2.331              sb.append('"');
   2.332 -            sb.append(property.name());
   2.333 +            sb.append(Prprt.name());
   2.334              sb.append('"');
   2.335              sep = ", ";
   2.336          }
   2.337 -        err().printMessage(Diagnostic.Kind.ERROR,
   2.338 +        error(
   2.339              propName + " is not one of known properties: " + sb
   2.340              , e
   2.341          );
   2.342 @@ -743,21 +783,15 @@
   2.343                  continue;
   2.344              }
   2.345              if (!e.getModifiers().contains(Modifier.STATIC)) {
   2.346 -                err().printMessage(
   2.347 -                    Diagnostic.Kind.ERROR, "@OnFunction method needs to be static", e
   2.348 -                );
   2.349 +                error("@OnFunction method needs to be static", e);
   2.350                  return false;
   2.351              }
   2.352              if (e.getModifiers().contains(Modifier.PRIVATE)) {
   2.353 -                err().printMessage(
   2.354 -                    Diagnostic.Kind.ERROR, "@OnFunction method cannot be private", e
   2.355 -                );
   2.356 +                error("@OnFunction method cannot be private", e);
   2.357                  return false;
   2.358              }
   2.359              if (e.getReturnType().getKind() != TypeKind.VOID) {
   2.360 -                err().printMessage(
   2.361 -                    Diagnostic.Kind.ERROR, "@OnFunction method should return void", e
   2.362 -                );
   2.363 +                error("@OnFunction method should return void", e);
   2.364                  return false;
   2.365              }
   2.366              String n = e.getSimpleName().toString();
   2.367 @@ -774,7 +808,7 @@
   2.368      }
   2.369  
   2.370      private boolean generateOnChange(Element clazz, Map<String,Collection<String>> propDeps,
   2.371 -        Property[] properties, String className, 
   2.372 +        Prprt[] properties, String className, 
   2.373          Map<String, Collection<String>> functionDeps
   2.374      ) {
   2.375          for (Element m : clazz.getEnclosedElements()) {
   2.376 @@ -787,24 +821,21 @@
   2.377                  continue;
   2.378              }
   2.379              for (String pn : onPC.value()) {
   2.380 -                if (findProperty(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
   2.381 -                    err().printMessage(Diagnostic.Kind.ERROR, "No property named '" + pn + "' in the model");
   2.382 +                if (findPrprt(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
   2.383 +                    error("No Prprt named '" + pn + "' in the model", clazz);
   2.384                      return false;
   2.385                  }
   2.386              }
   2.387              if (!e.getModifiers().contains(Modifier.STATIC)) {
   2.388 -                err().printMessage(
   2.389 -                    Diagnostic.Kind.ERROR, "@OnPropertyChange method needs to be static", e);
   2.390 +                error("@OnPrprtChange method needs to be static", e);
   2.391                  return false;
   2.392              }
   2.393              if (e.getModifiers().contains(Modifier.PRIVATE)) {
   2.394 -                err().printMessage(
   2.395 -                    Diagnostic.Kind.ERROR, "@OnPropertyChange method cannot be private", e);
   2.396 +                error("@OnPrprtChange method cannot be private", e);
   2.397                  return false;
   2.398              }
   2.399              if (e.getReturnType().getKind() != TypeKind.VOID) {
   2.400 -                err().printMessage(
   2.401 -                    Diagnostic.Kind.ERROR, "@OnPropertyChange method should return void", e);
   2.402 +                error("@OnPrprtChange method should return void", e);
   2.403                  return false;
   2.404              }
   2.405              String n = e.getSimpleName().toString();
   2.406 @@ -849,21 +880,15 @@
   2.407                  continue;
   2.408              }
   2.409              if (!e.getModifiers().contains(Modifier.STATIC)) {
   2.410 -                err().printMessage(
   2.411 -                    Diagnostic.Kind.ERROR, "@OnReceive method needs to be static", e
   2.412 -                );
   2.413 +                error("@OnReceive method needs to be static", e);
   2.414                  return false;
   2.415              }
   2.416              if (e.getModifiers().contains(Modifier.PRIVATE)) {
   2.417 -                err().printMessage(
   2.418 -                    Diagnostic.Kind.ERROR, "@OnReceive method cannot be private", e
   2.419 -                );
   2.420 +                error("@OnReceive method cannot be private", e);
   2.421                  return false;
   2.422              }
   2.423              if (e.getReturnType().getKind() != TypeKind.VOID) {
   2.424 -                err().printMessage(
   2.425 -                    Diagnostic.Kind.ERROR, "@OnReceive method should return void", e
   2.426 -                );
   2.427 +                error("@OnReceive method should return void", e);
   2.428                  return false;
   2.429              }
   2.430              String modelClass = null;
   2.431 @@ -882,7 +907,7 @@
   2.432                      }
   2.433                      if (modelType != null) {
   2.434                          if (modelClass != null) {
   2.435 -                            err().printMessage(Diagnostic.Kind.ERROR, "There can be only one model class among arguments", e);
   2.436 +                            error("There can be only one model class among arguments", e);
   2.437                          } else {
   2.438                              modelClass = modelType.toString();
   2.439                              if (expectsList) {
   2.440 @@ -895,7 +920,7 @@
   2.441                  }
   2.442              }
   2.443              if (modelClass == null) {
   2.444 -                err().printMessage(Diagnostic.Kind.ERROR, "The method needs to have one @Model class as parameter", e);
   2.445 +                error("The method needs to have one @Model class as parameter", e);
   2.446              }
   2.447              String n = e.getSimpleName().toString();
   2.448              body.append("public void ").append(n).append("(");
   2.449 @@ -915,9 +940,9 @@
   2.450                      sep = ", ";
   2.451                  }
   2.452                  if (!skipJSONP) {
   2.453 -                    err().printMessage(Diagnostic.Kind.ERROR, 
   2.454 +                    error(
   2.455                          "Name of jsonp attribute ('" + onR.jsonp() + 
   2.456 -                        "') is not used in url attribute '" + onR.url() + "'"
   2.457 +                        "') is not used in url attribute '" + onR.url() + "'", e
   2.458                      );
   2.459                  }
   2.460              }
   2.461 @@ -1013,7 +1038,7 @@
   2.462                          if (dataName != null) {
   2.463                              sb.append(" Try \"").append(dataName).append("\"");
   2.464                          }
   2.465 -                        err().printMessage(Diagnostic.Kind.ERROR, sb.toString(), ee);
   2.466 +                        error(sb.toString(), ee);
   2.467                      }
   2.468                      params.append(evName);
   2.469                      params.append(", \"");
   2.470 @@ -1032,7 +1057,7 @@
   2.471                  params.append(className).append(".this");
   2.472                  continue;
   2.473              }
   2.474 -            err().printMessage(Diagnostic.Kind.ERROR, 
   2.475 +            error(
   2.476                  "@On method can only accept String named 'id' or " + className + " arguments",
   2.477                  ee
   2.478              );
   2.479 @@ -1056,7 +1081,7 @@
   2.480                  if (propName != null && ve.getSimpleName().contentEquals(propName)) {
   2.481                      params.append('"').append(propValue).append('"');
   2.482                  } else {
   2.483 -                    err().printMessage(Diagnostic.Kind.ERROR, "Unexpected string parameter name. Try \"" + propName + "\".");
   2.484 +                    error("Unexpected string parameter name. Try \"" + propName + "\".", ee);
   2.485                  }
   2.486                  continue;
   2.487              }
   2.488 @@ -1069,8 +1094,8 @@
   2.489                  params.append(className).append(".this");
   2.490                  continue;
   2.491              }
   2.492 -            err().printMessage(Diagnostic.Kind.ERROR,
   2.493 -                "@OnPropertyChange method can only accept String or " + className + " arguments",
   2.494 +            error(
   2.495 +                "@OnPrprtChange method can only accept String or " + className + " arguments",
   2.496                  ee);
   2.497          }
   2.498          return params;
   2.499 @@ -1107,12 +1132,12 @@
   2.500          w.write("\n  }");
   2.501      }
   2.502      
   2.503 -    private void writeToString(Property[] props, Writer w) throws IOException {
   2.504 +    private void writeToString(Prprt[] props, Writer w) throws IOException {
   2.505          w.write("  public String toString() {\n");
   2.506          w.write("    StringBuilder sb = new StringBuilder();\n");
   2.507          w.write("    sb.append('{');\n");
   2.508          String sep = "";
   2.509 -        for (Property p : props) {
   2.510 +        for (Prprt p : props) {
   2.511              w.write(sep);
   2.512              w.append("    sb.append(\"" + p.name() + ": \");\n");
   2.513              w.append("    sb.append(org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toJSON(prop_");
   2.514 @@ -1123,14 +1148,15 @@
   2.515          w.write("    return sb.toString();\n");
   2.516          w.write("  }\n");
   2.517      }
   2.518 -    private void writeClone(String className, Property[] props, Writer w) throws IOException {
   2.519 +    private void writeClone(String className, Prprt[] props, Writer w) throws IOException {
   2.520          w.write("  public " + className + " clone() {\n");
   2.521          w.write("    " + className + " ret = new " + className + "();\n");
   2.522 -        for (Property p : props) {
   2.523 +        for (Prprt p : props) {
   2.524              if (!p.array()) {
   2.525                  boolean isModel[] = { false };
   2.526                  boolean isEnum[] = { false };
   2.527 -                checkType(p, isModel, isEnum);
   2.528 +                boolean isPrimitive[] = { false };
   2.529 +                checkType(p, isModel, isEnum, isPrimitive);
   2.530                  if (!isModel[0]) {
   2.531                      w.write("    ret.prop_" + p.name() + " = prop_" + p.name() + ";\n");
   2.532                      continue;
   2.533 @@ -1168,26 +1194,59 @@
   2.534          return pt.toString();
   2.535      }
   2.536  
   2.537 -    private String checkType(Property p, boolean[] isModel, boolean[] isEnum) {
   2.538 +    private String checkType(Prprt p, boolean[] isModel, boolean[] isEnum, boolean[] isPrimitive) {
   2.539 +        TypeMirror tm;
   2.540 +        try {
   2.541 +            String ret = p.typeName(processingEnv);
   2.542 +            TypeElement e = processingEnv.getElementUtils().getTypeElement(ret);
   2.543 +            if (e == null) {
   2.544 +                isModel[0] = true;
   2.545 +                isEnum[0] = false;
   2.546 +                isPrimitive[0] = false;
   2.547 +                return ret;
   2.548 +            }
   2.549 +            tm = e.asType();
   2.550 +        } catch (MirroredTypeException ex) {
   2.551 +            tm = ex.getTypeMirror();
   2.552 +        }
   2.553 +        tm = processingEnv.getTypeUtils().erasure(tm);
   2.554 +        isPrimitive[0] = tm.getKind().isPrimitive();
   2.555 +        final Element e = processingEnv.getTypeUtils().asElement(tm);
   2.556 +        final Model m = e == null ? null : e.getAnnotation(Model.class);
   2.557 +        
   2.558          String ret;
   2.559 -        try {
   2.560 -            ret = p.type().getName();
   2.561 -        } catch (MirroredTypeException ex) {
   2.562 -            TypeMirror tm = processingEnv.getTypeUtils().erasure(ex.getTypeMirror());
   2.563 -            final Element e = processingEnv.getTypeUtils().asElement(tm);
   2.564 -            final Model m = e == null ? null : e.getAnnotation(Model.class);
   2.565 -            if (m != null) {
   2.566 -                ret = findPkgName(e) + '.' + m.className();
   2.567 -                isModel[0] = true;
   2.568 -                models.put(e, m.className());
   2.569 -            } else {
   2.570 -                ret = tm.toString();
   2.571 +        if (m != null) {
   2.572 +            ret = findPkgName(e) + '.' + m.className();
   2.573 +            isModel[0] = true;
   2.574 +            models.put(e, m.className());
   2.575 +        } else if (findModelForMthd(e)) {
   2.576 +            ret = ((TypeElement)e).getQualifiedName().toString();
   2.577 +            isModel[0] = true;
   2.578 +        } else {
   2.579 +            ret = tm.toString();
   2.580 +        }
   2.581 +        TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
   2.582 +        enm = processingEnv.getTypeUtils().erasure(enm);
   2.583 +        isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
   2.584 +        return ret;
   2.585 +    }
   2.586 +    
   2.587 +    private static boolean findModelForMthd(Element clazz) {
   2.588 +        if (clazz == null) {
   2.589 +            return false;
   2.590 +        }
   2.591 +        for (Element e : clazz.getEnclosedElements()) {
   2.592 +            if (e.getKind() == ElementKind.METHOD) {
   2.593 +                ExecutableElement ee = (ExecutableElement)e;
   2.594 +                if (
   2.595 +                    ee.getSimpleName().contentEquals("modelFor") &&
   2.596 +                    ee.getParameters().isEmpty()
   2.597 +                ) {
   2.598 +                    return true;
   2.599 +                }
   2.600              }
   2.601 -            TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
   2.602 -            enm = processingEnv.getTypeUtils().erasure(enm);
   2.603 -            isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
   2.604          }
   2.605 -        return ret;
   2.606 +        return false;
   2.607      }
   2.608  
   2.609      private Iterable<String> findParamNames(Element e, String url, StringBuilder assembleURL) {
   2.610 @@ -1203,7 +1262,7 @@
   2.611              }
   2.612              int close = url.indexOf('}', next);
   2.613              if (close == -1) {
   2.614 -                err().printMessage(Diagnostic.Kind.ERROR, "Unbalanced '{' and '}' in " + url, e);
   2.615 +                error("Unbalanced '{' and '}' in " + url, e);
   2.616                  return params;
   2.617              }
   2.618              final String paramName = url.substring(next + 1, close);
   2.619 @@ -1215,8 +1274,8 @@
   2.620          }
   2.621      }
   2.622  
   2.623 -    private static Property findProperty(Property[] properties, String propName) {
   2.624 -        for (Property p : properties) {
   2.625 +    private static Prprt findPrprt(Prprt[] properties, String propName) {
   2.626 +        for (Prprt p : properties) {
   2.627              if (propName.equals(p.name())) {
   2.628                  return p;
   2.629              }
   2.630 @@ -1243,4 +1302,96 @@
   2.631          }
   2.632          return names;
   2.633      }
   2.634 +    
   2.635 +    private Prprt[] createProps(Element e, Property[] arr) {
   2.636 +        Prprt[] ret = Prprt.wrap(processingEnv, e, arr);
   2.637 +        Prprt[] prev = verify.put(e, ret);
   2.638 +        if (prev != null) {
   2.639 +            error("Two sets of properties for ", e);
   2.640 +        }
   2.641 +        return ret;
   2.642 +    }
   2.643 +    
   2.644 +    private static class Prprt {
   2.645 +        private final Element e;
   2.646 +        private final AnnotationMirror tm;
   2.647 +        private final Property p;
   2.648 +
   2.649 +        public Prprt(Element e, AnnotationMirror tm, Property p) {
   2.650 +            this.e = e;
   2.651 +            this.tm = tm;
   2.652 +            this.p = p;
   2.653 +        }
   2.654 +        
   2.655 +        String name() {
   2.656 +            return p.name();
   2.657 +        }
   2.658 +        
   2.659 +        boolean array() {
   2.660 +            return p.array();
   2.661 +        }
   2.662 +
   2.663 +        String typeName(ProcessingEnvironment env) {
   2.664 +            try {
   2.665 +                return p.type().getName();
   2.666 +            } catch (AnnotationTypeMismatchException ex) {
   2.667 +                for (Object v : getAnnoValues(env)) {
   2.668 +                    String s = v.toString().replace(" ", "");
   2.669 +                    if (s.startsWith("type=") && s.endsWith(".class")) {
   2.670 +                        return s.substring(5, s.length() - 6);
   2.671 +                    }
   2.672 +                }
   2.673 +                throw ex;
   2.674 +            }
   2.675 +        }
   2.676 +        
   2.677 +        
   2.678 +        static Prprt[] wrap(ProcessingEnvironment pe, Element e, Property[] arr) {
   2.679 +            if (arr.length == 0) {
   2.680 +                return new Prprt[0];
   2.681 +            }
   2.682 +            
   2.683 +            if (e.getKind() != ElementKind.CLASS) {
   2.684 +                throw new IllegalStateException("" + e.getKind());
   2.685 +            }
   2.686 +            TypeElement te = (TypeElement)e;
   2.687 +            List<? extends AnnotationValue> val = null;
   2.688 +            for (AnnotationMirror an : te.getAnnotationMirrors()) {
   2.689 +                for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : an.getElementValues().entrySet()) {
   2.690 +                    if (entry.getKey().getSimpleName().contentEquals("properties")) {
   2.691 +                        val = (List)entry.getValue().getValue();
   2.692 +                        break;
   2.693 +                    }
   2.694 +                }
   2.695 +            }
   2.696 +            if (val == null || val.size() != arr.length) {
   2.697 +                pe.getMessager().printMessage(Diagnostic.Kind.ERROR, "" + val, e);
   2.698 +                return new Prprt[0];
   2.699 +            }
   2.700 +            Prprt[] ret = new Prprt[arr.length];
   2.701 +            BIG: for (int i = 0; i < ret.length; i++) {
   2.702 +                AnnotationMirror am = (AnnotationMirror)val.get(i).getValue();
   2.703 +                ret[i] = new Prprt(e, am, arr[i]);
   2.704 +                
   2.705 +            }
   2.706 +            return ret;
   2.707 +        }
   2.708 +        
   2.709 +        private List<? extends Object> getAnnoValues(ProcessingEnvironment pe) {
   2.710 +            try {
   2.711 +                Class<?> trees = Class.forName("com.sun.tools.javac.api.JavacTrees");
   2.712 +                Method m = trees.getMethod("instance", ProcessingEnvironment.class);
   2.713 +                Object instance = m.invoke(null, pe);
   2.714 +                m = instance.getClass().getMethod("getPath", Element.class, AnnotationMirror.class);
   2.715 +                Object path = m.invoke(instance, e, tm);
   2.716 +                m = path.getClass().getMethod("getLeaf");
   2.717 +                Object leaf = m.invoke(path);
   2.718 +                m = leaf.getClass().getMethod("getArguments");
   2.719 +                return (List)m.invoke(leaf);
   2.720 +            } catch (Exception ex) {
   2.721 +                return Collections.emptyList();
   2.722 +            }
   2.723 +        }
   2.724 +    }
   2.725 +    
   2.726  }
     3.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Model.java	Mon Apr 08 19:33:08 2013 +0200
     3.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Model.java	Wed Apr 10 12:23:17 2013 +0200
     3.3 @@ -27,8 +27,9 @@
     3.4   * annotated by {@link ComputedProperty} which define derived
     3.5   * properties in the model class.
     3.6   * <p>
     3.7 - * The {@link #className() generated class} will have methods
     3.8 - * to convert the object <code>toJSON</code> and <code>fromJSON</code>.
     3.9 + * The {@link #className() generated class}'s <code>toString</code>
    3.10 + * converts the state of the object into 
    3.11 + * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> format.
    3.12   *
    3.13   * @author Jaroslav Tulach <jtulach@netbeans.org>
    3.14   */
     4.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnReceive.java	Mon Apr 08 19:33:08 2013 +0200
     4.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnReceive.java	Wed Apr 10 12:23:17 2013 +0200
     4.3 @@ -22,12 +22,51 @@
     4.4  import java.lang.annotation.RetentionPolicy;
     4.5  import java.lang.annotation.Target;
     4.6  
     4.7 -/** Static methods in classes annotated by {@link Model} or {@link Page}
     4.8 - * can be marked by this annotation establish a JSON communication point.
     4.9 +/** Static methods in classes annotated by {@link Page}
    4.10 + * can be marked by this annotation to establish a 
    4.11 + * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a>
    4.12 + * communication point.
    4.13   * The associated model page then gets new method to invoke a network
    4.14 - * connection 
    4.15 + * connection. Example follows:
    4.16 + * 
    4.17 + * <pre>
    4.18 + * {@link Page @Page}(className="MyModel", xhtml="page.html", properties={
    4.19 + *   {@link Property @Property}(name = "people", type=Person.class, array=true)
    4.20 + * })
    4.21 + * class MyModelImpl {
    4.22 + *   {@link Model @Model}(className="Person", properties={
    4.23 + *     {@link Property @Property}(name = "firstName", type=String.class),
    4.24 + *     {@link Property @Property}(name = "lastName", type=String.class)
    4.25 + *   })
    4.26 + *   static class PersonImpl {
    4.27 + *     {@link ComputedProperty @ComputedProperty}
    4.28 + *     static String fullName(String firstName, String lastName) {
    4.29 + *       return firstName + " " + lastName;
    4.30 + *     }
    4.31 + *   }
    4.32 + * 
    4.33 + *   {@link OnReceive @OnReceive}(url = "{protocol}://your.server.com/person/{name}")
    4.34 + *   static void getANewPerson(MyModel m, Person p) {
    4.35 + *     {@link Element#alert Element.alert}("Adding " + p.getFullName() + '!');
    4.36 + *     m.getPeople().add(p);
    4.37 + *   }
    4.38 + * 
    4.39 + *   // the above will generate method <code>getANewPerson</code> in class <code>MyModel</code>.
    4.40 + *   // with <code>protocol</code> and <code>name</code> arguments
    4.41 + *   // which asynchronously contacts the server and in case of success calls
    4.42 + *   // your {@link OnReceive @OnReceive} with parsed in data
    4.43 + * 
    4.44 + *   {@link On @On}(event={@link OnEvent#CLICK OnEvent.CLICK}, id="rqst")
    4.45 + *   static void requestSmith(MyModel m) {
    4.46 + *     m.getANewPerson("http", "Smith");
    4.47 + *   }
    4.48 + * }
    4.49 + * </pre>
    4.50 + * When the server returns <code>{ "firstName" : "John", "lastName" : "Smith" }</code>
    4.51 + * the browser will show alert message <em>Adding John Smith!</em>.
    4.52   * 
    4.53   * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.54 + * @since 0.6
    4.55   */
    4.56  @Retention(RetentionPolicy.SOURCE)
    4.57  @Target(ElementType.METHOD)
     5.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypesTest.java	Mon Apr 08 19:33:08 2013 +0200
     5.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypesTest.java	Wed Apr 10 12:23:17 2013 +0200
     5.3 @@ -43,7 +43,7 @@
     5.4          
     5.5          assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
     5.6          assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
     5.7 -//        assert Sex.MALE.equals(p.getSex()) : "Sex: " + p.getSex();
     5.8 +        assert Sex.MALE.equals(p.getSex()) : "Sex: " + p.getSex();
     5.9      }
    5.10      
    5.11      @Factory public static Object[] create() {
     6.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java	Mon Apr 08 19:33:08 2013 +0200
     6.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/JSONTest.java	Wed Apr 10 12:23:17 2013 +0200
     6.3 @@ -19,7 +19,7 @@
     6.4  
     6.5  import java.util.Arrays;
     6.6  import java.util.Iterator;
     6.7 -import java.util.List;
     6.8 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
     6.9  import org.apidesign.bck2brwsr.htmlpage.api.OnReceive;
    6.10  import org.apidesign.bck2brwsr.htmlpage.api.Page;
    6.11  import org.apidesign.bck2brwsr.htmlpage.api.Property;
    6.12 @@ -39,10 +39,12 @@
    6.13   */
    6.14  @Page(xhtml = "Empty.html", className = "JSONik", properties = {
    6.15      @Property(name = "fetched", type = PersonImpl.class),
    6.16 -    @Property(name = "fetchedCount", type = int.class)
    6.17 +    @Property(name = "fetchedCount", type = int.class),
    6.18 +    @Property(name = "fetchedSex", type = Sex.class, array = true)
    6.19  })
    6.20  public class JSONTest {
    6.21      private JSONik js;
    6.22 +    private Integer orig;
    6.23      
    6.24      @Test public void personToString() throws JSONException {
    6.25          Person p = new Person();
    6.26 @@ -169,7 +171,7 @@
    6.27          }
    6.28          
    6.29          assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
    6.30 -      //  assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    6.31 +        assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    6.32      }
    6.33      
    6.34      @OnReceive(url="/{url}?callme={me}", jsonp = "me")
    6.35 @@ -184,7 +186,11 @@
    6.36          parameters = { "callme" }
    6.37      ))
    6.38      @BrwsrTest public void loadAndParseJSONP() throws InterruptedException {
    6.39 +        
    6.40          if (js == null) {
    6.41 +            orig = scriptElements();
    6.42 +            assert orig > 0 : "There should be some scripts on the page";
    6.43 +            
    6.44              js = new JSONik();
    6.45              js.applyBindings();
    6.46  
    6.47 @@ -197,8 +203,15 @@
    6.48          }
    6.49          
    6.50          assert "Mitar".equals(p.getFirstName()) : "Unexpected: " + p.getFirstName();
    6.51 -      //  assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    6.52 +        assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    6.53 +        
    6.54 +        int now = scriptElements();
    6.55 +        
    6.56 +        assert orig == now : "The set of elements is unchanged. Delta: " + (now - orig);
    6.57      }
    6.58 +    
    6.59 +    @JavaScriptBody(args = {  }, body = "return window.document.getElementsByTagName('script').length;")
    6.60 +    private static native int scriptElements();
    6.61  
    6.62      @Http(@Http.Resource(
    6.63          content = "{'firstName': 'Sitar', 'sex': 'MALE'}", 
    6.64 @@ -220,7 +233,7 @@
    6.65          
    6.66          assert p != null : "We should get our person back: " + p;
    6.67          assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
    6.68 -//        assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    6.69 +        assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    6.70      }
    6.71      
    6.72      @Http(@Http.Resource(
    6.73 @@ -243,7 +256,7 @@
    6.74          
    6.75          assert p != null : "We should get our person back: " + p;
    6.76          assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
    6.77 -//        assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    6.78 +        assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
    6.79      }
    6.80      
    6.81      @Http(@Http.Resource(
    6.82 @@ -269,7 +282,7 @@
    6.83          
    6.84          assert p != null : "We should get our person back: " + p;
    6.85          assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
    6.86 -//        assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    6.87 +        assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
    6.88      }
    6.89      
    6.90      @Http(@Http.Resource(
    6.91 @@ -292,6 +305,38 @@
    6.92          assert js.getFetchedCount() == 6 : "1 + 2 + 3 is " + js.getFetchedCount();
    6.93      }
    6.94      
    6.95 +    @OnReceive(url="/{url}")
    6.96 +    static void fetchPeopleSex(People p, JSONik model) {
    6.97 +        model.setFetchedCount(1);
    6.98 +        model.getFetchedSex().addAll(p.getSex());
    6.99 +    }
   6.100 +    
   6.101 +    
   6.102 +    @Http(@Http.Resource(
   6.103 +        content = "{'sex':['FEMALE', 'MALE', 'MALE']}", 
   6.104 +        path="/people.json", 
   6.105 +        mimeType = "application/json"
   6.106 +    ))
   6.107 +    @BrwsrTest public void loadAndParseArrayOfEnums() throws InterruptedException {
   6.108 +        if (js == null) {
   6.109 +            js = new JSONik();
   6.110 +            js.applyBindings();
   6.111 +        
   6.112 +            js.fetchPeopleSex("people.json");
   6.113 +        }
   6.114 +        
   6.115 +        if (0 == js.getFetchedCount()) {
   6.116 +            throw new InterruptedException();
   6.117 +        }
   6.118 +
   6.119 +        assert js.getFetchedCount() == 1 : "Loaded";
   6.120 +        
   6.121 +        assert js.getFetchedSex().size() == 3 : "Three values " + js.getFetchedSex();
   6.122 +        assert js.getFetchedSex().get(0) == Sex.FEMALE : "Female first " + js.getFetchedSex();
   6.123 +        assert js.getFetchedSex().get(1) == Sex.MALE : "male 2nd " + js.getFetchedSex();
   6.124 +        assert js.getFetchedSex().get(2) == Sex.MALE : "male 3rd " + js.getFetchedSex();
   6.125 +    }
   6.126 +    
   6.127      @Http(@Http.Resource(
   6.128          content = "[{'firstName': 'Gitar', 'sex': 'FEMALE'},"
   6.129          + "{'firstName': 'Peter', 'sex': 'MALE'}"
   6.130 @@ -315,7 +360,7 @@
   6.131          assert js.getFetchedCount() == 2 : "We got two values: " + js.getFetchedCount();
   6.132          assert p != null : "We should get our person back: " + p;
   6.133          assert "Gitar".equals(p.getFirstName()) : "Expecting Gitar: " + p.getFirstName();
   6.134 -//        assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
   6.135 +        assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
   6.136      }
   6.137  
   6.138      @Factory public static Object[] create() {
     7.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java	Mon Apr 08 19:33:08 2013 +0200
     7.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java	Wed Apr 10 12:23:17 2013 +0200
     7.3 @@ -19,6 +19,8 @@
     7.4  
     7.5  import java.io.IOException;
     7.6  import java.util.Locale;
     7.7 +import javax.tools.Diagnostic;
     7.8 +import javax.tools.JavaFileObject;
     7.9  import static org.testng.Assert.*;
    7.10  import org.testng.annotations.Test;
    7.11  
    7.12 @@ -39,11 +41,12 @@
    7.13              + "}\n";
    7.14          
    7.15          Compile c = Compile.create(html, code);
    7.16 -        assertEquals(c.getErrors().size(), 1, "One error: " + c.getErrors());
    7.17 -        
    7.18 -        String msg = c.getErrors().get(0).getMessage(Locale.ENGLISH);
    7.19 -        if (!msg.contains("Runnable")) {
    7.20 -            fail("Should contain warning about Runnable: " + msg);
    7.21 +        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
    7.22 +        for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
    7.23 +            String msg = e.getMessage(Locale.ENGLISH);
    7.24 +            if (!msg.contains("Runnable")) {
    7.25 +                fail("Should contain warning about Runnable: " + msg);
    7.26 +            }
    7.27          }
    7.28      }
    7.29      
     8.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java	Mon Apr 08 19:33:08 2013 +0200
     8.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PersonImpl.java	Wed Apr 10 12:23:17 2013 +0200
     8.3 @@ -52,9 +52,11 @@
     8.4      }
     8.5      
     8.6      @Model(className = "People", properties = {
     8.7 -        @Property(array = true, name = "info", type = PersonImpl.class),
     8.8 +        @Property(array = true, name = "info", type = Person.class),
     8.9          @Property(array = true, name = "nicknames", type = String.class),
    8.10 -        @Property(array = true, name = "age", type = int.class),})
    8.11 +        @Property(array = true, name = "age", type = int.class),
    8.12 +        @Property(array = true, name = "sex", type = Sex.class)
    8.13 +    })
    8.14      public class PeopleImpl {
    8.15      }
    8.16  }
     9.1 --- a/javaquery/demo-twitter/pom.xml	Mon Apr 08 19:33:08 2013 +0200
     9.2 +++ b/javaquery/demo-twitter/pom.xml	Wed Apr 10 12:23:17 2013 +0200
     9.3 @@ -44,7 +44,7 @@
     9.4  
     9.5    <properties>
     9.6      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     9.7 -    <bck2brwsr.obfuscationlevel>FULL</bck2brwsr.obfuscationlevel>
     9.8 +    <bck2brwsr.obfuscationlevel>MINIMAL</bck2brwsr.obfuscationlevel>
     9.9    </properties>
    9.10    <build>
    9.11        <plugins>
    10.1 --- a/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java	Mon Apr 08 19:33:08 2013 +0200
    10.2 +++ b/javaquery/demo-twitter/src/main/java/org/apidesign/bck2brwsr/demo/twitter/TwitterClient.java	Wed Apr 10 12:23:17 2013 +0200
    10.3 @@ -29,11 +29,11 @@
    10.4   * @author Jaroslav Tulach
    10.5   */
    10.6  @Page(xhtml="index.html", className="TwitterModel", properties={
    10.7 -    @Property(name="savedLists", type=TwitterClient.Twttrs.class, array = true),
    10.8 +    @Property(name="savedLists", type=Tweeters.class, array = true),
    10.9      @Property(name="activeTweetersName", type=String.class),
   10.10      @Property(name="activeTweeters", type=String.class, array = true),
   10.11      @Property(name="userNameToAdd", type=String.class),
   10.12 -    @Property(name="currentTweets", type=TwitterClient.Twt.class, array = true)
   10.13 +    @Property(name="currentTweets", type=Tweet.class, array = true)
   10.14  })
   10.15  public class TwitterClient {
   10.16      @Model(className = "Tweeters", properties = {
    11.1 --- a/rt/emul/mini/src/main/java/java/lang/Enum.java	Mon Apr 08 19:33:08 2013 +0200
    11.2 +++ b/rt/emul/mini/src/main/java/java/lang/Enum.java	Wed Apr 10 12:23:17 2013 +0200
    11.3 @@ -27,6 +27,7 @@
    11.4  
    11.5  import java.io.Serializable;
    11.6  import java.io.IOException;
    11.7 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
    11.8  
    11.9  /**
   11.10   * This is the common base class of all Java language enumeration types.
   11.11 @@ -225,15 +226,17 @@
   11.12       */
   11.13      public static <T extends Enum<T>> T valueOf(Class<T> enumType,
   11.14                                                  String name) {
   11.15 -        throw new UnsupportedOperationException();
   11.16 -//        T result = enumType.enumConstantDirectory().get(name);
   11.17 -//        if (result != null)
   11.18 -//            return result;
   11.19 -//        if (name == null)
   11.20 -//            throw new NullPointerException("Name is null");
   11.21 -//        throw new IllegalArgumentException(
   11.22 -//            "No enum constant " + enumType.getCanonicalName() + "." + name);
   11.23 +        for (Object o : values(enumType)) {
   11.24 +            T t = enumType.cast(o);
   11.25 +            if (name.equals(((Enum)t).name)) {
   11.26 +                return t;
   11.27 +            }
   11.28 +        }
   11.29 +        throw new IllegalArgumentException();
   11.30      }
   11.31 +    
   11.32 +    @JavaScriptBody(args = { "enumType" }, body = "return enumType.cnstr.$VALUES;")
   11.33 +    private static native Object[] values(Class<?> enumType);
   11.34  
   11.35      /**
   11.36       * enum classes cannot have finalize methods.
    12.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Mon Apr 08 19:33:08 2013 +0200
    12.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Wed Apr 10 12:23:17 2013 +0200
    12.3 @@ -1940,7 +1940,7 @@
    12.4          final String type = jc.getClassName(indx);
    12.5          if (!type.startsWith("[")) {
    12.6              emit(out,
    12.7 -                 "if (@1 !== null && !@1.$instOf_@2) throw {};",
    12.8 +                 "if (@1 !== null && !@1.$instOf_@2) throw vm.java_lang_ClassCastException(true);",
    12.9                   smapper.getA(0), type.replace('/', '_'));
   12.10          } else {
   12.11              emit(out, "vm.java_lang_Class(false).forName__Ljava_lang_Class_2Ljava_lang_String_2('@2').cast__Ljava_lang_Object_2Ljava_lang_Object_2(@1);",
    13.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java	Mon Apr 08 19:33:08 2013 +0200
    13.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java	Wed Apr 10 12:23:17 2013 +0200
    13.3 @@ -207,5 +207,12 @@
    13.4              true
    13.5          );
    13.6      }
    13.7 +    
    13.8 +    @Test public void valueOfEnum() throws Exception {
    13.9 +        assertExec("can get value of enum", Classes.class,
   13.10 +            "valueEnum__Ljava_lang_String_2Ljava_lang_String_2",
   13.11 +            "TWO", "TWO"
   13.12 +        );
   13.13 +    }
   13.14  
   13.15  }
    14.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java	Mon Apr 08 19:33:08 2013 +0200
    14.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java	Wed Apr 10 12:23:17 2013 +0200
    14.3 @@ -230,4 +230,7 @@
    14.4          return Application.class.isAssignableFrom(MyApplication.class);
    14.5      }
    14.6      
    14.7 +    public static String valueEnum(String v) {
    14.8 +        return ClassesMarker.E.valueOf(v).toString();
    14.9 +    }
   14.10  }
    15.1 --- a/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java	Mon Apr 08 19:33:08 2013 +0200
    15.2 +++ b/rt/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java	Wed Apr 10 12:23:17 2013 +0200
    15.3 @@ -103,6 +103,15 @@
    15.4          StaticUse.class.getMethod("instanceMethod").invoke(null);
    15.5          return "should not happen";
    15.6      }
    15.7 +    
    15.8 +    @Compare public String classCastException() {
    15.9 +        try {
   15.10 +            Integer i = (Integer)StaticUseSub.getNonNull();
   15.11 +            return "" + i.intValue();
   15.12 +        } catch (ClassCastException ex) {
   15.13 +            return ex.getClass().getName();
   15.14 +        }
   15.15 +    }
   15.16  
   15.17      @Compare public String methodThatThrowsException() throws Exception {
   15.18          StaticUse.class.getMethod("instanceMethod").invoke(new StaticUse());