json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java
branchnetbeans
changeset 362 92fb71afdc0e
parent 361 700087d2a5d3
child 364 2739565c7a45
     1.1 --- a/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Mon Dec 16 15:48:09 2013 +0100
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,1767 +0,0 @@
     1.4 -/**
     1.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     1.6 - *
     1.7 - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
     1.8 - *
     1.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    1.10 - * Other names may be trademarks of their respective owners.
    1.11 - *
    1.12 - * The contents of this file are subject to the terms of either the GNU
    1.13 - * General Public License Version 2 only ("GPL") or the Common
    1.14 - * Development and Distribution License("CDDL") (collectively, the
    1.15 - * "License"). You may not use this file except in compliance with the
    1.16 - * License. You can obtain a copy of the License at
    1.17 - * http://www.netbeans.org/cddl-gplv2.html
    1.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    1.19 - * specific language governing permissions and limitations under the
    1.20 - * License.  When distributing the software, include this License Header
    1.21 - * Notice in each file and include the License file at
    1.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    1.23 - * particular file as subject to the "Classpath" exception as provided
    1.24 - * by Oracle in the GPL Version 2 section of the License file that
    1.25 - * accompanied this code. If applicable, add the following below the
    1.26 - * License Header, with the fields enclosed by brackets [] replaced by
    1.27 - * your own identifying information:
    1.28 - * "Portions Copyrighted [year] [name of copyright owner]"
    1.29 - *
    1.30 - * Contributor(s):
    1.31 - *
    1.32 - * The Original Software is NetBeans. The Initial Developer of the Original
    1.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
    1.34 - *
    1.35 - * If you wish your version of this file to be governed by only the CDDL
    1.36 - * or only the GPL Version 2, indicate your decision by adding
    1.37 - * "[Contributor] elects to include this software in this distribution
    1.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
    1.39 - * single choice of license, a recipient has the option to distribute
    1.40 - * your version of this file under either the CDDL, the GPL Version 2 or
    1.41 - * to extend the choice of license to its licensees as provided above.
    1.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
    1.43 - * Version 2 license, then the option applies only if the new code is
    1.44 - * made subject to such option by the copyright holder.
    1.45 - */
    1.46 -package org.apidesign.html.json.impl;
    1.47 -
    1.48 -import java.io.IOException;
    1.49 -import java.io.OutputStreamWriter;
    1.50 -import java.io.StringWriter;
    1.51 -import java.io.Writer;
    1.52 -import java.lang.annotation.AnnotationTypeMismatchException;
    1.53 -import java.lang.annotation.IncompleteAnnotationException;
    1.54 -import java.lang.reflect.Method;
    1.55 -import java.util.ArrayList;
    1.56 -import java.util.Arrays;
    1.57 -import java.util.Collection;
    1.58 -import java.util.Collections;
    1.59 -import java.util.HashMap;
    1.60 -import java.util.HashSet;
    1.61 -import java.util.LinkedHashSet;
    1.62 -import java.util.List;
    1.63 -import java.util.Map;
    1.64 -import java.util.ResourceBundle;
    1.65 -import java.util.Set;
    1.66 -import java.util.WeakHashMap;
    1.67 -import java.util.logging.Level;
    1.68 -import java.util.logging.Logger;
    1.69 -import javax.annotation.processing.AbstractProcessor;
    1.70 -import javax.annotation.processing.Completion;
    1.71 -import javax.annotation.processing.Completions;
    1.72 -import javax.annotation.processing.ProcessingEnvironment;
    1.73 -import javax.annotation.processing.Processor;
    1.74 -import javax.annotation.processing.RoundEnvironment;
    1.75 -import javax.annotation.processing.SupportedAnnotationTypes;
    1.76 -import javax.annotation.processing.SupportedSourceVersion;
    1.77 -import javax.lang.model.SourceVersion;
    1.78 -import javax.lang.model.element.AnnotationMirror;
    1.79 -import javax.lang.model.element.AnnotationValue;
    1.80 -import javax.lang.model.element.Element;
    1.81 -import javax.lang.model.element.ElementKind;
    1.82 -import javax.lang.model.element.ExecutableElement;
    1.83 -import javax.lang.model.element.Modifier;
    1.84 -import javax.lang.model.element.PackageElement;
    1.85 -import javax.lang.model.element.TypeElement;
    1.86 -import javax.lang.model.element.VariableElement;
    1.87 -import javax.lang.model.type.ArrayType;
    1.88 -import javax.lang.model.type.DeclaredType;
    1.89 -import javax.lang.model.type.MirroredTypeException;
    1.90 -import javax.lang.model.type.TypeKind;
    1.91 -import javax.lang.model.type.TypeMirror;
    1.92 -import javax.lang.model.util.Elements;
    1.93 -import javax.lang.model.util.Types;
    1.94 -import javax.tools.Diagnostic;
    1.95 -import javax.tools.FileObject;
    1.96 -import net.java.html.json.ComputedProperty;
    1.97 -import net.java.html.json.Model;
    1.98 -import net.java.html.json.Function;
    1.99 -import net.java.html.json.ModelOperation;
   1.100 -import net.java.html.json.OnPropertyChange;
   1.101 -import net.java.html.json.OnReceive;
   1.102 -import net.java.html.json.Property;
   1.103 -import org.openide.util.lookup.ServiceProvider;
   1.104 -
   1.105 -/** Annotation processor to process {@link Model @Model} annotations and
   1.106 - * generate appropriate model classes.
   1.107 - *
   1.108 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   1.109 - */
   1.110 -@ServiceProvider(service=Processor.class)
   1.111 -@SupportedSourceVersion(SourceVersion.RELEASE_6)
   1.112 -@SupportedAnnotationTypes({
   1.113 -    "net.java.html.json.Model",
   1.114 -    "net.java.html.json.ModelOperation",
   1.115 -    "net.java.html.json.Function",
   1.116 -    "net.java.html.json.OnReceive",
   1.117 -    "net.java.html.json.OnPropertyChange",
   1.118 -    "net.java.html.json.ComputedProperty",
   1.119 -    "net.java.html.json.Property"
   1.120 -})
   1.121 -public final class ModelProcessor extends AbstractProcessor {
   1.122 -    private static final Logger LOG = Logger.getLogger(ModelProcessor.class.getName());
   1.123 -    private final Map<Element,String> models = new WeakHashMap<Element,String>();
   1.124 -    private final Map<Element,Prprt[]> verify = new WeakHashMap<Element,Prprt[]>();
   1.125 -    @Override
   1.126 -    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   1.127 -        boolean ok = true;
   1.128 -        for (Element e : roundEnv.getElementsAnnotatedWith(Model.class)) {
   1.129 -            if (!processModel(e)) {
   1.130 -                ok = false;
   1.131 -            }
   1.132 -        }
   1.133 -        if (roundEnv.processingOver()) {
   1.134 -            models.clear();
   1.135 -            for (Map.Entry<Element, Prprt[]> entry : verify.entrySet()) {
   1.136 -                TypeElement te = (TypeElement)entry.getKey();
   1.137 -                String fqn = processingEnv.getElementUtils().getBinaryName(te).toString();
   1.138 -                Element finalElem = processingEnv.getElementUtils().getTypeElement(fqn);
   1.139 -                if (finalElem == null) {
   1.140 -                    continue;
   1.141 -                }
   1.142 -                Prprt[] props;
   1.143 -                Model m = finalElem.getAnnotation(Model.class);
   1.144 -                if (m == null) {
   1.145 -                    continue;
   1.146 -                }
   1.147 -                props = Prprt.wrap(processingEnv, finalElem, m.properties());
   1.148 -                for (Prprt p : props) {
   1.149 -                    boolean[] isModel = { false };
   1.150 -                    boolean[] isEnum = { false };
   1.151 -                    boolean[] isPrimitive = { false };
   1.152 -                    String t = checkType(p, isModel, isEnum, isPrimitive);
   1.153 -                    if (isEnum[0]) {
   1.154 -                        continue;
   1.155 -                    }
   1.156 -                    if (isPrimitive[0]) {
   1.157 -                        continue;
   1.158 -                    }
   1.159 -                    if (isModel[0]) {
   1.160 -                        continue;
   1.161 -                    }
   1.162 -                    if ("java.lang.String".equals(t)) {
   1.163 -                        continue;
   1.164 -                    }
   1.165 -                    error("The type " + t + " should be defined by @Model annotation", entry.getKey());
   1.166 -                }
   1.167 -            }
   1.168 -            verify.clear();
   1.169 -        }
   1.170 -        return ok;
   1.171 -    }
   1.172 -
   1.173 -    private void error(String msg, Element e) {
   1.174 -        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
   1.175 -    }
   1.176 -    
   1.177 -    private boolean processModel(Element e) {
   1.178 -        boolean ok = true;
   1.179 -        Model m = e.getAnnotation(Model.class);
   1.180 -        if (m == null) {
   1.181 -            return true;
   1.182 -        }
   1.183 -        String pkg = findPkgName(e);
   1.184 -        Writer w;
   1.185 -        String className = m.className();
   1.186 -        models.put(e, className);
   1.187 -        try {
   1.188 -            StringWriter body = new StringWriter();
   1.189 -            List<String> propsGetSet = new ArrayList<String>();
   1.190 -            List<String> functions = new ArrayList<String>();
   1.191 -            Map<String, Collection<String>> propsDeps = new HashMap<String, Collection<String>>();
   1.192 -            Map<String, Collection<String>> functionDeps = new HashMap<String, Collection<String>>();
   1.193 -            Prprt[] props = createProps(e, m.properties());
   1.194 -            
   1.195 -            if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
   1.196 -                ok = false;
   1.197 -            }
   1.198 -            if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
   1.199 -                ok = false;
   1.200 -            }
   1.201 -            if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) {
   1.202 -                ok = false;
   1.203 -            }
   1.204 -            if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
   1.205 -                ok = false;
   1.206 -            }
   1.207 -            if (!generateReceive(e, body, className, e.getEnclosedElements(), functions)) {
   1.208 -                ok = false;
   1.209 -            }
   1.210 -            if (!generateOperation(e, body, className, e.getEnclosedElements())) {
   1.211 -                ok = false;
   1.212 -            }
   1.213 -            FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
   1.214 -            w = new OutputStreamWriter(java.openOutputStream());
   1.215 -            try {
   1.216 -                w.append("package " + pkg + ";\n");
   1.217 -                w.append("import net.java.html.json.*;\n");
   1.218 -                w.append("public final class ").append(className).append(" implements Cloneable {\n");
   1.219 -                w.append("  private boolean locked;\n");
   1.220 -                w.append("  private net.java.html.BrwsrCtx context;\n");
   1.221 -                w.append("  private org.apidesign.html.json.impl.Bindings[] ko = { null };\n");
   1.222 -                w.append(body.toString());
   1.223 -                w.append("  private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
   1.224 -                w.append("  private ").append(className).append("(net.java.html.BrwsrCtx context) {\n");
   1.225 -                w.append("    this.context = context;\n");
   1.226 -                w.append("  };\n");
   1.227 -                w.append("  public ").append(className).append("() {\n");
   1.228 -                w.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
   1.229 -                for (Prprt p : props) {
   1.230 -                    if (p.array()) {
   1.231 -                        continue;
   1.232 -                    }
   1.233 -                    boolean[] isModel = {false};
   1.234 -                    boolean[] isEnum = {false};
   1.235 -                    boolean isPrimitive[] = {false};
   1.236 -                    String tn = checkType(p, isModel, isEnum, isPrimitive);
   1.237 -                    if (isModel[0]) {
   1.238 -                        w.write("    prop_" + p.name() + " = new " + tn + "();\n");
   1.239 -                    }
   1.240 -                }
   1.241 -                w.append("  };\n");
   1.242 -                if (props.length > 0) {
   1.243 -                    w.append("  public ").append(className).append("(");
   1.244 -                    Prprt firstArray = null;
   1.245 -                    String sep = "";
   1.246 -                    for (Prprt p : props) {
   1.247 -                        if (p.array()) {
   1.248 -                            if (firstArray == null) {
   1.249 -                                firstArray = p;
   1.250 -                            }
   1.251 -                            continue;
   1.252 -                        }
   1.253 -                        String tn = typeName(e, p);
   1.254 -                        w.write(sep);
   1.255 -                        w.write(tn);
   1.256 -                        w.write(" " + p.name());
   1.257 -                        sep = ", ";
   1.258 -                    }
   1.259 -                    if (firstArray != null) {
   1.260 -                        String tn;
   1.261 -                        boolean[] isModel = {false};
   1.262 -                        boolean[] isEnum = {false};
   1.263 -                        boolean isPrimitive[] = {false};
   1.264 -                        tn = checkType(firstArray, isModel, isEnum, isPrimitive);
   1.265 -                        w.write(sep);
   1.266 -                        w.write(tn);
   1.267 -                        w.write("... " + firstArray.name());
   1.268 -                    }
   1.269 -                    w.append(") {\n");
   1.270 -                    w.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
   1.271 -                    for (Prprt p : props) {
   1.272 -                        if (p.array()) {
   1.273 -                            continue;
   1.274 -                        }
   1.275 -                        w.write("    this.prop_" + p.name() + " = " + p.name() + ";\n");
   1.276 -                    }
   1.277 -                    if (firstArray != null) {
   1.278 -                        w.write("    this.prop_" + firstArray.name() + ".init(" + firstArray.name() + ");\n");
   1.279 -                    }
   1.280 -                    w.append("  };\n");
   1.281 -                }
   1.282 -                w.append("  private org.apidesign.html.json.impl.Bindings intKnckt() {\n");
   1.283 -                w.append("    if (ko[0] != null) return ko[0];\n");
   1.284 -                w.append("    ko[0] = org.apidesign.html.json.impl.Bindings.apply(context, this);\n");
   1.285 -                {
   1.286 -                    w.append("    org.apidesign.html.json.spi.PropertyBinding[] propArr = {\n");
   1.287 -                    for (int i = 0; i < propsGetSet.size(); i += 5) {
   1.288 -                        w.append("      ko[0].registerProperty(\"").append(propsGetSet.get(i)).append("\", this, new P(");
   1.289 -                        w.append((i / 5) + "), " + (propsGetSet.get(i + 2) == null) + "),\n");
   1.290 -                    }
   1.291 -                    w.append("    };\n");
   1.292 -                }
   1.293 -                {
   1.294 -                    w.append("    org.apidesign.html.json.spi.FunctionBinding[] funcArr = {\n");
   1.295 -                    for (int i = 0; i < functions.size(); i += 2) {
   1.296 -                        w.append("      ko[0].registerFunction(\"").append(functions.get(i)).append("\", this, new P(");
   1.297 -                        w.append((i / 2) + ")),\n");
   1.298 -                    }
   1.299 -                    w.append("    };\n");
   1.300 -                }
   1.301 -                w.append("    ko[0].finish(this, propArr, funcArr);\n");
   1.302 -                w.append("    return ko[0];\n");
   1.303 -                w.append("  };\n");
   1.304 -                w.append("  private static final class P implements org.apidesign.html.json.impl.SetAndGet<" + className + ">,\n");
   1.305 -                w.append("  org.apidesign.html.json.impl.Callback<" + className + ">,\n");
   1.306 -                w.append("  org.apidesign.html.json.impl.FromJSON<" + className + "> {\n");
   1.307 -                w.append("    private final int type;\n");
   1.308 -                w.append("    P(int t) { type = t; };\n");
   1.309 -                w.append("    public void setValue(" + className + " data, Object value) {\n");
   1.310 -                w.append("      switch (type) {\n");
   1.311 -                for (int i = 0; i < propsGetSet.size(); i += 5) {
   1.312 -                    final String set = propsGetSet.get(i + 2);
   1.313 -                    String tn = propsGetSet.get(i + 4);
   1.314 -                    String btn = findBoxedType(tn);
   1.315 -                    if (btn != null) {
   1.316 -                        tn = btn;
   1.317 -                    }
   1.318 -                    if (set != null) {
   1.319 -                        w.append("        case " + (i / 5) + ": data." + strip(set) + "(org.apidesign.html.json.impl.JSON.extractValue(" + tn + ".class, value)); return;\n");
   1.320 -                    }
   1.321 -                }
   1.322 -                w.append("      }\n");
   1.323 -                w.append("    }\n");
   1.324 -                w.append("    public Object getValue(" + className + " data) {\n");
   1.325 -                w.append("      switch (type) {\n");
   1.326 -                for (int i = 0; i < propsGetSet.size(); i += 5) {
   1.327 -                    final String get = propsGetSet.get(i + 1);
   1.328 -                    if (get != null) {
   1.329 -                        w.append("        case " + (i / 5) + ": return data." + strip(get) + "();\n");
   1.330 -                    }
   1.331 -                }
   1.332 -                w.append("      }\n");
   1.333 -                w.append("      throw new UnsupportedOperationException();\n");
   1.334 -                w.append("    }\n");
   1.335 -                w.append("    public void call(" + className + " model, Object data, Object ev) {\n");
   1.336 -                w.append("      switch (type) {\n");
   1.337 -                for (int i = 0; i < functions.size(); i += 2) {
   1.338 -                    final String name = functions.get(i);
   1.339 -                    w.append("        case " + (i / 2) + ": model." + name + "(data, ev); return;\n");
   1.340 -                }
   1.341 -                w.append("      }\n");
   1.342 -                w.append("      throw new UnsupportedOperationException();\n");
   1.343 -                w.append("    }\n");
   1.344 -                w.append("    public Class<" + className + "> factoryFor() { return " + className + ".class; }\n");
   1.345 -                w.append("    public " + className + " read(net.java.html.BrwsrCtx c, Object json) { return new " + className + "(c, json); }\n");
   1.346 -                w.append("    public " + className + " cloneTo(Object o, net.java.html.BrwsrCtx c) { return ((" + className + ")o).clone(c); }\n");
   1.347 -                w.append("  }\n");
   1.348 -                w.append("  static { org.apidesign.html.json.impl.JSON.register(new P(0)); }\n");
   1.349 -                w.append("  private ").append(className).append("(net.java.html.BrwsrCtx c, Object json) {\n");
   1.350 -                w.append("    this.context = c;\n");
   1.351 -                int values = 0;
   1.352 -                for (int i = 0; i < propsGetSet.size(); i += 5) {
   1.353 -                    Prprt p = findPrprt(props, propsGetSet.get(i));
   1.354 -                    if (p == null) {
   1.355 -                        continue;
   1.356 -                    }
   1.357 -                    values++;
   1.358 -                }
   1.359 -                w.append("    Object[] ret = new Object[" + values + "];\n");
   1.360 -                w.append("    org.apidesign.html.json.impl.JSON.extract(context, json, new String[] {\n");
   1.361 -                for (int i = 0; i < propsGetSet.size(); i += 5) {
   1.362 -                    Prprt p = findPrprt(props, propsGetSet.get(i));
   1.363 -                    if (p == null) {
   1.364 -                        continue;
   1.365 -                    }
   1.366 -                    w.append("      \"").append(propsGetSet.get(i)).append("\",\n");
   1.367 -                }
   1.368 -                w.append("    }, ret);\n");
   1.369 -                for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i += 5) {
   1.370 -                    final String pn = propsGetSet.get(i);
   1.371 -                    Prprt p = findPrprt(props, pn);
   1.372 -                    if (p == null) {
   1.373 -                        continue;
   1.374 -                    }
   1.375 -                    boolean[] isModel = { false };
   1.376 -                    boolean[] isEnum = { false };
   1.377 -                    boolean isPrimitive[] = { false };
   1.378 -                    String type = checkType(props[prop++], isModel, isEnum, isPrimitive);
   1.379 -                    if (p.array()) {
   1.380 -                        w.append("    if (ret[" + cnt + "] instanceof Object[]) {\n");
   1.381 -                        w.append("      for (Object e : ((Object[])ret[" + cnt + "])) {\n");
   1.382 -                        if (isModel[0]) {
   1.383 -                            w.append("        this.prop_").append(pn).append(".add(org.apidesign.html.json.impl.JSON.read");
   1.384 -                            w.append("(c, " + type + ".class, e));\n");
   1.385 -                        } else if (isEnum[0]) {
   1.386 -                            w.append("        this.prop_").append(pn);
   1.387 -                            w.append(".add(e == null ? null : ");
   1.388 -                            w.append(type).append(".valueOf(org.apidesign.html.json.impl.JSON.stringValue(e)));\n");
   1.389 -                        } else {
   1.390 -                            if (isPrimitive(type)) {
   1.391 -                                w.append("        this.prop_").append(pn).append(".add(org.apidesign.html.json.impl.JSON.numberValue(e).");
   1.392 -                                w.append(type).append("Value());\n");
   1.393 -                            } else {
   1.394 -                                w.append("        this.prop_").append(pn).append(".add((");
   1.395 -                                w.append(type).append(")e);\n");
   1.396 -                            }
   1.397 -                        }
   1.398 -                        w.append("      }\n");
   1.399 -                        w.append("    }\n");
   1.400 -                    } else {
   1.401 -                        if (isEnum[0]) {
   1.402 -                            w.append("    try {\n");
   1.403 -                            w.append("    this.prop_").append(pn);
   1.404 -                            w.append(" = ret[" + cnt + "] == null ? null : ");
   1.405 -                            w.append(type).append(".valueOf(org.apidesign.html.json.impl.JSON.stringValue(ret[" + cnt + "]));\n");
   1.406 -                            w.append("    } catch (IllegalArgumentException ex) {\n");
   1.407 -                            w.append("      ex.printStackTrace();\n");
   1.408 -                            w.append("    }\n");
   1.409 -                        } else if (isPrimitive(type)) {
   1.410 -                            w.append("    this.prop_").append(pn);
   1.411 -                            w.append(" = ret[" + cnt + "] == null ? ");
   1.412 -                            if ("char".equals(type)) {
   1.413 -                                w.append("0 : (org.apidesign.html.json.impl.JSON.charValue(");
   1.414 -                            } else if ("boolean".equals(type)) {
   1.415 -                                w.append("false : (org.apidesign.html.json.impl.JSON.boolValue(");
   1.416 -                            } else {
   1.417 -                                w.append("0 : (org.apidesign.html.json.impl.JSON.numberValue(");
   1.418 -                            }
   1.419 -                            w.append("ret[" + cnt + "])).");
   1.420 -                            w.append(type).append("Value();\n");
   1.421 -                        } else if (isModel[0]) {
   1.422 -                            w.append("    this.prop_").append(pn).append(" = org.apidesign.html.json.impl.JSON.read");
   1.423 -                            w.append("(c, " + type + ".class, ");
   1.424 -                            w.append("ret[" + cnt + "]);\n");
   1.425 -                        }else {
   1.426 -                            w.append("    this.prop_").append(pn);
   1.427 -                            w.append(" = (").append(type).append(')');
   1.428 -                            w.append("ret[" + cnt + "];\n");
   1.429 -                        }
   1.430 -                    }
   1.431 -                    cnt++;
   1.432 -                }
   1.433 -                w.append("  };\n");
   1.434 -                writeToString(props, w);
   1.435 -                writeClone(className, props, w);
   1.436 -                w.write("  /** Activates this model instance in the current {@link \n"
   1.437 -                    + "net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}. \n"
   1.438 -                    + "In case of using Knockout technology, this means to \n"
   1.439 -                    + "bind JSON like data in this model instance with Knockout tags in \n"
   1.440 -                    + "the surrounding HTML page.\n"
   1.441 -                    + "*/\n"
   1.442 -                );
   1.443 -                w.write("  public " + className + " applyBindings() {\n");
   1.444 -                w.write("    intKnckt().applyBindings();\n");
   1.445 -                w.write("    return this;\n");
   1.446 -                w.write("  }\n");
   1.447 -                w.write("  public boolean equals(Object o) {\n");
   1.448 -                w.write("    if (o == this) return true;\n");
   1.449 -                w.write("    if (o instanceof org.apidesign.html.json.impl.WrapperObject) {\n");
   1.450 -                w.write("      ((org.apidesign.html.json.impl.WrapperObject)o).setRealObject(intKnckt().koData());\n");
   1.451 -                w.write("      return false;\n");
   1.452 -                w.write("    }\n");
   1.453 -                w.write("    if (!(o instanceof " + className + ")) return false;\n");
   1.454 -                w.write("    " + className + " p = (" + className + ")o;\n");
   1.455 -                for (Prprt p : props) {
   1.456 -                    w.write("    if (!org.apidesign.html.json.impl.JSON.isSame(prop_" + p.name() + ", p.prop_" + p.name() + ")) return false;\n");
   1.457 -                }
   1.458 -                w.write("    return true;\n");
   1.459 -                w.write("  }\n");
   1.460 -                w.write("  public int hashCode() {\n");
   1.461 -                w.write("    int h = " + className + ".class.getName().hashCode();\n");
   1.462 -                for (Prprt p : props) {
   1.463 -                    w.write("    h = org.apidesign.html.json.impl.JSON.hashPlus(prop_" + p.name() + ", h);\n");
   1.464 -                }
   1.465 -                w.write("    return h;\n");
   1.466 -                w.write("  }\n");
   1.467 -                w.write("}\n");
   1.468 -            } finally {
   1.469 -                w.close();
   1.470 -            }
   1.471 -        } catch (IOException ex) {
   1.472 -            error("Can't create " + className + ".java", e);
   1.473 -            return false;
   1.474 -        }
   1.475 -        return ok;
   1.476 -    }
   1.477 -    
   1.478 -    private boolean generateProperties(
   1.479 -        Element where,
   1.480 -        Writer w, Prprt[] properties,
   1.481 -        Collection<String> props, 
   1.482 -        Map<String,Collection<String>> deps,
   1.483 -        Map<String,Collection<String>> functionDeps
   1.484 -    ) throws IOException {
   1.485 -        boolean ok = true;
   1.486 -        for (Prprt p : properties) {
   1.487 -            final String tn;
   1.488 -            tn = typeName(where, p);
   1.489 -            String[] gs = toGetSet(p.name(), tn, p.array());
   1.490 -            String castTo;
   1.491 -            
   1.492 -            if (p.array()) {
   1.493 -                w.write("  private org.apidesign.html.json.impl.JSONList<" + tn + "> prop_" + p.name() + " = new org.apidesign.html.json.impl.JSONList<" + tn + ">(ko, \""
   1.494 -                    + p.name() + "\"");
   1.495 -                Collection<String> dependants = deps.get(p.name());
   1.496 -                if (dependants != null) {
   1.497 -                    for (String depProp : dependants) {
   1.498 -                        w.write(", ");
   1.499 -                        w.write('\"');
   1.500 -                        w.write(depProp);
   1.501 -                        w.write('\"');
   1.502 -                    }
   1.503 -                }
   1.504 -                w.write(")");
   1.505 -                
   1.506 -                dependants = functionDeps.get(p.name());
   1.507 -                if (dependants != null) {
   1.508 -                    w.write(".onChange(new Runnable() { public void run() {\n");
   1.509 -                    for (String call : dependants) {
   1.510 -                        w.append("  ").append(call);
   1.511 -                    }
   1.512 -                    w.write("  }})");
   1.513 -                }
   1.514 -                w.write(";\n");
   1.515 -            
   1.516 -                castTo = "java.util.List";
   1.517 -                w.write("  public java.util.List<" + tn + "> " + gs[0] + "() {\n");
   1.518 -                w.write("    if (locked) throw new IllegalStateException();\n");
   1.519 -                w.write("    return prop_" + p.name() + ";\n");
   1.520 -                w.write("  }\n");
   1.521 -            } else {
   1.522 -                castTo = tn;
   1.523 -                w.write("  private " + tn + " prop_" + p.name() + ";\n");
   1.524 -                w.write("  public " + tn + " " + gs[0] + "() {\n");
   1.525 -                w.write("    if (locked) throw new IllegalStateException();\n");
   1.526 -                w.write("    return prop_" + p.name() + ";\n");
   1.527 -                w.write("  }\n");
   1.528 -                w.write("  public void " + gs[1] + "(" + tn + " v) {\n");
   1.529 -                w.write("    if (locked) throw new IllegalStateException();\n");
   1.530 -                w.write("    if (org.apidesign.html.json.impl.JSON.isSame(prop_" + p.name() + ", v)) return;\n");
   1.531 -                w.write("    prop_" + p.name() + " = v;\n");
   1.532 -                w.write("    org.apidesign.html.json.impl.Bindings b = ko[0];\n");
   1.533 -                w.write("    if (b != null) {\n");
   1.534 -                w.write("      b.valueHasMutated(\"" + p.name() + "\");\n");
   1.535 -                Collection<String> dependants = deps.get(p.name());
   1.536 -                if (dependants != null) {
   1.537 -                    for (String depProp : dependants) {
   1.538 -                        w.write("      b.valueHasMutated(\"" + depProp + "\");\n");
   1.539 -                    }
   1.540 -                }
   1.541 -                w.write("    }\n");
   1.542 -                dependants = functionDeps.get(p.name());
   1.543 -                if (dependants != null) {
   1.544 -                    for (String call : dependants) {
   1.545 -                        w.append("  ").append(call);
   1.546 -                    }
   1.547 -                }
   1.548 -                w.write("  }\n");
   1.549 -            }
   1.550 -            
   1.551 -            props.add(p.name());
   1.552 -            props.add(gs[2]);
   1.553 -            props.add(gs[3]);
   1.554 -            props.add(gs[0]);
   1.555 -            props.add(castTo);
   1.556 -        }
   1.557 -        return ok;
   1.558 -    }
   1.559 -
   1.560 -    private boolean generateComputedProperties(
   1.561 -        Writer w, Prprt[] fixedProps,
   1.562 -        Collection<? extends Element> arr, Collection<String> props,
   1.563 -        Map<String,Collection<String>> deps
   1.564 -    ) throws IOException {
   1.565 -        boolean ok = true;
   1.566 -        for (Element e : arr) {
   1.567 -            if (e.getKind() != ElementKind.METHOD) {
   1.568 -                continue;
   1.569 -            }
   1.570 -            if (e.getAnnotation(ComputedProperty.class) == null) {
   1.571 -                continue;
   1.572 -            }
   1.573 -            if (!e.getModifiers().contains(Modifier.STATIC)) {
   1.574 -                error("Method " + e.getSimpleName() + " has to be static when annotated by @ComputedProperty", e);
   1.575 -                ok = false;
   1.576 -                continue;
   1.577 -            }
   1.578 -            ExecutableElement ee = (ExecutableElement)e;
   1.579 -            final TypeMirror rt = ee.getReturnType();
   1.580 -            final Types tu = processingEnv.getTypeUtils();
   1.581 -            TypeMirror ert = tu.erasure(rt);
   1.582 -            String tn = fqn(ert, ee);
   1.583 -            boolean array = false;
   1.584 -            final TypeMirror toCheck;
   1.585 -            if (tn.equals("java.util.List")) {
   1.586 -                array = true;
   1.587 -                toCheck = ((DeclaredType)rt).getTypeArguments().get(0);
   1.588 -            } else {
   1.589 -                toCheck = rt;
   1.590 -            }
   1.591 -            
   1.592 -            final String sn = ee.getSimpleName().toString();
   1.593 -            
   1.594 -            if (toCheck.getKind().isPrimitive()) {
   1.595 -                // OK
   1.596 -            } else {
   1.597 -                TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
   1.598 -                TypeMirror enumType = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
   1.599 -
   1.600 -                if (tu.isSubtype(toCheck, stringType)) {
   1.601 -                    // OK
   1.602 -                } else if (tu.isSubtype(tu.erasure(toCheck), tu.erasure(enumType))) {
   1.603 -                    // OK
   1.604 -                } else if (isModel(toCheck)) {
   1.605 -                    // OK
   1.606 -                } else {
   1.607 -                    ok = false;
   1.608 -                    error(sn + " cannot return " + toCheck, e);
   1.609 -                }
   1.610 -            }
   1.611 -            
   1.612 -            String[] gs = toGetSet(sn, tn, array);
   1.613 -            
   1.614 -            w.write("  public " + tn + " " + gs[0] + "() {\n");
   1.615 -            w.write("    if (locked) throw new IllegalStateException();\n");
   1.616 -            int arg = 0;
   1.617 -            for (VariableElement pe : ee.getParameters()) {
   1.618 -                final String dn = pe.getSimpleName().toString();
   1.619 -                
   1.620 -                if (!verifyPropName(pe, dn, fixedProps)) {
   1.621 -                    ok = false;
   1.622 -                }
   1.623 -                
   1.624 -                final String dt = fqn(pe.asType(), ee);
   1.625 -                String[] call = toGetSet(dn, dt, false);
   1.626 -                w.write("    " + dt + " arg" + (++arg) + " = ");
   1.627 -                w.write(call[0] + "();\n");
   1.628 -                
   1.629 -                Collection<String> depends = deps.get(dn);
   1.630 -                if (depends == null) {
   1.631 -                    depends = new LinkedHashSet<String>();
   1.632 -                    deps.put(dn, depends);
   1.633 -                }
   1.634 -                depends.add(sn);
   1.635 -            }
   1.636 -            w.write("    try {\n");
   1.637 -            w.write("      locked = true;\n");
   1.638 -            w.write("      return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
   1.639 -            String sep = "";
   1.640 -            for (int i = 1; i <= arg; i++) {
   1.641 -                w.write(sep);
   1.642 -                w.write("arg" + i);
   1.643 -                sep = ", ";
   1.644 -            }
   1.645 -            w.write(");\n");
   1.646 -            w.write("    } finally {\n");
   1.647 -            w.write("      locked = false;\n");
   1.648 -            w.write("    }\n");
   1.649 -            w.write("  }\n");
   1.650 -
   1.651 -            props.add(e.getSimpleName().toString());
   1.652 -            props.add(gs[2]);
   1.653 -            props.add(null);
   1.654 -            props.add(gs[0]);
   1.655 -            props.add(tn);
   1.656 -        }
   1.657 -        
   1.658 -        return ok;
   1.659 -    }
   1.660 -
   1.661 -    private static String[] toGetSet(String name, String type, boolean array) {
   1.662 -        String n = Character.toUpperCase(name.charAt(0)) + name.substring(1);
   1.663 -        String bck2brwsrType = "L" + type.replace('.', '_') + "_2";
   1.664 -        if ("int".equals(type)) {
   1.665 -            bck2brwsrType = "I";
   1.666 -        }
   1.667 -        if ("double".equals(type)) {
   1.668 -            bck2brwsrType = "D";
   1.669 -        }
   1.670 -        String pref = "get";
   1.671 -        if ("boolean".equals(type)) {
   1.672 -            pref = "is";
   1.673 -            bck2brwsrType = "Z";
   1.674 -        }
   1.675 -        final String nu = n.replace('.', '_');
   1.676 -        if (array) {
   1.677 -            return new String[] { 
   1.678 -                "get" + n,
   1.679 -                null,
   1.680 -                "get" + nu + "__Ljava_util_List_2",
   1.681 -                null
   1.682 -            };
   1.683 -        }
   1.684 -        return new String[]{
   1.685 -            pref + n, 
   1.686 -            "set" + n, 
   1.687 -            pref + nu + "__" + bck2brwsrType,
   1.688 -            "set" + nu + "__V" + bck2brwsrType
   1.689 -        };
   1.690 -    }
   1.691 -
   1.692 -    private String typeName(Element where, Prprt p) {
   1.693 -        String ret;
   1.694 -        boolean[] isModel = { false };
   1.695 -        boolean[] isEnum = { false };
   1.696 -        boolean isPrimitive[] = { false };
   1.697 -        ret = checkType(p, isModel, isEnum, isPrimitive);
   1.698 -        if (p.array()) {
   1.699 -            String bt = findBoxedType(ret);
   1.700 -            if (bt != null) {
   1.701 -                return bt;
   1.702 -            }
   1.703 -        }
   1.704 -        return ret;
   1.705 -    }
   1.706 -    
   1.707 -    private static String findBoxedType(String ret) {
   1.708 -        if (ret.equals("boolean")) {
   1.709 -            return Boolean.class.getName();
   1.710 -        }
   1.711 -        if (ret.equals("byte")) {
   1.712 -            return Byte.class.getName();
   1.713 -        }
   1.714 -        if (ret.equals("short")) {
   1.715 -            return Short.class.getName();
   1.716 -        }
   1.717 -        if (ret.equals("char")) {
   1.718 -            return Character.class.getName();
   1.719 -        }
   1.720 -        if (ret.equals("int")) {
   1.721 -            return Integer.class.getName();
   1.722 -        }
   1.723 -        if (ret.equals("long")) {
   1.724 -            return Long.class.getName();
   1.725 -        }
   1.726 -        if (ret.equals("float")) {
   1.727 -            return Float.class.getName();
   1.728 -        }
   1.729 -        if (ret.equals("double")) {
   1.730 -            return Double.class.getName();
   1.731 -        }
   1.732 -        return null;
   1.733 -    }
   1.734 -
   1.735 -    private boolean verifyPropName(Element e, String propName, Prprt[] existingProps) {
   1.736 -        StringBuilder sb = new StringBuilder();
   1.737 -        String sep = "";
   1.738 -        for (Prprt Prprt : existingProps) {
   1.739 -            if (Prprt.name().equals(propName)) {
   1.740 -                return true;
   1.741 -            }
   1.742 -            sb.append(sep);
   1.743 -            sb.append('"');
   1.744 -            sb.append(Prprt.name());
   1.745 -            sb.append('"');
   1.746 -            sep = ", ";
   1.747 -        }
   1.748 -        error(
   1.749 -            propName + " is not one of known properties: " + sb
   1.750 -            , e
   1.751 -        );
   1.752 -        return false;
   1.753 -    }
   1.754 -
   1.755 -    private static String findPkgName(Element e) {
   1.756 -        for (;;) {
   1.757 -            if (e.getKind() == ElementKind.PACKAGE) {
   1.758 -                return ((PackageElement)e).getQualifiedName().toString();
   1.759 -            }
   1.760 -            e = e.getEnclosingElement();
   1.761 -        }
   1.762 -    }
   1.763 -
   1.764 -    private boolean generateFunctions(
   1.765 -        Element clazz, StringWriter body, String className, 
   1.766 -        List<? extends Element> enclosedElements, List<String> functions
   1.767 -    ) {
   1.768 -        for (Element m : enclosedElements) {
   1.769 -            if (m.getKind() != ElementKind.METHOD) {
   1.770 -                continue;
   1.771 -            }
   1.772 -            ExecutableElement e = (ExecutableElement)m;
   1.773 -            Function onF = e.getAnnotation(Function.class);
   1.774 -            if (onF == null) {
   1.775 -                continue;
   1.776 -            }
   1.777 -            if (!e.getModifiers().contains(Modifier.STATIC)) {
   1.778 -                error("@OnFunction method needs to be static", e);
   1.779 -                return false;
   1.780 -            }
   1.781 -            if (e.getModifiers().contains(Modifier.PRIVATE)) {
   1.782 -                error("@OnFunction method cannot be private", e);
   1.783 -                return false;
   1.784 -            }
   1.785 -            if (e.getReturnType().getKind() != TypeKind.VOID) {
   1.786 -                error("@OnFunction method should return void", e);
   1.787 -                return false;
   1.788 -            }
   1.789 -            String n = e.getSimpleName().toString();
   1.790 -            body.append("  private void ").append(n).append("(Object data, Object ev) {\n");
   1.791 -            body.append("    ").append(((TypeElement)clazz).getQualifiedName()).append(".").append(n).append("(");
   1.792 -            body.append(wrapParams(e, null, className, "ev", "data"));
   1.793 -            body.append(");\n");
   1.794 -            body.append("  }\n");
   1.795 -            
   1.796 -            functions.add(n);
   1.797 -            functions.add(n + "__VLjava_lang_Object_2Ljava_lang_Object_2");
   1.798 -        }
   1.799 -        return true;
   1.800 -    }
   1.801 -
   1.802 -    private boolean generateOnChange(Element clazz, Map<String,Collection<String>> propDeps,
   1.803 -        Prprt[] properties, String className, 
   1.804 -        Map<String, Collection<String>> functionDeps
   1.805 -    ) {
   1.806 -        for (Element m : clazz.getEnclosedElements()) {
   1.807 -            if (m.getKind() != ElementKind.METHOD) {
   1.808 -                continue;
   1.809 -            }
   1.810 -            ExecutableElement e = (ExecutableElement) m;
   1.811 -            OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class);
   1.812 -            if (onPC == null) {
   1.813 -                continue;
   1.814 -            }
   1.815 -            for (String pn : onPC.value()) {
   1.816 -                if (findPrprt(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
   1.817 -                    error("No Prprt named '" + pn + "' in the model", clazz);
   1.818 -                    return false;
   1.819 -                }
   1.820 -            }
   1.821 -            if (!e.getModifiers().contains(Modifier.STATIC)) {
   1.822 -                error("@OnPrprtChange method needs to be static", e);
   1.823 -                return false;
   1.824 -            }
   1.825 -            if (e.getModifiers().contains(Modifier.PRIVATE)) {
   1.826 -                error("@OnPrprtChange method cannot be private", e);
   1.827 -                return false;
   1.828 -            }
   1.829 -            if (e.getReturnType().getKind() != TypeKind.VOID) {
   1.830 -                error("@OnPrprtChange method should return void", e);
   1.831 -                return false;
   1.832 -            }
   1.833 -            String n = e.getSimpleName().toString();
   1.834 -            
   1.835 -            
   1.836 -            for (String pn : onPC.value()) {
   1.837 -                StringBuilder call = new StringBuilder();
   1.838 -                call.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
   1.839 -                call.append(wrapPropName(e, className, "name", pn));
   1.840 -                call.append(");\n");
   1.841 -                
   1.842 -                Collection<String> change = functionDeps.get(pn);
   1.843 -                if (change == null) {
   1.844 -                    change = new ArrayList<String>();
   1.845 -                    functionDeps.put(pn, change);
   1.846 -                }
   1.847 -                change.add(call.toString());
   1.848 -                for (String dpn : findDerivedFrom(propDeps, pn)) {
   1.849 -                    change = functionDeps.get(dpn);
   1.850 -                    if (change == null) {
   1.851 -                        change = new ArrayList<String>();
   1.852 -                        functionDeps.put(dpn, change);
   1.853 -                    }
   1.854 -                    change.add(call.toString());
   1.855 -                }
   1.856 -            }
   1.857 -        }
   1.858 -        return true;
   1.859 -    }
   1.860 -
   1.861 -    private boolean generateOperation(Element clazz, 
   1.862 -        StringWriter body, String className, 
   1.863 -        List<? extends Element> enclosedElements
   1.864 -    ) {
   1.865 -        for (Element m : enclosedElements) {
   1.866 -            if (m.getKind() != ElementKind.METHOD) {
   1.867 -                continue;
   1.868 -            }
   1.869 -            ExecutableElement e = (ExecutableElement)m;
   1.870 -            ModelOperation mO = e.getAnnotation(ModelOperation.class);
   1.871 -            if (mO == null) {
   1.872 -                continue;
   1.873 -            }
   1.874 -            if (!e.getModifiers().contains(Modifier.STATIC)) {
   1.875 -                error("@ModelOperation method needs to be static", e);
   1.876 -                return false;
   1.877 -            }
   1.878 -            if (e.getModifiers().contains(Modifier.PRIVATE)) {
   1.879 -                error("@ModelOperation method cannot be private", e);
   1.880 -                return false;
   1.881 -            }
   1.882 -            if (e.getReturnType().getKind() != TypeKind.VOID) {
   1.883 -                error("@ModelOperation method should return void", e);
   1.884 -                return false;
   1.885 -            }
   1.886 -            List<String> args = new ArrayList<String>();
   1.887 -            {
   1.888 -                body.append("  public void ").append(m.getSimpleName()).append("(");
   1.889 -                String sep = "";
   1.890 -                boolean checkFirst = true;
   1.891 -                for (VariableElement ve : e.getParameters()) {
   1.892 -                    final TypeMirror type = ve.asType();
   1.893 -                    CharSequence simpleName;
   1.894 -                    if (type.getKind() == TypeKind.DECLARED) {
   1.895 -                        simpleName = ((DeclaredType)type).asElement().getSimpleName();
   1.896 -                    } else {
   1.897 -                        simpleName = type.toString();
   1.898 -                    }
   1.899 -                    if (simpleName.toString().equals(className)) {
   1.900 -                        checkFirst = false;
   1.901 -                    } else {
   1.902 -                        if (checkFirst) {
   1.903 -                            error("First parameter of @ModelOperation method must be " + className, m);
   1.904 -                            return false;
   1.905 -                        }
   1.906 -                        args.add(ve.getSimpleName().toString());
   1.907 -                        body.append(sep).append("final ");
   1.908 -                        body.append(ve.asType().toString()).append(" ");
   1.909 -                        body.append(ve.toString());
   1.910 -                        sep = ", ";
   1.911 -                    }
   1.912 -                }
   1.913 -                body.append(") {\n");
   1.914 -                body.append("    org.apidesign.html.json.impl.JSON.runInBrowser(this.context, new Runnable() { public void run() {\n");
   1.915 -                body.append("      ").append(clazz.getSimpleName()).append(".").append(m.getSimpleName()).append("(");
   1.916 -                body.append(className).append(".this");
   1.917 -                for (String s : args) {
   1.918 -                    body.append(", ").append(s);
   1.919 -                }
   1.920 -                body.append(");\n");
   1.921 -                body.append("    }});\n");
   1.922 -                body.append("  }\n");
   1.923 -            }
   1.924 -            
   1.925 -        }
   1.926 -        return true;
   1.927 -    }
   1.928 -    
   1.929 -    
   1.930 -    private boolean generateReceive(
   1.931 -        Element clazz, StringWriter body, String className, 
   1.932 -        List<? extends Element> enclosedElements, List<String> functions
   1.933 -    ) {
   1.934 -        for (Element m : enclosedElements) {
   1.935 -            if (m.getKind() != ElementKind.METHOD) {
   1.936 -                continue;
   1.937 -            }
   1.938 -            ExecutableElement e = (ExecutableElement)m;
   1.939 -            OnReceive onR = e.getAnnotation(OnReceive.class);
   1.940 -            if (onR == null) {
   1.941 -                continue;
   1.942 -            }
   1.943 -            if (!e.getModifiers().contains(Modifier.STATIC)) {
   1.944 -                error("@OnReceive method needs to be static", e);
   1.945 -                return false;
   1.946 -            }
   1.947 -            if (e.getModifiers().contains(Modifier.PRIVATE)) {
   1.948 -                error("@OnReceive method cannot be private", e);
   1.949 -                return false;
   1.950 -            }
   1.951 -            if (e.getReturnType().getKind() != TypeKind.VOID) {
   1.952 -                error("@OnReceive method should return void", e);
   1.953 -                return false;
   1.954 -            }
   1.955 -            if (!onR.jsonp().isEmpty() && !"GET".equals(onR.method())) {
   1.956 -                error("JSONP works only with GET transport method", e);
   1.957 -            }
   1.958 -            String dataMirror = findDataSpecified(e, onR);
   1.959 -            if ("PUT".equals(onR.method()) && dataMirror == null) {
   1.960 -                error("PUT method needs to specify a data() class", e);
   1.961 -                return false;
   1.962 -            }
   1.963 -            if ("POST".equals(onR.method()) && dataMirror == null) {
   1.964 -                error("POST method needs to specify a data() class", e);
   1.965 -                return false;
   1.966 -            }
   1.967 -            String modelClass = null;
   1.968 -            boolean expectsList = false;
   1.969 -            List<String> args = new ArrayList<String>();
   1.970 -            {
   1.971 -                for (VariableElement ve : e.getParameters()) {
   1.972 -                    TypeMirror modelType = null;
   1.973 -                    final TypeMirror type = ve.asType();
   1.974 -                    CharSequence simpleName;
   1.975 -                    if (type.getKind() == TypeKind.DECLARED) {
   1.976 -                        simpleName = ((DeclaredType)type).asElement().getSimpleName();
   1.977 -                    } else {
   1.978 -                        simpleName = type.toString();
   1.979 -                    }
   1.980 -                    if (simpleName.toString().equals(className)) {
   1.981 -                        args.add(className + ".this");
   1.982 -                    } else if (isModel(ve.asType())) {
   1.983 -                        modelType = ve.asType();
   1.984 -                    } else if (ve.asType().getKind() == TypeKind.ARRAY) {
   1.985 -                        modelType = ((ArrayType)ve.asType()).getComponentType();
   1.986 -                        expectsList = true;
   1.987 -                    } else if (ve.asType().toString().equals("java.lang.String")) {
   1.988 -                        modelType = ve.asType();
   1.989 -                    }
   1.990 -                    if (modelType != null) {
   1.991 -                        if (modelClass != null) {
   1.992 -                            error("There can be only one model class among arguments", e);
   1.993 -                        } else {
   1.994 -                            modelClass = modelType.toString();
   1.995 -                            if (expectsList) {
   1.996 -                                args.add("arr");
   1.997 -                            } else {
   1.998 -                                args.add("arr[0]");
   1.999 -                            }
  1.1000 -                        }
  1.1001 -                    }
  1.1002 -                }
  1.1003 -            }
  1.1004 -            if (modelClass == null) {
  1.1005 -                error("The method needs to have one @Model class as parameter", e);
  1.1006 -            }
  1.1007 -            String n = e.getSimpleName().toString();
  1.1008 -            if ("WebSocket".equals(onR.method())) {
  1.1009 -                body.append("  /** Performs WebSocket communication. Call with <code>null</code> data parameter\n");
  1.1010 -                body.append("  * to open the connection (even if not required). Call with non-null data to\n");
  1.1011 -                body.append("  * send messages to server. Call again with <code>null</code> data to close the socket.\n");
  1.1012 -                body.append("  */\n");
  1.1013 -            }
  1.1014 -            body.append("  public void ").append(n).append("(");
  1.1015 -            StringBuilder urlBefore = new StringBuilder();
  1.1016 -            StringBuilder urlAfter = new StringBuilder();
  1.1017 -            String jsonpVarName = null;
  1.1018 -            {
  1.1019 -                String sep = "";
  1.1020 -                boolean skipJSONP = onR.jsonp().isEmpty();
  1.1021 -                for (String p : findParamNames(e, onR.url(), onR.jsonp(), urlBefore, urlAfter)) {
  1.1022 -                    if (!skipJSONP && p.equals(onR.jsonp())) {
  1.1023 -                        skipJSONP = true;
  1.1024 -                        jsonpVarName = p;
  1.1025 -                        continue;
  1.1026 -                    }
  1.1027 -                    body.append(sep);
  1.1028 -                    body.append("String ").append(p);
  1.1029 -                    sep = ", ";
  1.1030 -                }
  1.1031 -                if (!skipJSONP) {
  1.1032 -                    error(
  1.1033 -                        "Name of jsonp attribute ('" + onR.jsonp() + 
  1.1034 -                        "') is not used in url attribute '" + onR.url() + "'", e
  1.1035 -                    );
  1.1036 -                }
  1.1037 -                if (dataMirror != null) {
  1.1038 -                    body.append(sep).append(dataMirror.toString()).append(" data");
  1.1039 -                }
  1.1040 -            }
  1.1041 -            body.append(") {\n");
  1.1042 -            boolean webSocket = onR.method().equals("WebSocket");
  1.1043 -            if (webSocket) {
  1.1044 -                if (generateWSReceiveBody(body, onR, e, clazz, className, expectsList, modelClass, n, args, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
  1.1045 -                    return false;
  1.1046 -                }
  1.1047 -                body.append("  }\n");
  1.1048 -                body.append("  private org.apidesign.html.json.impl.JSON.WS ws_" + e.getSimpleName() + ";\n");
  1.1049 -            } else {
  1.1050 -                if (generateJSONReceiveBody(body, onR, e, clazz, className, expectsList, modelClass, n, args, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
  1.1051 -                    return false;
  1.1052 -                }
  1.1053 -                body.append("  }\n");
  1.1054 -            }
  1.1055 -        }
  1.1056 -        return true;
  1.1057 -    }
  1.1058 -
  1.1059 -    private boolean generateJSONReceiveBody(StringWriter body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
  1.1060 -        body.append(
  1.1061 -            "    class ProcessResult extends org.apidesign.html.json.impl.RcvrJSON {\n" +
  1.1062 -            "      @Override\n" +
  1.1063 -            "      public void onError(org.apidesign.html.json.impl.RcvrJSON.MsgEvnt ev) {\n" +
  1.1064 -            "        Exception value = ev.getException();\n"
  1.1065 -            );
  1.1066 -        if (onR.onError().isEmpty()) {
  1.1067 -            body.append(
  1.1068 -                "        value.printStackTrace();\n"
  1.1069 -                );
  1.1070 -        } else {
  1.1071 -            if (!findOnError(e, ((TypeElement)clazz), onR.onError(), className)) {
  1.1072 -                return true;
  1.1073 -            }
  1.1074 -            body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
  1.1075 -            body.append(className).append(".this, value);\n");
  1.1076 -        }
  1.1077 -        body.append(
  1.1078 -            "      }\n" +
  1.1079 -            "      @Override\n" +
  1.1080 -            "      public void onMessage(org.apidesign.html.json.impl.RcvrJSON.MsgEvnt ev) {\n"
  1.1081 -            );
  1.1082 -        if (expectsList) {
  1.1083 -            body.append(
  1.1084 -                "        " + modelClass + "[] arr = new " + modelClass + "[ev.dataSize()];\n"
  1.1085 -                );
  1.1086 -        } else {
  1.1087 -            body.append(
  1.1088 -                "        " + modelClass + "[] arr = { null };\n"
  1.1089 -                );
  1.1090 -        }
  1.1091 -        body.append(
  1.1092 -            "        ev.dataRead(context, " + modelClass + ".class, arr);\n"
  1.1093 -            );
  1.1094 -        {
  1.1095 -            body.append("        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  1.1096 -            String sep = "";
  1.1097 -            for (String arg : args) {
  1.1098 -                body.append(sep);
  1.1099 -                body.append(arg);
  1.1100 -                sep = ", ";
  1.1101 -            }
  1.1102 -            body.append(");\n");
  1.1103 -        }
  1.1104 -        body.append(
  1.1105 -            "      }\n" +
  1.1106 -            "    }\n"
  1.1107 -            );
  1.1108 -        body.append("    ProcessResult pr = new ProcessResult();\n");
  1.1109 -        body.append("    org.apidesign.html.json.impl.JSON.loadJSON(context, pr,\n        ");
  1.1110 -        body.append(urlBefore).append(", ");
  1.1111 -        if (jsonpVarName != null) {
  1.1112 -            body.append(urlAfter);
  1.1113 -        } else {
  1.1114 -            body.append("null");
  1.1115 -        }
  1.1116 -        if (!"GET".equals(onR.method()) || dataMirror != null) {
  1.1117 -            body.append(", \"").append(onR.method()).append('"');
  1.1118 -            if (dataMirror != null) {
  1.1119 -                body.append(", data");
  1.1120 -            } else {
  1.1121 -                body.append(", null");
  1.1122 -            }
  1.1123 -        } else {
  1.1124 -            body.append(", null, null");
  1.1125 -        }
  1.1126 -        body.append(");\n");
  1.1127 -        return false;
  1.1128 -    }
  1.1129 -    
  1.1130 -    private boolean generateWSReceiveBody(StringWriter body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
  1.1131 -        body.append(
  1.1132 -            "    class ProcessResult extends org.apidesign.html.json.impl.RcvrJSON {\n" +
  1.1133 -            "      @Override\n" +
  1.1134 -            "      public void onOpen(org.apidesign.html.json.impl.RcvrJSON.MsgEvnt ev) {\n"
  1.1135 -        );
  1.1136 -        body.append("        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  1.1137 -        {
  1.1138 -            String sep = "";
  1.1139 -            for (String arg : args) {
  1.1140 -                body.append(sep);
  1.1141 -                if (arg.startsWith("arr")) {
  1.1142 -                    body.append("null");
  1.1143 -                } else {
  1.1144 -                    body.append(arg);
  1.1145 -                }
  1.1146 -                sep = ", ";
  1.1147 -            }
  1.1148 -        }
  1.1149 -        body.append(");\n");
  1.1150 -        body.append(
  1.1151 -            "      }\n" +
  1.1152 -            "      @Override\n" +
  1.1153 -            "      public void onError(org.apidesign.html.json.impl.RcvrJSON.MsgEvnt ev) {\n" +
  1.1154 -            "        Exception value = ev.getException();\n"
  1.1155 -            );
  1.1156 -        if (onR.onError().isEmpty()) {
  1.1157 -            body.append(
  1.1158 -                "        value.printStackTrace();\n"
  1.1159 -                );
  1.1160 -        } else {
  1.1161 -            if (!findOnError(e, ((TypeElement)clazz), onR.onError(), className)) {
  1.1162 -                return true;
  1.1163 -            }
  1.1164 -            body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
  1.1165 -            body.append(className).append(".this, value);\n");
  1.1166 -        }
  1.1167 -        body.append(
  1.1168 -            "      }\n" +
  1.1169 -            "      @Override\n" +
  1.1170 -            "      public void onMessage(org.apidesign.html.json.impl.RcvrJSON.MsgEvnt ev) {\n"
  1.1171 -        );
  1.1172 -        if (expectsList) {
  1.1173 -            body.append(
  1.1174 -                "        " + modelClass + "[] arr = new " + modelClass + "[ev.dataSize()];\n"
  1.1175 -                );
  1.1176 -        } else {
  1.1177 -            body.append(
  1.1178 -                "        " + modelClass + "[] arr = { null };\n"
  1.1179 -                );
  1.1180 -        }
  1.1181 -        body.append(
  1.1182 -            "        ev.dataRead(context, " + modelClass + ".class, arr);\n"
  1.1183 -            );
  1.1184 -        {
  1.1185 -            body.append("        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  1.1186 -            String sep = "";
  1.1187 -            for (String arg : args) {
  1.1188 -                body.append(sep);
  1.1189 -                body.append(arg);
  1.1190 -                sep = ", ";
  1.1191 -            }
  1.1192 -            body.append(");\n");
  1.1193 -        }
  1.1194 -        body.append(
  1.1195 -            "      }\n"
  1.1196 -        );
  1.1197 -        if (!onR.onError().isEmpty()) {
  1.1198 -            body.append(
  1.1199 -                "      @Override\n"
  1.1200 -              + "      public void onClose(org.apidesign.html.json.impl.RcvrJSON.MsgEvnt ev) {\n"
  1.1201 -            );
  1.1202 -            body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
  1.1203 -            body.append(className).append(".this, null);\n");
  1.1204 -            body.append(
  1.1205 -                "      }\n"
  1.1206 -            );
  1.1207 -        }
  1.1208 -        body.append("    }\n");
  1.1209 -        body.append("    if (this.ws_").append(e.getSimpleName()).append(" == null) {\n");
  1.1210 -        body.append("      ProcessResult pr = new ProcessResult();\n");
  1.1211 -        body.append("      this.ws_").append(e.getSimpleName());
  1.1212 -        body.append("= org.apidesign.html.json.impl.JSON.openWS(context, pr,\n        ");
  1.1213 -        body.append(urlBefore).append(", data);\n");
  1.1214 -        body.append("    } else {\n");
  1.1215 -        body.append("      this.ws_").append(e.getSimpleName()).append(".send(context, ").append(urlBefore).append(", data);\n");
  1.1216 -        body.append("    }\n");
  1.1217 -        return false;
  1.1218 -    }
  1.1219 -
  1.1220 -    private CharSequence wrapParams(
  1.1221 -        ExecutableElement ee, String id, String className, String evName, String dataName
  1.1222 -    ) {
  1.1223 -        TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
  1.1224 -        StringBuilder params = new StringBuilder();
  1.1225 -        boolean first = true;
  1.1226 -        for (VariableElement ve : ee.getParameters()) {
  1.1227 -            if (!first) {
  1.1228 -                params.append(", ");
  1.1229 -            }
  1.1230 -            first = false;
  1.1231 -            String toCall = null;
  1.1232 -            String toFinish = null;
  1.1233 -            if (ve.asType() == stringType) {
  1.1234 -                if (ve.getSimpleName().contentEquals("id")) {
  1.1235 -                    params.append('"').append(id).append('"');
  1.1236 -                    continue;
  1.1237 -                }
  1.1238 -                toCall = "org.apidesign.html.json.impl.JSON.toString(context, ";
  1.1239 -            }
  1.1240 -            if (ve.asType().getKind() == TypeKind.DOUBLE) {
  1.1241 -                toCall = "org.apidesign.html.json.impl.JSON.toNumber(context, ";
  1.1242 -                toFinish = ".doubleValue()";
  1.1243 -            }
  1.1244 -            if (ve.asType().getKind() == TypeKind.INT) {
  1.1245 -                toCall = "org.apidesign.html.json.impl.JSON.toNumber(context, ";
  1.1246 -                toFinish = ".intValue()";
  1.1247 -            }
  1.1248 -            if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
  1.1249 -                toCall = "org.apidesign.html.json.impl.JSON.toModel(context, " + ve.asType() + ".class, ";
  1.1250 -            }
  1.1251 -
  1.1252 -            if (toCall != null) {
  1.1253 -                params.append(toCall);
  1.1254 -                if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
  1.1255 -                    params.append(dataName);
  1.1256 -                    params.append(", null");
  1.1257 -                } else {
  1.1258 -                    if (evName == null) {
  1.1259 -                        final StringBuilder sb = new StringBuilder();
  1.1260 -                        sb.append("Unexpected string parameter name.");
  1.1261 -                        if (dataName != null) {
  1.1262 -                            sb.append(" Try \"").append(dataName).append("\"");
  1.1263 -                        }
  1.1264 -                        error(sb.toString(), ee);
  1.1265 -                    }
  1.1266 -                    params.append(evName);
  1.1267 -                    params.append(", \"");
  1.1268 -                    params.append(ve.getSimpleName().toString());
  1.1269 -                    params.append("\"");
  1.1270 -                }
  1.1271 -                params.append(")");
  1.1272 -                if (toFinish != null) {
  1.1273 -                    params.append(toFinish);
  1.1274 -                }
  1.1275 -                continue;
  1.1276 -            }
  1.1277 -            String rn = fqn(ve.asType(), ee);
  1.1278 -            int last = rn.lastIndexOf('.');
  1.1279 -            if (last >= 0) {
  1.1280 -                rn = rn.substring(last + 1);
  1.1281 -            }
  1.1282 -            if (rn.equals(className)) {
  1.1283 -                params.append(className).append(".this");
  1.1284 -                continue;
  1.1285 -            }
  1.1286 -            error(
  1.1287 -                "The annotated method can only accept " + className + " argument or argument named 'data'",
  1.1288 -                ee
  1.1289 -            );
  1.1290 -        }
  1.1291 -        return params;
  1.1292 -    }
  1.1293 -    
  1.1294 -    
  1.1295 -    private CharSequence wrapPropName(
  1.1296 -        ExecutableElement ee, String className, String propName, String propValue
  1.1297 -    ) {
  1.1298 -        TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
  1.1299 -        StringBuilder params = new StringBuilder();
  1.1300 -        boolean first = true;
  1.1301 -        for (VariableElement ve : ee.getParameters()) {
  1.1302 -            if (!first) {
  1.1303 -                params.append(", ");
  1.1304 -            }
  1.1305 -            first = false;
  1.1306 -            if (ve.asType() == stringType) {
  1.1307 -                if (propName != null && ve.getSimpleName().contentEquals(propName)) {
  1.1308 -                    params.append('"').append(propValue).append('"');
  1.1309 -                } else {
  1.1310 -                    error("Unexpected string parameter name. Try \"" + propName + "\".", ee);
  1.1311 -                }
  1.1312 -                continue;
  1.1313 -            }
  1.1314 -            String rn = fqn(ve.asType(), ee);
  1.1315 -            int last = rn.lastIndexOf('.');
  1.1316 -            if (last >= 0) {
  1.1317 -                rn = rn.substring(last + 1);
  1.1318 -            }
  1.1319 -            if (rn.equals(className)) {
  1.1320 -                params.append(className).append(".this");
  1.1321 -                continue;
  1.1322 -            }
  1.1323 -            error(
  1.1324 -                "@OnPrprtChange method can only accept String or " + className + " arguments",
  1.1325 -                ee);
  1.1326 -        }
  1.1327 -        return params;
  1.1328 -    }
  1.1329 -    
  1.1330 -    private boolean isModel(TypeMirror tm) {
  1.1331 -        if (tm.getKind() == TypeKind.ERROR) {
  1.1332 -            return true;
  1.1333 -        }
  1.1334 -        final Element e = processingEnv.getTypeUtils().asElement(tm);
  1.1335 -        if (e == null) {
  1.1336 -            return false;
  1.1337 -        }
  1.1338 -        for (Element ch : e.getEnclosedElements()) {
  1.1339 -            if (ch.getKind() == ElementKind.METHOD) {
  1.1340 -                ExecutableElement ee = (ExecutableElement)ch;
  1.1341 -                if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) {
  1.1342 -                    return true;
  1.1343 -                }
  1.1344 -            }
  1.1345 -        }
  1.1346 -        return models.values().contains(e.getSimpleName().toString());
  1.1347 -    }
  1.1348 -    
  1.1349 -    private void writeToString(Prprt[] props, Writer w) throws IOException {
  1.1350 -        w.write("  public String toString() {\n");
  1.1351 -        w.write("    StringBuilder sb = new StringBuilder();\n");
  1.1352 -        w.write("    sb.append('{');\n");
  1.1353 -        String sep = "";
  1.1354 -        for (Prprt p : props) {
  1.1355 -            w.write(sep);
  1.1356 -            w.append("    sb.append('\"').append(\"" + p.name() + "\")");
  1.1357 -                w.append(".append('\"').append(\":\");\n");
  1.1358 -            w.append("    sb.append(org.apidesign.html.json.impl.JSON.toJSON(prop_");
  1.1359 -            w.append(p.name()).append("));\n");
  1.1360 -            sep =    "    sb.append(',');\n";
  1.1361 -        }
  1.1362 -        w.write("    sb.append('}');\n");
  1.1363 -        w.write("    return sb.toString();\n");
  1.1364 -        w.write("  }\n");
  1.1365 -    }
  1.1366 -    private void writeClone(String className, Prprt[] props, Writer w) throws IOException {
  1.1367 -        w.write("  public " + className + " clone() {\n");
  1.1368 -        w.write("    return clone(context);\n");
  1.1369 -        w.write("  }\n");
  1.1370 -        w.write("  private " + className + " clone(net.java.html.BrwsrCtx ctx) {\n");
  1.1371 -        w.write("    " + className + " ret = new " + className + "(ctx);\n");
  1.1372 -        for (Prprt p : props) {
  1.1373 -            if (!p.array()) {
  1.1374 -                boolean isModel[] = { false };
  1.1375 -                boolean isEnum[] = { false };
  1.1376 -                boolean isPrimitive[] = { false };
  1.1377 -                checkType(p, isModel, isEnum, isPrimitive);
  1.1378 -                if (!isModel[0]) {
  1.1379 -                    w.write("    ret.prop_" + p.name() + " = prop_" + p.name() + ";\n");
  1.1380 -                    continue;
  1.1381 -                }
  1.1382 -                w.write("    ret.prop_" + p.name() + " =  prop_" + p.name() + "  == null ? null : prop_" + p.name() + ".clone();\n");
  1.1383 -            } else {
  1.1384 -                w.write("    ret.prop_" + p.name() + ".cloneAll(ctx, prop_" + p.name() + ");\n");
  1.1385 -            }
  1.1386 -        }
  1.1387 -        
  1.1388 -        w.write("    return ret;\n");
  1.1389 -        w.write("  }\n");
  1.1390 -    }
  1.1391 -
  1.1392 -    private String inPckName(Element e) {
  1.1393 -        StringBuilder sb = new StringBuilder();
  1.1394 -        while (e.getKind() != ElementKind.PACKAGE) {
  1.1395 -            if (sb.length() == 0) {
  1.1396 -                sb.append(e.getSimpleName());
  1.1397 -            } else {
  1.1398 -                sb.insert(0, '.');
  1.1399 -                sb.insert(0, e.getSimpleName());
  1.1400 -            }
  1.1401 -            e = e.getEnclosingElement();
  1.1402 -        }
  1.1403 -        return sb.toString();
  1.1404 -    }
  1.1405 -
  1.1406 -    private String fqn(TypeMirror pt, Element relative) {
  1.1407 -        if (pt.getKind() == TypeKind.ERROR) {
  1.1408 -            final Elements eu = processingEnv.getElementUtils();
  1.1409 -            PackageElement pckg = eu.getPackageOf(relative);
  1.1410 -            return pckg.getQualifiedName() + "." + pt.toString();
  1.1411 -        }
  1.1412 -        return pt.toString();
  1.1413 -    }
  1.1414 -
  1.1415 -    private String checkType(Prprt p, boolean[] isModel, boolean[] isEnum, boolean[] isPrimitive) {
  1.1416 -        TypeMirror tm;
  1.1417 -        try {
  1.1418 -            String ret = p.typeName(processingEnv);
  1.1419 -            TypeElement e = processingEnv.getElementUtils().getTypeElement(ret);
  1.1420 -            if (e == null) {
  1.1421 -                isModel[0] = true;
  1.1422 -                isEnum[0] = false;
  1.1423 -                isPrimitive[0] = false;
  1.1424 -                return ret;
  1.1425 -            }
  1.1426 -            tm = e.asType();
  1.1427 -        } catch (MirroredTypeException ex) {
  1.1428 -            tm = ex.getTypeMirror();
  1.1429 -        }
  1.1430 -        tm = processingEnv.getTypeUtils().erasure(tm);
  1.1431 -        if (isPrimitive[0] = tm.getKind().isPrimitive()) {
  1.1432 -            isEnum[0] = false;
  1.1433 -            isModel[0] = false;
  1.1434 -            return tm.toString();
  1.1435 -        }
  1.1436 -        final Element e = processingEnv.getTypeUtils().asElement(tm);
  1.1437 -        if (e.getKind() == ElementKind.CLASS && tm.getKind() == TypeKind.ERROR) {
  1.1438 -            isModel[0] = true;
  1.1439 -            isEnum[0] = false;
  1.1440 -            return e.getSimpleName().toString();
  1.1441 -        }
  1.1442 -        
  1.1443 -        final Model m = e == null ? null : e.getAnnotation(Model.class);
  1.1444 -        String ret;
  1.1445 -        if (m != null) {
  1.1446 -            ret = findPkgName(e) + '.' + m.className();
  1.1447 -            isModel[0] = true;
  1.1448 -            models.put(e, m.className());
  1.1449 -        } else if (findModelForMthd(e)) {
  1.1450 -            ret = ((TypeElement)e).getQualifiedName().toString();
  1.1451 -            isModel[0] = true;
  1.1452 -        } else {
  1.1453 -            ret = tm.toString();
  1.1454 -        }
  1.1455 -        TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
  1.1456 -        enm = processingEnv.getTypeUtils().erasure(enm);
  1.1457 -        isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
  1.1458 -        return ret;
  1.1459 -    }
  1.1460 -    
  1.1461 -    private static boolean findModelForMthd(Element clazz) {
  1.1462 -        if (clazz == null) {
  1.1463 -            return false;
  1.1464 -        }
  1.1465 -        for (Element e : clazz.getEnclosedElements()) {
  1.1466 -            if (e.getKind() == ElementKind.METHOD) {
  1.1467 -                ExecutableElement ee = (ExecutableElement)e;
  1.1468 -                if (
  1.1469 -                    ee.getSimpleName().contentEquals("modelFor") &&
  1.1470 -                    ee.getParameters().isEmpty()
  1.1471 -                ) {
  1.1472 -                    return true;
  1.1473 -                }
  1.1474 -            }
  1.1475 -        }
  1.1476 -        return false;
  1.1477 -    }
  1.1478 -
  1.1479 -    private Iterable<String> findParamNames(
  1.1480 -        Element e, String url, String jsonParam, StringBuilder... both
  1.1481 -    ) {
  1.1482 -        List<String> params = new ArrayList<String>();
  1.1483 -        int wasJSON = 0;
  1.1484 -
  1.1485 -        for (int pos = 0; ;) {
  1.1486 -            int next = url.indexOf('{', pos);
  1.1487 -            if (next == -1) {
  1.1488 -                both[wasJSON].append('"')
  1.1489 -                    .append(url.substring(pos))
  1.1490 -                    .append('"');
  1.1491 -                return params;
  1.1492 -            }
  1.1493 -            int close = url.indexOf('}', next);
  1.1494 -            if (close == -1) {
  1.1495 -                error("Unbalanced '{' and '}' in " + url, e);
  1.1496 -                return params;
  1.1497 -            }
  1.1498 -            final String paramName = url.substring(next + 1, close);
  1.1499 -            params.add(paramName);
  1.1500 -            if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
  1.1501 -                both[wasJSON].append('"')
  1.1502 -                    .append(url.substring(pos, next))
  1.1503 -                    .append('"');
  1.1504 -                wasJSON = 1;
  1.1505 -            } else {
  1.1506 -                both[wasJSON].append('"')
  1.1507 -                    .append(url.substring(pos, next))
  1.1508 -                    .append("\" + ").append(paramName).append(" + ");
  1.1509 -            }
  1.1510 -            pos = close + 1;
  1.1511 -        }
  1.1512 -    }
  1.1513 -
  1.1514 -    private static Prprt findPrprt(Prprt[] properties, String propName) {
  1.1515 -        for (Prprt p : properties) {
  1.1516 -            if (propName.equals(p.name())) {
  1.1517 -                return p;
  1.1518 -            }
  1.1519 -        }
  1.1520 -        return null;
  1.1521 -    }
  1.1522 -
  1.1523 -    private boolean isPrimitive(String type) {
  1.1524 -        return 
  1.1525 -            "int".equals(type) ||
  1.1526 -            "double".equals(type) ||
  1.1527 -            "long".equals(type) ||
  1.1528 -            "short".equals(type) ||
  1.1529 -            "byte".equals(type) ||
  1.1530 -            "char".equals(type) ||
  1.1531 -            "boolean".equals(type) ||
  1.1532 -            "float".equals(type);
  1.1533 -    }
  1.1534 -
  1.1535 -    private static Collection<String> findDerivedFrom(Map<String, Collection<String>> propsDeps, String derivedProp) {
  1.1536 -        Set<String> names = new HashSet<String>();
  1.1537 -        for (Map.Entry<String, Collection<String>> e : propsDeps.entrySet()) {
  1.1538 -            if (e.getValue().contains(derivedProp)) {
  1.1539 -                names.add(e.getKey());
  1.1540 -            }
  1.1541 -        }
  1.1542 -        return names;
  1.1543 -    }
  1.1544 -    
  1.1545 -    private Prprt[] createProps(Element e, Property[] arr) {
  1.1546 -        Prprt[] ret = Prprt.wrap(processingEnv, e, arr);
  1.1547 -        Prprt[] prev = verify.put(e, ret);
  1.1548 -        if (prev != null) {
  1.1549 -            error("Two sets of properties for ", e);
  1.1550 -        }
  1.1551 -        return ret;
  1.1552 -    }
  1.1553 -    
  1.1554 -    private static String strip(String s) {
  1.1555 -        int indx = s.indexOf("__");
  1.1556 -        if (indx >= 0) {
  1.1557 -            return s.substring(0, indx);
  1.1558 -        } else {
  1.1559 -            return s;
  1.1560 -        }
  1.1561 -    }
  1.1562 -
  1.1563 -    private String findDataSpecified(ExecutableElement e, OnReceive onR) {
  1.1564 -        try {
  1.1565 -            return onR.data().getName();
  1.1566 -        } catch (MirroredTypeException ex) {
  1.1567 -            final TypeMirror tm = ex.getTypeMirror();
  1.1568 -            String name;
  1.1569 -            final Element te = processingEnv.getTypeUtils().asElement(tm);
  1.1570 -            if (te.getKind() == ElementKind.CLASS && tm.getKind() == TypeKind.ERROR) {
  1.1571 -                name = te.getSimpleName().toString();
  1.1572 -            } else {
  1.1573 -                name = tm.toString();
  1.1574 -            }
  1.1575 -            return "java.lang.Object".equals(name) ? null : name;
  1.1576 -        } catch (Exception ex) {
  1.1577 -            // fallback
  1.1578 -        }
  1.1579 -        
  1.1580 -        AnnotationMirror found = null;
  1.1581 -        for (AnnotationMirror am : e.getAnnotationMirrors()) {
  1.1582 -            if (am.getAnnotationType().toString().equals(OnReceive.class.getName())) {
  1.1583 -                found = am;
  1.1584 -            }
  1.1585 -        }
  1.1586 -        if (found == null) {
  1.1587 -            return null;
  1.1588 -        }
  1.1589 -        
  1.1590 -        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : found.getElementValues().entrySet()) {
  1.1591 -            ExecutableElement ee = entry.getKey();
  1.1592 -            AnnotationValue av = entry.getValue();
  1.1593 -            if (ee.getSimpleName().contentEquals("data")) {
  1.1594 -                List<? extends Object> values = getAnnoValues(processingEnv, e, found);
  1.1595 -                for (Object v : values) {
  1.1596 -                    String sv = v.toString();
  1.1597 -                    if (sv.startsWith("data = ") && sv.endsWith(".class")) {
  1.1598 -                        return sv.substring(7, sv.length() - 6);
  1.1599 -                    }
  1.1600 -                }
  1.1601 -                return "error";
  1.1602 -            }
  1.1603 -        }
  1.1604 -        return null;
  1.1605 -    }
  1.1606 -
  1.1607 -    static List<? extends Object> getAnnoValues(ProcessingEnvironment pe, Element e, AnnotationMirror am) {
  1.1608 -        try {
  1.1609 -            Class<?> trees = Class.forName("com.sun.tools.javac.api.JavacTrees");
  1.1610 -            Method m = trees.getMethod("instance", ProcessingEnvironment.class);
  1.1611 -            Object instance = m.invoke(null, pe);
  1.1612 -            m = instance.getClass().getMethod("getPath", Element.class, AnnotationMirror.class);
  1.1613 -            Object path = m.invoke(instance, e, am);
  1.1614 -            m = path.getClass().getMethod("getLeaf");
  1.1615 -            Object leaf = m.invoke(path);
  1.1616 -            m = leaf.getClass().getMethod("getArguments");
  1.1617 -            return (List) m.invoke(leaf);
  1.1618 -        } catch (Exception ex) {
  1.1619 -            return Collections.emptyList();
  1.1620 -        }
  1.1621 -    }
  1.1622 -
  1.1623 -    private static class Prprt {
  1.1624 -        private final Element e;
  1.1625 -        private final AnnotationMirror tm;
  1.1626 -        private final Property p;
  1.1627 -
  1.1628 -        public Prprt(Element e, AnnotationMirror tm, Property p) {
  1.1629 -            this.e = e;
  1.1630 -            this.tm = tm;
  1.1631 -            this.p = p;
  1.1632 -        }
  1.1633 -        
  1.1634 -        String name() {
  1.1635 -            return p.name();
  1.1636 -        }
  1.1637 -        
  1.1638 -        boolean array() {
  1.1639 -            return p.array();
  1.1640 -        }
  1.1641 -
  1.1642 -        String typeName(ProcessingEnvironment env) {
  1.1643 -            RuntimeException ex;
  1.1644 -            try {
  1.1645 -                return p.type().getName();
  1.1646 -            } catch (IncompleteAnnotationException e) {
  1.1647 -                ex = e;
  1.1648 -            } catch (AnnotationTypeMismatchException e) {
  1.1649 -                ex = e;
  1.1650 -            }
  1.1651 -            for (Object v : getAnnoValues(env, e, tm)) {
  1.1652 -                String s = v.toString().replace(" ", "");
  1.1653 -                if (s.startsWith("type=") && s.endsWith(".class")) {
  1.1654 -                    return s.substring(5, s.length() - 6);
  1.1655 -                }
  1.1656 -            }
  1.1657 -            throw ex;
  1.1658 -        }
  1.1659 -        
  1.1660 -        
  1.1661 -        static Prprt[] wrap(ProcessingEnvironment pe, Element e, Property[] arr) {
  1.1662 -            if (arr.length == 0) {
  1.1663 -                return new Prprt[0];
  1.1664 -            }
  1.1665 -            
  1.1666 -            if (e.getKind() != ElementKind.CLASS) {
  1.1667 -                throw new IllegalStateException("" + e.getKind());
  1.1668 -            }
  1.1669 -            TypeElement te = (TypeElement)e;
  1.1670 -            List<? extends AnnotationValue> val = null;
  1.1671 -            for (AnnotationMirror an : te.getAnnotationMirrors()) {
  1.1672 -                for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : an.getElementValues().entrySet()) {
  1.1673 -                    if (entry.getKey().getSimpleName().contentEquals("properties")) {
  1.1674 -                        val = (List)entry.getValue().getValue();
  1.1675 -                        break;
  1.1676 -                    }
  1.1677 -                }
  1.1678 -            }
  1.1679 -            if (val == null || val.size() != arr.length) {
  1.1680 -                pe.getMessager().printMessage(Diagnostic.Kind.ERROR, "" + val, e);
  1.1681 -                return new Prprt[0];
  1.1682 -            }
  1.1683 -            Prprt[] ret = new Prprt[arr.length];
  1.1684 -            BIG: for (int i = 0; i < ret.length; i++) {
  1.1685 -                AnnotationMirror am = (AnnotationMirror)val.get(i).getValue();
  1.1686 -                ret[i] = new Prprt(e, am, arr[i]);
  1.1687 -                
  1.1688 -            }
  1.1689 -            return ret;
  1.1690 -        }
  1.1691 -    }
  1.1692 -
  1.1693 -    @Override
  1.1694 -    public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
  1.1695 -        final Level l = Level.FINE;
  1.1696 -        LOG.log(l, " element: {0}", element);
  1.1697 -        LOG.log(l, " annotation: {0}", annotation);
  1.1698 -        LOG.log(l, " member: {0}", member);
  1.1699 -        LOG.log(l, " userText: {0}", userText);
  1.1700 -        LOG.log(l, "str: {0}", annotation.getAnnotationType().toString());
  1.1701 -        if (annotation.getAnnotationType().toString().equals(OnReceive.class.getName())) {
  1.1702 -            if (member.getSimpleName().contentEquals("method")) {
  1.1703 -                return Arrays.asList(
  1.1704 -                    methodOf("GET"),
  1.1705 -                    methodOf("POST"),
  1.1706 -                    methodOf("PUT"),
  1.1707 -                    methodOf("DELETE"),
  1.1708 -                    methodOf("HEAD"),
  1.1709 -                    methodOf("WebSocket")
  1.1710 -                );
  1.1711 -            }
  1.1712 -        }
  1.1713 -        
  1.1714 -        return super.getCompletions(element, annotation, member, userText);
  1.1715 -    }
  1.1716 -    
  1.1717 -    private static final Completion methodOf(String method) {
  1.1718 -        ResourceBundle rb = ResourceBundle.getBundle("org.apidesign.html.json.impl.Bundle");
  1.1719 -        return Completions.of('"' + method + '"', rb.getString("MSG_Completion_" + method));
  1.1720 -    }
  1.1721 -    
  1.1722 -    private boolean findOnError(ExecutableElement errElem, TypeElement te, String name, String className) {
  1.1723 -        String err = null;
  1.1724 -        METHODS:
  1.1725 -        for (Element e : te.getEnclosedElements()) {
  1.1726 -            if (e.getKind() != ElementKind.METHOD) {
  1.1727 -                continue;
  1.1728 -            }
  1.1729 -            if (!e.getSimpleName().contentEquals(name)) {
  1.1730 -                continue;
  1.1731 -            }
  1.1732 -            if (!e.getModifiers().contains(Modifier.STATIC)) {
  1.1733 -                errElem = (ExecutableElement) e;
  1.1734 -                err = "Would have to be static";
  1.1735 -                continue;
  1.1736 -            }
  1.1737 -            ExecutableElement ee = (ExecutableElement) e;
  1.1738 -            TypeMirror excType = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
  1.1739 -            final List<? extends VariableElement> params = ee.getParameters();
  1.1740 -            boolean error = false;
  1.1741 -            if (params.size() != 2) {
  1.1742 -                error = true;
  1.1743 -            } else {
  1.1744 -                String firstType = params.get(0).asType().toString();
  1.1745 -                int lastDot = firstType.lastIndexOf('.');
  1.1746 -                if (lastDot != -1) {
  1.1747 -                    firstType = firstType.substring(lastDot + 1);
  1.1748 -                }
  1.1749 -                if (!firstType.equals(className)) {
  1.1750 -                    error = true;
  1.1751 -                }
  1.1752 -                if (!processingEnv.getTypeUtils().isAssignable(excType, params.get(1).asType())) {
  1.1753 -                    error = true;
  1.1754 -                }
  1.1755 -            }
  1.1756 -            if (error) {
  1.1757 -                errElem = (ExecutableElement) e;
  1.1758 -                err = "Error method first argument needs to be " + className + " and second Exception";
  1.1759 -                continue;
  1.1760 -            }
  1.1761 -            return true;
  1.1762 -        }
  1.1763 -        if (err == null) {
  1.1764 -            err = "Cannot find " + name + "(" + className + ", Exception) method in this class";
  1.1765 -        }
  1.1766 -        error(err, errElem);
  1.1767 -        return false;
  1.1768 -    }
  1.1769 -    
  1.1770 -}