json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
author Jaroslav Tulach <jtulach@netbeans.org>
Tue, 26 Aug 2014 18:13:30 +0200
changeset 838 bdc3d696dd4a
parent 829 1baad5268149
child 862 7cacce04dfd8
permissions -rw-r--r--
During the API review process (bug 246133) the reviewers decided that in order to include html4j to NetBeans Platform, we need to stop using org.apidesign namespace and switch to NetBeans one. Repackaging all SPI packages into org.netbeans.html.smthng.spi.
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package org.netbeans.html.json.impl;
    44 
    45 import java.io.IOException;
    46 import java.io.OutputStreamWriter;
    47 import java.io.StringWriter;
    48 import java.io.Writer;
    49 import java.lang.annotation.AnnotationTypeMismatchException;
    50 import java.lang.annotation.IncompleteAnnotationException;
    51 import java.lang.reflect.Method;
    52 import java.util.ArrayList;
    53 import java.util.Arrays;
    54 import java.util.Collection;
    55 import java.util.Collections;
    56 import java.util.HashMap;
    57 import java.util.HashSet;
    58 import java.util.LinkedHashSet;
    59 import java.util.List;
    60 import java.util.Map;
    61 import java.util.ResourceBundle;
    62 import java.util.Set;
    63 import java.util.WeakHashMap;
    64 import java.util.logging.Level;
    65 import java.util.logging.Logger;
    66 import javax.annotation.processing.AbstractProcessor;
    67 import javax.annotation.processing.Completion;
    68 import javax.annotation.processing.Completions;
    69 import javax.annotation.processing.ProcessingEnvironment;
    70 import javax.annotation.processing.Processor;
    71 import javax.annotation.processing.RoundEnvironment;
    72 import javax.annotation.processing.SupportedAnnotationTypes;
    73 import javax.annotation.processing.SupportedSourceVersion;
    74 import javax.lang.model.SourceVersion;
    75 import javax.lang.model.element.AnnotationMirror;
    76 import javax.lang.model.element.AnnotationValue;
    77 import javax.lang.model.element.Element;
    78 import javax.lang.model.element.ElementKind;
    79 import javax.lang.model.element.ExecutableElement;
    80 import javax.lang.model.element.Modifier;
    81 import javax.lang.model.element.PackageElement;
    82 import javax.lang.model.element.TypeElement;
    83 import javax.lang.model.element.VariableElement;
    84 import javax.lang.model.type.ArrayType;
    85 import javax.lang.model.type.DeclaredType;
    86 import javax.lang.model.type.MirroredTypeException;
    87 import javax.lang.model.type.TypeKind;
    88 import javax.lang.model.type.TypeMirror;
    89 import javax.lang.model.util.Elements;
    90 import javax.lang.model.util.Types;
    91 import javax.tools.Diagnostic;
    92 import javax.tools.FileObject;
    93 import net.java.html.json.ComputedProperty;
    94 import net.java.html.json.Model;
    95 import net.java.html.json.Function;
    96 import net.java.html.json.ModelOperation;
    97 import net.java.html.json.OnPropertyChange;
    98 import net.java.html.json.OnReceive;
    99 import net.java.html.json.Property;
   100 import org.openide.util.lookup.ServiceProvider;
   101 
   102 /** Annotation processor to process {@link Model @Model} annotations and
   103  * generate appropriate model classes.
   104  *
   105  * @author Jaroslav Tulach
   106  */
   107 @ServiceProvider(service=Processor.class)
   108 @SupportedSourceVersion(SourceVersion.RELEASE_6)
   109 @SupportedAnnotationTypes({
   110     "net.java.html.json.Model",
   111     "net.java.html.json.ModelOperation",
   112     "net.java.html.json.Function",
   113     "net.java.html.json.OnReceive",
   114     "net.java.html.json.OnPropertyChange",
   115     "net.java.html.json.ComputedProperty",
   116     "net.java.html.json.Property"
   117 })
   118 public final class ModelProcessor extends AbstractProcessor {
   119     private static final Logger LOG = Logger.getLogger(ModelProcessor.class.getName());
   120     private final Map<Element,String> models = new WeakHashMap<Element,String>();
   121     private final Map<Element,Prprt[]> verify = new WeakHashMap<Element,Prprt[]>();
   122     @Override
   123     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   124         boolean ok = true;
   125         for (Element e : roundEnv.getElementsAnnotatedWith(Model.class)) {
   126             if (!processModel(e)) {
   127                 ok = false;
   128             }
   129         }
   130         if (roundEnv.processingOver()) {
   131             models.clear();
   132             for (Map.Entry<Element, Prprt[]> entry : verify.entrySet()) {
   133                 TypeElement te = (TypeElement)entry.getKey();
   134                 String fqn = processingEnv.getElementUtils().getBinaryName(te).toString();
   135                 Element finalElem = processingEnv.getElementUtils().getTypeElement(fqn);
   136                 if (finalElem == null) {
   137                     continue;
   138                 }
   139                 Prprt[] props;
   140                 Model m = finalElem.getAnnotation(Model.class);
   141                 if (m == null) {
   142                     continue;
   143                 }
   144                 props = Prprt.wrap(processingEnv, finalElem, m.properties());
   145                 for (Prprt p : props) {
   146                     boolean[] isModel = { false };
   147                     boolean[] isEnum = { false };
   148                     boolean[] isPrimitive = { false };
   149                     String t = checkType(p, isModel, isEnum, isPrimitive);
   150                     if (isEnum[0]) {
   151                         continue;
   152                     }
   153                     if (isPrimitive[0]) {
   154                         continue;
   155                     }
   156                     if (isModel[0]) {
   157                         continue;
   158                     }
   159                     if ("java.lang.String".equals(t)) {
   160                         continue;
   161                     }
   162                     error("The type " + t + " should be defined by @Model annotation", entry.getKey());
   163                 }
   164             }
   165             verify.clear();
   166         }
   167         return ok;
   168     }
   169 
   170     private void error(String msg, Element e) {
   171         processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
   172     }
   173     
   174     private boolean processModel(Element e) {
   175         boolean ok = true;
   176         Model m = e.getAnnotation(Model.class);
   177         if (m == null) {
   178             return true;
   179         }
   180         String pkg = findPkgName(e);
   181         Writer w;
   182         String className = m.className();
   183         models.put(e, className);
   184         try {
   185             StringWriter body = new StringWriter();
   186             StringBuilder onReceiveType = new StringBuilder();
   187             List<GetSet> propsGetSet = new ArrayList<GetSet>();
   188             List<Object> functions = new ArrayList<Object>();
   189             Map<String, Collection<String[]>> propsDeps = new HashMap<String, Collection<String[]>>();
   190             Map<String, Collection<String>> functionDeps = new HashMap<String, Collection<String>>();
   191             Prprt[] props = createProps(e, m.properties());
   192             
   193             if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
   194                 ok = false;
   195             }
   196             if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
   197                 ok = false;
   198             }
   199             if (!generateProperties(e, body, className, props, propsGetSet, propsDeps, functionDeps)) {
   200                 ok = false;
   201             }
   202             if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
   203                 ok = false;
   204             }
   205             int functionsCount = functions.size() / 2;
   206             for (int i = 0; i < functions.size(); i += 2) {
   207                 for (Prprt p : props) {
   208                     if (p.name().equals(functions.get(i))) {
   209                         error("Function cannot have the name of an existing property", e);
   210                         ok = false;
   211                     }
   212                 }
   213             }
   214             if (!generateReceive(e, body, className, e.getEnclosedElements(), onReceiveType)) {
   215                 ok = false;
   216             }
   217             if (!generateOperation(e, body, className, e.getEnclosedElements(), functions)) {
   218                 ok = false;
   219             }
   220             FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
   221             w = new OutputStreamWriter(java.openOutputStream());
   222             try {
   223                 w.append("package " + pkg + ";\n");
   224                 w.append("import net.java.html.json.*;\n");
   225                 w.append("public final class ").append(className).append(" implements Cloneable {\n");
   226                 w.append("  private static final Html4JavaType TYPE = new Html4JavaType();\n");
   227                 w.append("  private final org.netbeans.html.json.spi.Proto proto;\n");
   228                 w.append(body.toString());
   229                 w.append("  private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
   230                 w.append("  private ").append(className).append("(net.java.html.BrwsrCtx context) {\n");
   231                 w.append("    this.proto = TYPE.createProto(this, context);\n");
   232                 for (Prprt p : props) {
   233                     if (p.array()) {
   234                         final String tn = typeName(e, p);
   235                         String[] gs = toGetSet(p.name(), tn, p.array());
   236                         w.write("    this.prop_" + p.name() + " = proto.createList(\""
   237                             + p.name() + "\"");
   238                         if (functionDeps.containsKey(p.name())) {
   239                             int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name());
   240                             w.write(", " + index);
   241                         } else {
   242                             w.write(", -1");
   243                         }
   244                         Collection<String[]> dependants = propsDeps.get(p.name());
   245                         if (dependants != null) {
   246                             for (String[] depProp : dependants) {
   247                                 w.write(", ");
   248                                 w.write('\"');
   249                                 w.write(depProp[0]);
   250                                 w.write('\"');
   251                             }
   252                         }
   253                         w.write(")");
   254                         w.write(";\n");
   255                     }
   256                 }
   257                 w.append("  };\n");
   258                 w.append("  public ").append(className).append("() {\n");
   259                 w.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
   260                 for (Prprt p : props) {
   261                     if (!p.array()) {
   262                         boolean[] isModel = {false};
   263                         boolean[] isEnum = {false};
   264                         boolean isPrimitive[] = {false};
   265                         String tn = checkType(p, isModel, isEnum, isPrimitive);
   266                         if (isModel[0]) {
   267                             w.write("    prop_" + p.name() + " = new " + tn + "();\n");
   268                         }
   269                     }
   270                 }
   271                 w.append("  };\n");
   272                 if (props.length > 0) {
   273                     w.append("  public ").append(className).append("(");
   274                     Prprt firstArray = null;
   275                     String sep = "";
   276                     for (Prprt p : props) {
   277                         if (p.array()) {
   278                             if (firstArray == null) {
   279                                 firstArray = p;
   280                             }
   281                             continue;
   282                         }
   283                         String tn = typeName(e, p);
   284                         w.write(sep);
   285                         w.write(tn);
   286                         w.write(" " + p.name());
   287                         sep = ", ";
   288                     }
   289                     if (firstArray != null) {
   290                         String tn;
   291                         boolean[] isModel = {false};
   292                         boolean[] isEnum = {false};
   293                         boolean isPrimitive[] = {false};
   294                         tn = checkType(firstArray, isModel, isEnum, isPrimitive);
   295                         w.write(sep);
   296                         w.write(tn);
   297                         w.write("... " + firstArray.name());
   298                     }
   299                     w.append(") {\n");
   300                     w.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
   301                     for (Prprt p : props) {
   302                         if (p.array()) {
   303                             continue;
   304                         }
   305                         w.write("    this.prop_" + p.name() + " = " + p.name() + ";\n");
   306                     }
   307                     if (firstArray != null) {
   308                         w.write("    proto.initTo(this.prop_" + firstArray.name() + ", " + firstArray.name() + ");\n");
   309                     }
   310                     w.append("  };\n");
   311                 }
   312                 w.append("  private static class Html4JavaType extends org.netbeans.html.json.spi.Proto.Type<").append(className).append("> {\n");
   313                 w.append("    private Html4JavaType() {\n      super(").append(className).append(".class, ").
   314                     append(inPckName(e)).append(".class, " + propsGetSet.size() + ", "
   315                     + functionsCount + ");\n");
   316                 {
   317                     for (int i = 0; i < propsGetSet.size(); i++) {
   318                         w.append("      registerProperty(\"").append(propsGetSet.get(i).name).append("\", ");
   319                         w.append((i) + ", " + propsGetSet.get(i).readOnly + ");\n");
   320                     }
   321                 }
   322                 {
   323                     for (int i = 0; i < functionsCount; i++) {
   324                         w.append("      registerFunction(\"").append((String)functions.get(i * 2)).append("\", ");
   325                         w.append(i + ");\n");
   326                     }
   327                 }
   328                 w.append("    }\n");
   329                 w.append("    @Override public void setValue(" + className + " data, int type, Object value) {\n");
   330                 w.append("      switch (type) {\n");
   331                 for (int i = 0; i < propsGetSet.size(); i++) {
   332                     final GetSet pgs = propsGetSet.get(i);
   333                     if (pgs.readOnly) {
   334                         continue;
   335                     }
   336                     final String set = pgs.setter;
   337                     String tn = pgs.type;
   338                     String btn = findBoxedType(tn);
   339                     if (btn != null) {
   340                         tn = btn;
   341                     }
   342                     w.append("        case " + i + ": ");
   343                     if (pgs.setter != null) {
   344                         w.append("data.").append(strip(pgs.setter)).append("(TYPE.extractValue(" + tn + ".class, value)); return;\n");
   345                     } else {
   346                         w.append("TYPE.replaceValue(data.").append(strip(pgs.getter)).append("(), " + tn + ".class, value); return;\n");
   347                     }
   348                 }
   349                 w.append("      }\n");
   350                 w.append("      throw new UnsupportedOperationException();\n");
   351                 w.append("    }\n");
   352                 w.append("    @Override public Object getValue(" + className + " data, int type) {\n");
   353                 w.append("      switch (type) {\n");
   354                 for (int i = 0; i < propsGetSet.size(); i++) {
   355                     final String get = propsGetSet.get(i).getter;
   356                     if (get != null) {
   357                         w.append("        case " + i + ": return data." + strip(get) + "();\n");
   358                     }
   359                 }
   360                 w.append("      }\n");
   361                 w.append("      throw new UnsupportedOperationException();\n");
   362                 w.append("    }\n");
   363                 w.append("    @Override public void call(" + className + " model, int type, Object data, Object ev) throws Exception {\n");
   364                 w.append("      switch (type) {\n");
   365                 for (int i = 0; i < functions.size(); i += 2) {
   366                     final String name = (String)functions.get(i);
   367                     final Object param = functions.get(i + 1);
   368                     if (param instanceof ExecutableElement) {
   369                         ExecutableElement ee = (ExecutableElement)param;
   370                         w.append("        case " + (i / 2) + ":\n");
   371                         w.append("          ").append(((TypeElement)e).getQualifiedName()).append(".").append(name).append("(");
   372                         w.append(wrapParams(ee, null, className, "model", "ev", "data"));
   373                         w.append(");\n");
   374                         w.append("          return;\n");
   375                     } else {
   376                         String call = (String)param;
   377                         w.append("        case " + (i / 2) + ":\n"); // model." + name + "(data, ev); return;\n");
   378                         w.append("          ").append(call).append("\n");
   379                         w.append("          return;\n");
   380                         
   381                     }
   382                 }
   383                 w.append("      }\n");
   384                 w.append("      throw new UnsupportedOperationException();\n");
   385                 w.append("    }\n");
   386                 w.append("    @Override public org.netbeans.html.json.spi.Proto protoFor(Object obj) {\n");
   387                 w.append("      return ((" + className + ")obj).proto;");
   388                 w.append("    }\n");
   389                 w.append("    @Override public void onChange(" + className + " model, int type) {\n");
   390                 w.append("      switch (type) {\n");
   391                 {
   392                     String[] arr = functionDeps.keySet().toArray(new String[0]);
   393                     for (int i = 0; i < arr.length; i++) {
   394                         Collection<String> onChange = functionDeps.get(arr[i]);
   395                         if (onChange != null) {
   396                             w.append("      case " + i + ":\n");
   397                             for (String call : onChange) {
   398                                 w.append("      ").append(call).append("\n");
   399                             }
   400                             w.write("      return;\n");
   401                         }
   402                     }
   403                 }
   404                 w.append("    }\n");
   405                 w.append("      throw new UnsupportedOperationException();\n");
   406                 w.append("    }\n");
   407                 w.append(onReceiveType);
   408                 w.append("    @Override public " + className + " read(net.java.html.BrwsrCtx c, Object json) { return new " + className + "(c, json); }\n");
   409                 w.append("    @Override public " + className + " cloneTo(" + className + " o, net.java.html.BrwsrCtx c) { return o.clone(c); }\n");
   410                 w.append("  }\n");
   411                 w.append("  private ").append(className).append("(net.java.html.BrwsrCtx c, Object json) {\n");
   412                 w.append("    this(c);\n");
   413                 int values = 0;
   414                 for (int i = 0; i < propsGetSet.size(); i++) {
   415                     Prprt p = findPrprt(props, propsGetSet.get(i).name);
   416                     if (p == null) {
   417                         continue;
   418                     }
   419                     values++;
   420                 }
   421                 w.append("    Object[] ret = new Object[" + values + "];\n");
   422                 w.append("    proto.extract(json, new String[] {\n");
   423                 for (int i = 0; i < propsGetSet.size(); i++) {
   424                     Prprt p = findPrprt(props, propsGetSet.get(i).name);
   425                     if (p == null) {
   426                         continue;
   427                     }
   428                     w.append("      \"").append(propsGetSet.get(i).name).append("\",\n");
   429                 }
   430                 w.append("    }, ret);\n");
   431                 for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i++) {
   432                     final String pn = propsGetSet.get(i).name;
   433                     Prprt p = findPrprt(props, pn);
   434                     if (p == null || prop >= props.length) {
   435                         continue;
   436                     }
   437                     boolean[] isModel = { false };
   438                     boolean[] isEnum = { false };
   439                     boolean isPrimitive[] = { false };
   440                     String type = checkType(props[prop++], isModel, isEnum, isPrimitive);
   441                     if (p.array()) {
   442                         w.append("    if (ret[" + cnt + "] instanceof Object[]) {\n");
   443                         w.append("      for (Object e : ((Object[])ret[" + cnt + "])) {\n");
   444                         if (isModel[0]) {
   445                             w.append("        this.prop_").append(pn).append(".add(proto.read");
   446                             w.append("(" + type + ".class, e));\n");
   447                         } else if (isEnum[0]) {
   448                             w.append("        this.prop_").append(pn);
   449                             w.append(".add(e == null ? null : ");
   450                             w.append(type).append(".valueOf(TYPE.stringValue(e)));\n");
   451                         } else {
   452                             if (isPrimitive(type)) {
   453                                 w.append("        this.prop_").append(pn).append(".add(TYPE.numberValue(e).");
   454                                 w.append(type).append("Value());\n");
   455                             } else {
   456                                 w.append("        this.prop_").append(pn).append(".add((");
   457                                 w.append(type).append(")e);\n");
   458                             }
   459                         }
   460                         w.append("      }\n");
   461                         w.append("    }\n");
   462                     } else {
   463                         if (isEnum[0]) {
   464                             w.append("    try {\n");
   465                             w.append("    this.prop_").append(pn);
   466                             w.append(" = ret[" + cnt + "] == null ? null : ");
   467                             w.append(type).append(".valueOf(TYPE.stringValue(ret[" + cnt + "]));\n");
   468                             w.append("    } catch (IllegalArgumentException ex) {\n");
   469                             w.append("      ex.printStackTrace();\n");
   470                             w.append("    }\n");
   471                         } else if (isPrimitive(type)) {
   472                             w.append("    this.prop_").append(pn);
   473                             w.append(" = ret[" + cnt + "] == null ? ");
   474                             if ("char".equals(type)) {
   475                                 w.append("0 : (TYPE.charValue(");
   476                             } else if ("boolean".equals(type)) {
   477                                 w.append("false : (TYPE.boolValue(");
   478                             } else {
   479                                 w.append("0 : (TYPE.numberValue(");
   480                             }
   481                             w.append("ret[" + cnt + "])).");
   482                             w.append(type).append("Value();\n");
   483                         } else if (isModel[0]) {
   484                             w.append("    this.prop_").append(pn).append(" = proto.read");
   485                             w.append("(" + type + ".class, ");
   486                             w.append("ret[" + cnt + "]);\n");
   487                         }else {
   488                             w.append("    this.prop_").append(pn);
   489                             w.append(" = (").append(type).append(')');
   490                             w.append("ret[" + cnt + "];\n");
   491                         }
   492                     }
   493                     cnt++;
   494                 }
   495                 w.append("  }\n");
   496                 writeToString(props, w);
   497                 writeClone(className, props, w);
   498                 w.write("  /** Activates this model instance in the current {@link \n"
   499                     + "net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}. \n"
   500                     + "In case of using Knockout technology, this means to \n"
   501                     + "bind JSON like data in this model instance with Knockout tags in \n"
   502                     + "the surrounding HTML page.\n"
   503                     + "*/\n"
   504                 );
   505                 w.write("  public " + className + " applyBindings() {\n");
   506                 w.write("    proto.applyBindings();\n");
   507                 w.write("    return this;\n");
   508                 w.write("  }\n");
   509                 w.write("  public boolean equals(Object o) {\n");
   510                 w.write("    if (o == this) return true;\n");
   511                 w.write("    if (!(o instanceof " + className + ")) return false;\n");
   512                 w.write("    " + className + " p = (" + className + ")o;\n");
   513                 for (Prprt p : props) {
   514                     w.write("    if (!TYPE.isSame(prop_" + p.name() + ", p.prop_" + p.name() + ")) return false;\n");
   515                 }
   516                 w.write("    return true;\n");
   517                 w.write("  }\n");
   518                 w.write("  public int hashCode() {\n");
   519                 w.write("    int h = " + className + ".class.getName().hashCode();\n");
   520                 for (Prprt p : props) {
   521                     w.write("    h = TYPE.hashPlus(prop_" + p.name() + ", h);\n");
   522                 }
   523                 w.write("    return h;\n");
   524                 w.write("  }\n");
   525                 w.write("}\n");
   526             } finally {
   527                 w.close();
   528             }
   529         } catch (IOException ex) {
   530             error("Can't create " + className + ".java", e);
   531             return false;
   532         }
   533         return ok;
   534     }
   535     
   536     private boolean generateProperties(
   537         Element where,
   538         Writer w, String className, Prprt[] properties,
   539         List<GetSet> props, 
   540         Map<String,Collection<String[]>> deps,
   541         Map<String,Collection<String>> functionDeps
   542     ) throws IOException {
   543         boolean ok = true;
   544         for (Prprt p : properties) {
   545             final String tn;
   546             tn = typeName(where, p);
   547             String[] gs = toGetSet(p.name(), tn, p.array());
   548             String castTo;
   549             
   550             if (p.array()) {
   551                 w.write("  private final java.util.List<" + tn + "> prop_" + p.name() + ";\n");
   552             
   553                 castTo = "java.util.List";
   554                 w.write("  public java.util.List<" + tn + "> " + gs[0] + "() {\n");
   555                 w.write("    proto.accessProperty(\"" + p.name() + "\");\n");
   556                 w.write("    return prop_" + p.name() + ";\n");
   557                 w.write("  }\n");
   558             } else {
   559                 castTo = tn;
   560                 w.write("  private " + tn + " prop_" + p.name() + ";\n");
   561                 w.write("  public " + tn + " " + gs[0] + "() {\n");
   562                 w.write("    proto.accessProperty(\"" + p.name() + "\");\n");
   563                 w.write("    return prop_" + p.name() + ";\n");
   564                 w.write("  }\n");
   565                 w.write("  public void " + gs[1] + "(" + tn + " v) {\n");
   566                 w.write("    proto.verifyUnlocked();\n");
   567                 w.write("    if (TYPE.isSame(prop_" + p.name() + ", v)) return;\n");
   568                 w.write("    Object o = prop_" + p.name() + ";\n");
   569                 w.write("    prop_" + p.name() + " = v;\n");
   570                 w.write("    proto.valueHasMutated(\"" + p.name() + "\", o, v);\n");
   571                 {
   572                     Collection<String[]> dependants = deps.get(p.name());
   573                     if (dependants != null) {
   574                         for (String[] pair : dependants) {
   575                             w.write("    proto.valueHasMutated(\"" + pair[0] + "\", null, " + pair[1] + "());\n"); 
   576                         }
   577                     }
   578                 }
   579                 {
   580                     Collection<String> dependants = functionDeps.get(p.name());
   581                     if (dependants != null) {
   582                         w.append(className).append(" model = ").append(className).append(".this;\n");
   583                         for (String call : dependants) {
   584                             w.append("  ").append(call);
   585                         }
   586                     }
   587                 }
   588                 w.write("  }\n");
   589             }
   590             
   591             for (int i = 0; i < props.size(); i++) {
   592                 if (props.get(i).name.equals(p.name())) {
   593                     error("Cannot have the name " + p.name() + " defined twice", where);
   594                     ok = false;
   595                 }
   596             }
   597             
   598             props.add(new GetSet(
   599                 p.name(),
   600                 gs[2],
   601                 gs[3],
   602                 tn,
   603                 gs[3] == null && !p.array()
   604             ));
   605         }
   606         return ok;
   607     }
   608 
   609     private boolean generateComputedProperties(
   610         Writer w, Prprt[] fixedProps,
   611         Collection<? extends Element> arr, Collection<GetSet> props,
   612         Map<String,Collection<String[]>> deps
   613     ) throws IOException {
   614         boolean ok = true;
   615         for (Element e : arr) {
   616             if (e.getKind() != ElementKind.METHOD) {
   617                 continue;
   618             }
   619             final ComputedProperty cp = e.getAnnotation(ComputedProperty.class);
   620             final Transitive tp = e.getAnnotation(Transitive.class);
   621             if (cp == null) {
   622                 continue;
   623             }
   624             if (!e.getModifiers().contains(Modifier.STATIC)) {
   625                 error("Method " + e.getSimpleName() + " has to be static when annotated by @ComputedProperty", e);
   626                 ok = false;
   627                 continue;
   628             }
   629             ExecutableElement ee = (ExecutableElement)e;
   630             final TypeMirror rt = ee.getReturnType();
   631             final Types tu = processingEnv.getTypeUtils();
   632             TypeMirror ert = tu.erasure(rt);
   633             String tn = fqn(ert, ee);
   634             boolean array = false;
   635             final TypeMirror toCheck;
   636             if (tn.equals("java.util.List")) {
   637                 array = true;
   638                 toCheck = ((DeclaredType)rt).getTypeArguments().get(0);
   639             } else {
   640                 toCheck = rt;
   641             }
   642             
   643             final String sn = ee.getSimpleName().toString();
   644             
   645             if (toCheck.getKind().isPrimitive()) {
   646                 // OK
   647             } else {
   648                 TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
   649                 TypeMirror enumType = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
   650 
   651                 if (tu.isSubtype(toCheck, stringType)) {
   652                     // OK
   653                 } else if (tu.isSubtype(tu.erasure(toCheck), tu.erasure(enumType))) {
   654                     // OK
   655                 } else if (isModel(toCheck)) {
   656                     // OK
   657                 } else {
   658                     try {
   659                         tu.unboxedType(toCheck);
   660                         // boxed types are OK
   661                     } catch (IllegalArgumentException ex) {
   662                         ok = false;
   663                         error(sn + " cannot return " + toCheck, e);
   664                     }
   665                 }
   666             }
   667             
   668             String[] gs = toGetSet(sn, tn, array);
   669             
   670             w.write("  public " + tn);
   671             if (array) {
   672                 w.write("<" + toCheck + ">");
   673             }
   674             w.write(" " + gs[0] + "() {\n");
   675             int arg = 0;
   676             boolean deep = false;
   677             for (VariableElement pe : ee.getParameters()) {
   678                 final String dn = pe.getSimpleName().toString();
   679                 
   680                 if (!verifyPropName(pe, dn, fixedProps)) {
   681                     ok = false;
   682                 }
   683                 final TypeMirror pt = pe.asType();
   684                 if (isModel(pt)) {
   685                     deep = true;
   686                 }
   687                 final String dt = fqn(pt, ee);
   688                 if (dt.startsWith("java.util.List") && pt instanceof DeclaredType) {
   689                     final List<? extends TypeMirror> ptArgs = ((DeclaredType)pt).getTypeArguments();
   690                     if (ptArgs.size() == 1 && isModel(ptArgs.get(0))) {
   691                         deep = true;
   692                     }
   693                 }
   694                 String[] call = toGetSet(dn, dt, false);
   695                 w.write("    " + dt + " arg" + (++arg) + " = ");
   696                 w.write(call[0] + "();\n");
   697                 
   698                 Collection<String[]> depends = deps.get(dn);
   699                 if (depends == null) {
   700                     depends = new LinkedHashSet<String[]>();
   701                     deps.put(dn, depends);
   702                 }
   703                 depends.add(new String[] { sn, gs[0] });
   704             }
   705             w.write("    try {\n");
   706             if (tp != null) {
   707                 deep = tp.deep();
   708             }
   709             if (deep) {
   710                 w.write("      proto.acquireLock(\"" + sn + "\");\n");
   711             } else {
   712                 w.write("      proto.acquireLock();\n");
   713             }
   714             w.write("      return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
   715             String sep = "";
   716             for (int i = 1; i <= arg; i++) {
   717                 w.write(sep);
   718                 w.write("arg" + i);
   719                 sep = ", ";
   720             }
   721             w.write(");\n");
   722             w.write("    } finally {\n");
   723             w.write("      proto.releaseLock();\n");
   724             w.write("    }\n");
   725             w.write("  }\n");
   726 
   727             props.add(new GetSet(
   728                 e.getSimpleName().toString(),
   729                 gs[2],
   730                 null,
   731                 tn,
   732                 true
   733             ));
   734         }
   735         
   736         return ok;
   737     }
   738 
   739     private static String[] toGetSet(String name, String type, boolean array) {
   740         String n = Character.toUpperCase(name.charAt(0)) + name.substring(1);
   741         String bck2brwsrType = "L" + type.replace('.', '_') + "_2";
   742         if ("int".equals(type)) {
   743             bck2brwsrType = "I";
   744         }
   745         if ("double".equals(type)) {
   746             bck2brwsrType = "D";
   747         }
   748         String pref = "get";
   749         if ("boolean".equals(type)) {
   750             pref = "is";
   751             bck2brwsrType = "Z";
   752         }
   753         final String nu = n.replace('.', '_');
   754         if (array) {
   755             return new String[] { 
   756                 "get" + n,
   757                 null,
   758                 "get" + nu + "__Ljava_util_List_2",
   759                 null
   760             };
   761         }
   762         return new String[]{
   763             pref + n, 
   764             "set" + n, 
   765             pref + nu + "__" + bck2brwsrType,
   766             "set" + nu + "__V" + bck2brwsrType
   767         };
   768     }
   769 
   770     private String typeName(Element where, Prprt p) {
   771         String ret;
   772         boolean[] isModel = { false };
   773         boolean[] isEnum = { false };
   774         boolean isPrimitive[] = { false };
   775         ret = checkType(p, isModel, isEnum, isPrimitive);
   776         if (p.array()) {
   777             String bt = findBoxedType(ret);
   778             if (bt != null) {
   779                 return bt;
   780             }
   781         }
   782         return ret;
   783     }
   784     
   785     private static String findBoxedType(String ret) {
   786         if (ret.equals("boolean")) {
   787             return Boolean.class.getName();
   788         }
   789         if (ret.equals("byte")) {
   790             return Byte.class.getName();
   791         }
   792         if (ret.equals("short")) {
   793             return Short.class.getName();
   794         }
   795         if (ret.equals("char")) {
   796             return Character.class.getName();
   797         }
   798         if (ret.equals("int")) {
   799             return Integer.class.getName();
   800         }
   801         if (ret.equals("long")) {
   802             return Long.class.getName();
   803         }
   804         if (ret.equals("float")) {
   805             return Float.class.getName();
   806         }
   807         if (ret.equals("double")) {
   808             return Double.class.getName();
   809         }
   810         return null;
   811     }
   812 
   813     private boolean verifyPropName(Element e, String propName, Prprt[] existingProps) {
   814         StringBuilder sb = new StringBuilder();
   815         String sep = "";
   816         for (Prprt Prprt : existingProps) {
   817             if (Prprt.name().equals(propName)) {
   818                 return true;
   819             }
   820             sb.append(sep);
   821             sb.append('"');
   822             sb.append(Prprt.name());
   823             sb.append('"');
   824             sep = ", ";
   825         }
   826         error(
   827             propName + " is not one of known properties: " + sb
   828             , e
   829         );
   830         return false;
   831     }
   832 
   833     private static String findPkgName(Element e) {
   834         for (;;) {
   835             if (e.getKind() == ElementKind.PACKAGE) {
   836                 return ((PackageElement)e).getQualifiedName().toString();
   837             }
   838             e = e.getEnclosingElement();
   839         }
   840     }
   841 
   842     private boolean generateFunctions(
   843         Element clazz, StringWriter body, String className, 
   844         List<? extends Element> enclosedElements, List<Object> functions
   845     ) {
   846         for (Element m : enclosedElements) {
   847             if (m.getKind() != ElementKind.METHOD) {
   848                 continue;
   849             }
   850             ExecutableElement e = (ExecutableElement)m;
   851             Function onF = e.getAnnotation(Function.class);
   852             if (onF == null) {
   853                 continue;
   854             }
   855             if (!e.getModifiers().contains(Modifier.STATIC)) {
   856                 error("@OnFunction method needs to be static", e);
   857                 return false;
   858             }
   859             if (e.getModifiers().contains(Modifier.PRIVATE)) {
   860                 error("@OnFunction method cannot be private", e);
   861                 return false;
   862             }
   863             if (e.getReturnType().getKind() != TypeKind.VOID) {
   864                 error("@OnFunction method should return void", e);
   865                 return false;
   866             }
   867             String n = e.getSimpleName().toString();
   868             functions.add(n);
   869             functions.add(e);
   870         }
   871         return true;
   872     }
   873 
   874     private boolean generateOnChange(Element clazz, Map<String,Collection<String[]>> propDeps,
   875         Prprt[] properties, String className, 
   876         Map<String, Collection<String>> functionDeps
   877     ) {
   878         for (Element m : clazz.getEnclosedElements()) {
   879             if (m.getKind() != ElementKind.METHOD) {
   880                 continue;
   881             }
   882             ExecutableElement e = (ExecutableElement) m;
   883             OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class);
   884             if (onPC == null) {
   885                 continue;
   886             }
   887             for (String pn : onPC.value()) {
   888                 if (findPrprt(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
   889                     error("No Prprt named '" + pn + "' in the model", clazz);
   890                     return false;
   891                 }
   892             }
   893             if (!e.getModifiers().contains(Modifier.STATIC)) {
   894                 error("@OnPrprtChange method needs to be static", e);
   895                 return false;
   896             }
   897             if (e.getModifiers().contains(Modifier.PRIVATE)) {
   898                 error("@OnPrprtChange method cannot be private", e);
   899                 return false;
   900             }
   901             if (e.getReturnType().getKind() != TypeKind.VOID) {
   902                 error("@OnPrprtChange method should return void", e);
   903                 return false;
   904             }
   905             String n = e.getSimpleName().toString();
   906             
   907             
   908             for (String pn : onPC.value()) {
   909                 StringBuilder call = new StringBuilder();
   910                 call.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
   911                 call.append(wrapPropName(e, className, "name", pn));
   912                 call.append(");\n");
   913                 
   914                 Collection<String> change = functionDeps.get(pn);
   915                 if (change == null) {
   916                     change = new ArrayList<String>();
   917                     functionDeps.put(pn, change);
   918                 }
   919                 change.add(call.toString());
   920                 for (String dpn : findDerivedFrom(propDeps, pn)) {
   921                     change = functionDeps.get(dpn);
   922                     if (change == null) {
   923                         change = new ArrayList<String>();
   924                         functionDeps.put(dpn, change);
   925                     }
   926                     change.add(call.toString());
   927                 }
   928             }
   929         }
   930         return true;
   931     }
   932 
   933     private boolean generateOperation(Element clazz, 
   934         StringWriter body, String className, 
   935         List<? extends Element> enclosedElements,
   936         List<Object> functions
   937     ) {
   938         for (Element m : enclosedElements) {
   939             if (m.getKind() != ElementKind.METHOD) {
   940                 continue;
   941             }
   942             ExecutableElement e = (ExecutableElement)m;
   943             ModelOperation mO = e.getAnnotation(ModelOperation.class);
   944             if (mO == null) {
   945                 continue;
   946             }
   947             if (!e.getModifiers().contains(Modifier.STATIC)) {
   948                 error("@ModelOperation method needs to be static", e);
   949                 return false;
   950             }
   951             if (e.getModifiers().contains(Modifier.PRIVATE)) {
   952                 error("@ModelOperation method cannot be private", e);
   953                 return false;
   954             }
   955             if (e.getReturnType().getKind() != TypeKind.VOID) {
   956                 error("@ModelOperation method should return void", e);
   957                 return false;
   958             }
   959             List<String> args = new ArrayList<String>();
   960             {
   961                 body.append("  public void ").append(m.getSimpleName()).append("(");
   962                 String sep = "";
   963                 boolean checkFirst = true;
   964                 for (VariableElement ve : e.getParameters()) {
   965                     final TypeMirror type = ve.asType();
   966                     CharSequence simpleName;
   967                     if (type.getKind() == TypeKind.DECLARED) {
   968                         simpleName = ((DeclaredType)type).asElement().getSimpleName();
   969                     } else {
   970                         simpleName = type.toString();
   971                     }
   972                     if (checkFirst && simpleName.toString().equals(className)) {
   973                         checkFirst = false;
   974                     } else {
   975                         if (checkFirst) {
   976                             error("First parameter of @ModelOperation method must be " + className, m);
   977                             return false;
   978                         }
   979                         args.add(ve.getSimpleName().toString());
   980                         body.append(sep).append("final ");
   981                         body.append(ve.asType().toString()).append(" ");
   982                         body.append(ve.toString());
   983                         sep = ", ";
   984                     }
   985                 }
   986                 body.append(") {\n");
   987                 int idx = functions.size() / 2;
   988                 functions.add(m.getSimpleName().toString());
   989                 body.append("    proto.runInBrowser(" + idx);
   990                 for (String s : args) {
   991                     body.append(", ").append(s);
   992                 }
   993                 body.append(");\n");
   994                 body.append("  }\n");
   995 
   996                 StringBuilder call = new StringBuilder();
   997                 call.append("{ Object[] arr = (Object[])data; ");
   998                 call.append(inPckName(clazz)).append(".").append(m.getSimpleName()).append("(");
   999                 int i = 0;
  1000                 for (VariableElement ve : e.getParameters()) {
  1001                     if (i++ == 0) {
  1002                         call.append("model");
  1003                         continue;
  1004                     }
  1005                     String type = ve.asType().toString();
  1006                     String boxedType = findBoxedType(type);
  1007                     if (boxedType != null) {
  1008                         type = boxedType;
  1009                     }
  1010                     call.append(", ").append("(").append(type).append(")arr[").append(i - 2).append("]");
  1011                 }
  1012                 call.append("); }");
  1013                 functions.add(call.toString());
  1014             }
  1015             
  1016         }
  1017         return true;
  1018     }
  1019     
  1020     
  1021     private boolean generateReceive(
  1022         Element clazz, StringWriter body, String className, 
  1023         List<? extends Element> enclosedElements, StringBuilder inType
  1024     ) {
  1025         inType.append("  @Override public void onMessage(").append(className).append(" model, int index, int type, Object data, Object[] params) {\n");
  1026         inType.append("    switch (index) {\n");
  1027         int index = 0;
  1028         for (Element m : enclosedElements) {
  1029             if (m.getKind() != ElementKind.METHOD) {
  1030                 continue;
  1031             }
  1032             ExecutableElement e = (ExecutableElement)m;
  1033             OnReceive onR = e.getAnnotation(OnReceive.class);
  1034             if (onR == null) {
  1035                 continue;
  1036             }
  1037             if (!e.getModifiers().contains(Modifier.STATIC)) {
  1038                 error("@OnReceive method needs to be static", e);
  1039                 return false;
  1040             }
  1041             if (e.getModifiers().contains(Modifier.PRIVATE)) {
  1042                 error("@OnReceive method cannot be private", e);
  1043                 return false;
  1044             }
  1045             if (e.getReturnType().getKind() != TypeKind.VOID) {
  1046                 error("@OnReceive method should return void", e);
  1047                 return false;
  1048             }
  1049             if (!onR.jsonp().isEmpty() && !"GET".equals(onR.method())) {
  1050                 error("JSONP works only with GET transport method", e);
  1051             }
  1052             String dataMirror = findDataSpecified(e, onR);
  1053             if ("PUT".equals(onR.method()) && dataMirror == null) {
  1054                 error("PUT method needs to specify a data() class", e);
  1055                 return false;
  1056             }
  1057             if ("POST".equals(onR.method()) && dataMirror == null) {
  1058                 error("POST method needs to specify a data() class", e);
  1059                 return false;
  1060             }
  1061             if (e.getParameters().size() < 2) {
  1062                 error("@OnReceive method needs at least two parameters", e);
  1063             }
  1064             int expectsList = 0;
  1065             List<String> args = new ArrayList<String>();
  1066             List<String> params = new ArrayList<String>();
  1067             // first argument is model class
  1068             {
  1069                 TypeMirror type = e.getParameters().get(0).asType();
  1070                 CharSequence simpleName;
  1071                 if (type.getKind() == TypeKind.DECLARED) {
  1072                     simpleName = ((DeclaredType) type).asElement().getSimpleName();
  1073                 } else {
  1074                     simpleName = type.toString();
  1075                 }
  1076                 if (simpleName.toString().equals(className)) {
  1077                     args.add("model");
  1078                 } else {
  1079                     error("First parameter needs to be " + className, e);
  1080                     return false;
  1081                 }                    
  1082             }
  1083                 
  1084             String modelClass;
  1085             {
  1086                 final Types tu = processingEnv.getTypeUtils();
  1087                 TypeMirror type = e.getParameters().get(1).asType();
  1088                 TypeMirror modelType = null;
  1089                 TypeMirror ert = tu.erasure(type);
  1090 
  1091                 if (isModel(type)) {
  1092                     modelType = type;
  1093                 } else if (type.getKind() == TypeKind.ARRAY) {
  1094                     modelType = ((ArrayType)type).getComponentType();
  1095                     expectsList = 1;
  1096                 } else if ("java.util.List".equals(fqn(ert, e))) {
  1097                     List<? extends TypeMirror> typeArgs = ((DeclaredType)type).getTypeArguments();
  1098                     if (typeArgs.size() == 1) {
  1099                         modelType = typeArgs.get(0);
  1100                         expectsList = 2;
  1101                     }
  1102                 } else if (type.toString().equals("java.lang.String")) {
  1103                     modelType = type;
  1104                 }
  1105                 if (modelType == null) {
  1106                     error("Second arguments needs to be a model, String or array or List of models", e);
  1107                     return false;
  1108                 }
  1109                 modelClass = modelType.toString();
  1110                 if (expectsList == 1) {
  1111                     args.add("arr");
  1112                 } else if (expectsList == 2) {
  1113                     args.add("java.util.Arrays.asList(arr)");
  1114                 } else {
  1115                     args.add("arr[0]");
  1116                 }
  1117             }
  1118             String n = e.getSimpleName().toString();
  1119             final boolean isWebSocket = "WebSocket".equals(onR.method());
  1120             if (isWebSocket) {
  1121                 body.append("  /** Performs WebSocket communication. Call with <code>null</code> data parameter\n");
  1122                 body.append("  * to open the connection (even if not required). Call with non-null data to\n");
  1123                 body.append("  * send messages to server. Call again with <code>null</code> data to close the socket.\n");
  1124                 body.append("  */\n");
  1125             }
  1126             body.append("  public void ").append(n).append("(");
  1127             StringBuilder urlBefore = new StringBuilder();
  1128             StringBuilder urlAfter = new StringBuilder();
  1129             String jsonpVarName = null;
  1130             {
  1131                 String sep = "";
  1132                 boolean skipJSONP = onR.jsonp().isEmpty();
  1133                 for (String p : findParamNames(e, onR.url(), onR.jsonp(), urlBefore, urlAfter)) {
  1134                     if (!skipJSONP && p.equals(onR.jsonp())) {
  1135                         skipJSONP = true;
  1136                         jsonpVarName = p;
  1137                         continue;
  1138                     }
  1139                     body.append(sep);
  1140                     body.append("String ").append(p);
  1141                     sep = ", ";
  1142                 }
  1143                 if (!skipJSONP) {
  1144                     error(
  1145                         "Name of jsonp attribute ('" + onR.jsonp() + 
  1146                         "') is not used in url attribute '" + onR.url() + "'", e
  1147                     );
  1148                 }
  1149                 if (dataMirror != null) {
  1150                     body.append(sep).append(dataMirror.toString()).append(" data");
  1151                 }
  1152                 for (int i = 2; i < e.getParameters().size(); i++) {
  1153                     if (isWebSocket) {
  1154                         error("@OnReceive(method=\"WebSocket\") can only have two arguments", e);
  1155                         return false;
  1156                     }
  1157                     
  1158                     VariableElement ve = e.getParameters().get(i);
  1159                     body.append(sep).append(ve.asType().toString()).append(" ").append(ve.getSimpleName());
  1160                     final String tp = ve.asType().toString();
  1161                     String btn = findBoxedType(tp);
  1162                     if (btn == null) {
  1163                         btn = tp;
  1164                     }
  1165                     args.add("(" + btn + ")params[" + (i - 2) + "]");
  1166                     params.add(ve.getSimpleName().toString());
  1167                     sep = ", ";
  1168                 }
  1169             }
  1170             body.append(") {\n");
  1171             boolean webSocket = onR.method().equals("WebSocket");
  1172             if (webSocket) {
  1173                 if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
  1174                     return false;
  1175                 }
  1176                 body.append("  }\n");
  1177                 body.append("  private Object ws_" + e.getSimpleName() + ";\n");
  1178             } else {
  1179                 if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
  1180                     return false;
  1181                 }
  1182                 body.append("  }\n");
  1183             }
  1184         }
  1185         inType.append("    }\n");
  1186         inType.append("    throw new UnsupportedOperationException(\"index: \" + index + \" type: \" + type);\n");
  1187         inType.append("  }\n");
  1188         return true;
  1189     }
  1190 
  1191     private boolean generateJSONReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
  1192         body.append(
  1193             "    case " + index + ": {\n" +
  1194             "      if (type == 2) { /* on error */\n" +
  1195             "        Exception ex = (Exception)data;\n"
  1196             );
  1197         if (onR.onError().isEmpty()) {
  1198             body.append(
  1199                 "        ex.printStackTrace();\n"
  1200                 );
  1201         } else {
  1202             if (!findOnError(e, ((TypeElement)clazz), onR.onError(), className)) {
  1203                 return true;
  1204             }
  1205             body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
  1206             body.append("model, ex);\n");
  1207         }
  1208         body.append(
  1209             "        return;\n" +
  1210             "      } else if (type == 1) {\n" +
  1211             "        Object[] ev = (Object[])data;\n"
  1212             );
  1213         if (expectsList) {
  1214             body.append(
  1215                 "        " + modelClass + "[] arr = new " + modelClass + "[ev.length];\n"
  1216                 );
  1217         } else {
  1218             body.append(
  1219                 "        " + modelClass + "[] arr = { null };\n"
  1220                 );
  1221         }
  1222         body.append(
  1223             "        TYPE.copyJSON(model.proto.getContext(), ev, " + modelClass + ".class, arr);\n"
  1224         );
  1225         {
  1226             body.append("        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  1227             String sep = "";
  1228             for (String arg : args) {
  1229                 body.append(sep);
  1230                 body.append(arg);
  1231                 sep = ", ";
  1232             }
  1233             body.append(");\n");
  1234         }
  1235         body.append(
  1236             "        return;\n" +
  1237             "      }\n" +
  1238             "    }\n"
  1239             );
  1240         method.append("    proto.loadJSON(" + index + ",\n        ");
  1241         method.append(urlBefore).append(", ");
  1242         if (jsonpVarName != null) {
  1243             method.append(urlAfter);
  1244         } else {
  1245             method.append("null");
  1246         }
  1247         if (!"GET".equals(onR.method()) || dataMirror != null) {
  1248             method.append(", \"").append(onR.method()).append('"');
  1249             if (dataMirror != null) {
  1250                 method.append(", data");
  1251             } else {
  1252                 method.append(", null");
  1253             }
  1254         } else {
  1255             method.append(", null, null");
  1256         }
  1257         for (String a : params) {
  1258             method.append(", ").append(a);
  1259         }
  1260         method.append(");\n");
  1261         return false;
  1262     }
  1263     
  1264     private boolean generateWSReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
  1265         body.append(
  1266             "    case " + index + ": {\n" +
  1267             "      if (type == 0) { /* on open */\n" +
  1268             "        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  1269         {
  1270             String sep = "";
  1271             for (String arg : args) {
  1272                 body.append(sep);
  1273                 if (arg.startsWith("arr") || arg.startsWith("java.util.Array")) {
  1274                     body.append("null");
  1275                 } else {
  1276                     body.append(arg);
  1277                 }
  1278                 sep = ", ";
  1279             }
  1280         }
  1281         body.append(");\n");
  1282         body.append(
  1283             "        return;\n" +
  1284             "      } else if (type == 2) { /* on error */\n" +
  1285             "        Exception value = (Exception)data;\n"
  1286             );
  1287         if (onR.onError().isEmpty()) {
  1288             body.append(
  1289                 "        value.printStackTrace();\n"
  1290                 );
  1291         } else {
  1292             if (!findOnError(e, ((TypeElement)clazz), onR.onError(), className)) {
  1293                 return true;
  1294             }
  1295             body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
  1296             body.append("model, value);\n");
  1297         }
  1298         body.append(
  1299             "        return;\n" +
  1300             "      } else if (type == 1) {\n" +
  1301             "        Object[] ev = (Object[])data;\n"
  1302         );
  1303         if (expectsList) {
  1304             body.append(
  1305                 "        " + modelClass + "[] arr = new " + modelClass + "[ev.length];\n"
  1306                 );
  1307         } else {
  1308             body.append(
  1309                 "        " + modelClass + "[] arr = { null };\n"
  1310                 );
  1311         }
  1312         body.append(
  1313             "        TYPE.copyJSON(model.proto.getContext(), ev, " + modelClass + ".class, arr);\n"
  1314         );
  1315         {
  1316             body.append("        ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  1317             String sep = "";
  1318             for (String arg : args) {
  1319                 body.append(sep);
  1320                 body.append(arg);
  1321                 sep = ", ";
  1322             }
  1323             body.append(");\n");
  1324         }
  1325         body.append(
  1326             "        return;\n" +
  1327             "      }"
  1328         );
  1329         if (!onR.onError().isEmpty()) {
  1330             body.append(" else if (type == 3) { /* on close */\n");
  1331             body.append("        ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
  1332             body.append("model, null);\n");
  1333             body.append(
  1334                 "        return;" +
  1335                 "      }"
  1336             );
  1337         }
  1338         body.append("\n");
  1339         body.append("    }\n");
  1340         method.append("    if (this.ws_").append(e.getSimpleName()).append(" == null) {\n");
  1341         method.append("      this.ws_").append(e.getSimpleName());
  1342         method.append("= proto.wsOpen(" + index + ", ");
  1343         method.append(urlBefore).append(", data);\n");
  1344         method.append("    } else {\n");
  1345         method.append("      proto.wsSend(this.ws_").append(e.getSimpleName()).append(", ").append(urlBefore).append(", data");
  1346         for (String a : params) {
  1347             method.append(", ").append(a);
  1348         }
  1349         method.append(");\n");
  1350         method.append("    }\n");
  1351         return false;
  1352     }
  1353 
  1354     private CharSequence wrapParams(
  1355         ExecutableElement ee, String id, String className, String classRef, String evName, String dataName
  1356     ) {
  1357         TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
  1358         StringBuilder params = new StringBuilder();
  1359         boolean first = true;
  1360         for (VariableElement ve : ee.getParameters()) {
  1361             if (!first) {
  1362                 params.append(", ");
  1363             }
  1364             first = false;
  1365             String toCall = null;
  1366             String toFinish = null;
  1367             boolean addNull = true;
  1368             if (ve.asType() == stringType) {
  1369                 if (ve.getSimpleName().contentEquals("id")) {
  1370                     params.append('"').append(id).append('"');
  1371                     continue;
  1372                 }
  1373                 toCall = classRef + ".proto.toString(";
  1374             }
  1375             if (ve.asType().getKind() == TypeKind.DOUBLE) {
  1376                 toCall = classRef + ".proto.toNumber(";
  1377                 toFinish = ".doubleValue()";
  1378             }
  1379             if (ve.asType().getKind() == TypeKind.INT) {
  1380                 toCall = classRef + ".proto.toNumber(";
  1381                 toFinish = ".intValue()";
  1382             }
  1383             if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
  1384                 toCall = classRef + ".proto.toModel(" + ve.asType() + ".class, ";
  1385                 addNull = false;
  1386             }
  1387 
  1388             if (toCall != null) {
  1389                 params.append(toCall);
  1390                 if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
  1391                     params.append(dataName);
  1392                     if (addNull) {
  1393                         params.append(", null");
  1394                     }
  1395                 } else {
  1396                     if (evName == null) {
  1397                         final StringBuilder sb = new StringBuilder();
  1398                         sb.append("Unexpected string parameter name.");
  1399                         if (dataName != null) {
  1400                             sb.append(" Try \"").append(dataName).append("\"");
  1401                         }
  1402                         error(sb.toString(), ee);
  1403                     }
  1404                     params.append(evName);
  1405                     params.append(", \"");
  1406                     params.append(ve.getSimpleName().toString());
  1407                     params.append("\"");
  1408                 }
  1409                 params.append(")");
  1410                 if (toFinish != null) {
  1411                     params.append(toFinish);
  1412                 }
  1413                 continue;
  1414             }
  1415             String rn = fqn(ve.asType(), ee);
  1416             int last = rn.lastIndexOf('.');
  1417             if (last >= 0) {
  1418                 rn = rn.substring(last + 1);
  1419             }
  1420             if (rn.equals(className)) {
  1421                 params.append(classRef);
  1422                 continue;
  1423             }
  1424             error(
  1425                 "The annotated method can only accept " + className + " argument or argument named 'data'",
  1426                 ee
  1427             );
  1428         }
  1429         return params;
  1430     }
  1431     
  1432     
  1433     private CharSequence wrapPropName(
  1434         ExecutableElement ee, String className, String propName, String propValue
  1435     ) {
  1436         TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
  1437         StringBuilder params = new StringBuilder();
  1438         boolean first = true;
  1439         for (VariableElement ve : ee.getParameters()) {
  1440             if (!first) {
  1441                 params.append(", ");
  1442             }
  1443             first = false;
  1444             if (ve.asType() == stringType) {
  1445                 if (propName != null && ve.getSimpleName().contentEquals(propName)) {
  1446                     params.append('"').append(propValue).append('"');
  1447                 } else {
  1448                     error("Unexpected string parameter name. Try \"" + propName + "\".", ee);
  1449                 }
  1450                 continue;
  1451             }
  1452             String rn = fqn(ve.asType(), ee);
  1453             int last = rn.lastIndexOf('.');
  1454             if (last >= 0) {
  1455                 rn = rn.substring(last + 1);
  1456             }
  1457             if (rn.equals(className)) {
  1458                 params.append("model");
  1459                 continue;
  1460             }
  1461             error(
  1462                 "@OnPrprtChange method can only accept String or " + className + " arguments",
  1463                 ee);
  1464         }
  1465         return params;
  1466     }
  1467     
  1468     private boolean isModel(TypeMirror tm) {
  1469         if (tm.getKind() == TypeKind.ERROR) {
  1470             return true;
  1471         }
  1472         final Element e = processingEnv.getTypeUtils().asElement(tm);
  1473         if (e == null) {
  1474             return false;
  1475         }
  1476         for (Element ch : e.getEnclosedElements()) {
  1477             if (ch.getKind() == ElementKind.METHOD) {
  1478                 ExecutableElement ee = (ExecutableElement)ch;
  1479                 if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) {
  1480                     return true;
  1481                 }
  1482             }
  1483         }
  1484         return models.values().contains(e.getSimpleName().toString());
  1485     }
  1486     
  1487     private void writeToString(Prprt[] props, Writer w) throws IOException {
  1488         w.write("  public String toString() {\n");
  1489         w.write("    StringBuilder sb = new StringBuilder();\n");
  1490         w.write("    sb.append('{');\n");
  1491         String sep = "";
  1492         for (Prprt p : props) {
  1493             w.write(sep);
  1494             w.append("    sb.append('\"').append(\"" + p.name() + "\")");
  1495                 w.append(".append('\"').append(\":\");\n");
  1496             w.append("    sb.append(TYPE.toJSON(prop_");
  1497             w.append(p.name()).append("));\n");
  1498             sep =    "    sb.append(',');\n";
  1499         }
  1500         w.write("    sb.append('}');\n");
  1501         w.write("    return sb.toString();\n");
  1502         w.write("  }\n");
  1503     }
  1504     private void writeClone(String className, Prprt[] props, Writer w) throws IOException {
  1505         w.write("  public " + className + " clone() {\n");
  1506         w.write("    return clone(proto.getContext());\n");
  1507         w.write("  }\n");
  1508         w.write("  private " + className + " clone(net.java.html.BrwsrCtx ctx) {\n");
  1509         w.write("    " + className + " ret = new " + className + "(ctx);\n");
  1510         for (Prprt p : props) {
  1511             if (!p.array()) {
  1512                 boolean isModel[] = { false };
  1513                 boolean isEnum[] = { false };
  1514                 boolean isPrimitive[] = { false };
  1515                 checkType(p, isModel, isEnum, isPrimitive);
  1516                 if (!isModel[0]) {
  1517                     w.write("    ret.prop_" + p.name() + " = prop_" + p.name() + ";\n");
  1518                     continue;
  1519                 }
  1520                 w.write("    ret.prop_" + p.name() + " =  prop_" + p.name() + "  == null ? null : prop_" + p.name() + ".clone();\n");
  1521             } else {
  1522                 w.write("    proto.cloneList(ret.prop_" + p.name() + ", ctx, prop_" + p.name() + ");\n");
  1523             }
  1524         }
  1525         
  1526         w.write("    return ret;\n");
  1527         w.write("  }\n");
  1528     }
  1529 
  1530     private String inPckName(Element e) {
  1531         StringBuilder sb = new StringBuilder();
  1532         while (e.getKind() != ElementKind.PACKAGE) {
  1533             if (sb.length() == 0) {
  1534                 sb.append(e.getSimpleName());
  1535             } else {
  1536                 sb.insert(0, '.');
  1537                 sb.insert(0, e.getSimpleName());
  1538             }
  1539             e = e.getEnclosingElement();
  1540         }
  1541         return sb.toString();
  1542     }
  1543 
  1544     private String fqn(TypeMirror pt, Element relative) {
  1545         if (pt.getKind() == TypeKind.ERROR) {
  1546             final Elements eu = processingEnv.getElementUtils();
  1547             PackageElement pckg = eu.getPackageOf(relative);
  1548             return pckg.getQualifiedName() + "." + pt.toString();
  1549         }
  1550         return pt.toString();
  1551     }
  1552 
  1553     private String checkType(Prprt p, boolean[] isModel, boolean[] isEnum, boolean[] isPrimitive) {
  1554         TypeMirror tm;
  1555         try {
  1556             String ret = p.typeName(processingEnv);
  1557             TypeElement e = processingEnv.getElementUtils().getTypeElement(ret);
  1558             if (e == null) {
  1559                 isModel[0] = true;
  1560                 isEnum[0] = false;
  1561                 isPrimitive[0] = false;
  1562                 return ret;
  1563             }
  1564             tm = e.asType();
  1565         } catch (MirroredTypeException ex) {
  1566             tm = ex.getTypeMirror();
  1567         }
  1568         tm = processingEnv.getTypeUtils().erasure(tm);
  1569         if (isPrimitive[0] = tm.getKind().isPrimitive()) {
  1570             isEnum[0] = false;
  1571             isModel[0] = false;
  1572             return tm.toString();
  1573         }
  1574         final Element e = processingEnv.getTypeUtils().asElement(tm);
  1575         if (e.getKind() == ElementKind.CLASS && tm.getKind() == TypeKind.ERROR) {
  1576             isModel[0] = true;
  1577             isEnum[0] = false;
  1578             return e.getSimpleName().toString();
  1579         }
  1580         
  1581         final Model m = e == null ? null : e.getAnnotation(Model.class);
  1582         String ret;
  1583         if (m != null) {
  1584             ret = findPkgName(e) + '.' + m.className();
  1585             isModel[0] = true;
  1586             models.put(e, m.className());
  1587         } else if (findModelForMthd(e)) {
  1588             ret = ((TypeElement)e).getQualifiedName().toString();
  1589             isModel[0] = true;
  1590         } else {
  1591             ret = tm.toString();
  1592         }
  1593         TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
  1594         enm = processingEnv.getTypeUtils().erasure(enm);
  1595         isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
  1596         return ret;
  1597     }
  1598     
  1599     private static boolean findModelForMthd(Element clazz) {
  1600         if (clazz == null) {
  1601             return false;
  1602         }
  1603         for (Element e : clazz.getEnclosedElements()) {
  1604             if (e.getKind() == ElementKind.METHOD) {
  1605                 ExecutableElement ee = (ExecutableElement)e;
  1606                 if (
  1607                     ee.getSimpleName().contentEquals("modelFor") &&
  1608                     ee.getParameters().isEmpty()
  1609                 ) {
  1610                     return true;
  1611                 }
  1612             }
  1613         }
  1614         return false;
  1615     }
  1616 
  1617     private Iterable<String> findParamNames(
  1618         Element e, String url, String jsonParam, StringBuilder... both
  1619     ) {
  1620         List<String> params = new ArrayList<String>();
  1621         int wasJSON = 0;
  1622 
  1623         for (int pos = 0; ;) {
  1624             int next = url.indexOf('{', pos);
  1625             if (next == -1) {
  1626                 both[wasJSON].append('"')
  1627                     .append(url.substring(pos))
  1628                     .append('"');
  1629                 return params;
  1630             }
  1631             int close = url.indexOf('}', next);
  1632             if (close == -1) {
  1633                 error("Unbalanced '{' and '}' in " + url, e);
  1634                 return params;
  1635             }
  1636             final String paramName = url.substring(next + 1, close);
  1637             params.add(paramName);
  1638             if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
  1639                 both[wasJSON].append('"')
  1640                     .append(url.substring(pos, next))
  1641                     .append('"');
  1642                 wasJSON = 1;
  1643             } else {
  1644                 both[wasJSON].append('"')
  1645                     .append(url.substring(pos, next))
  1646                     .append("\" + ").append(paramName).append(" + ");
  1647             }
  1648             pos = close + 1;
  1649         }
  1650     }
  1651 
  1652     private static Prprt findPrprt(Prprt[] properties, String propName) {
  1653         for (Prprt p : properties) {
  1654             if (propName.equals(p.name())) {
  1655                 return p;
  1656             }
  1657         }
  1658         return null;
  1659     }
  1660 
  1661     private boolean isPrimitive(String type) {
  1662         return 
  1663             "int".equals(type) ||
  1664             "double".equals(type) ||
  1665             "long".equals(type) ||
  1666             "short".equals(type) ||
  1667             "byte".equals(type) ||
  1668             "char".equals(type) ||
  1669             "boolean".equals(type) ||
  1670             "float".equals(type);
  1671     }
  1672 
  1673     private static Collection<String> findDerivedFrom(Map<String, Collection<String[]>> propsDeps, String derivedProp) {
  1674         Set<String> names = new HashSet<String>();
  1675         for (Map.Entry<String, Collection<String[]>> e : propsDeps.entrySet()) {
  1676             for (String[] pair : e.getValue()) {
  1677                 if (pair[0].equals(derivedProp)) {
  1678                     names.add(e.getKey());
  1679                     break;
  1680                 }
  1681             }
  1682         }
  1683         return names;
  1684     }
  1685     
  1686     private Prprt[] createProps(Element e, Property[] arr) {
  1687         Prprt[] ret = Prprt.wrap(processingEnv, e, arr);
  1688         Prprt[] prev = verify.put(e, ret);
  1689         if (prev != null) {
  1690             error("Two sets of properties for ", e);
  1691         }
  1692         return ret;
  1693     }
  1694     
  1695     private static String strip(String s) {
  1696         int indx = s.indexOf("__");
  1697         if (indx >= 0) {
  1698             return s.substring(0, indx);
  1699         } else {
  1700             return s;
  1701         }
  1702     }
  1703 
  1704     private String findDataSpecified(ExecutableElement e, OnReceive onR) {
  1705         try {
  1706             return onR.data().getName();
  1707         } catch (MirroredTypeException ex) {
  1708             final TypeMirror tm = ex.getTypeMirror();
  1709             String name;
  1710             final Element te = processingEnv.getTypeUtils().asElement(tm);
  1711             if (te.getKind() == ElementKind.CLASS && tm.getKind() == TypeKind.ERROR) {
  1712                 name = te.getSimpleName().toString();
  1713             } else {
  1714                 name = tm.toString();
  1715             }
  1716             return "java.lang.Object".equals(name) ? null : name;
  1717         } catch (Exception ex) {
  1718             // fallback
  1719         }
  1720         
  1721         AnnotationMirror found = null;
  1722         for (AnnotationMirror am : e.getAnnotationMirrors()) {
  1723             if (am.getAnnotationType().toString().equals(OnReceive.class.getName())) {
  1724                 found = am;
  1725             }
  1726         }
  1727         if (found == null) {
  1728             return null;
  1729         }
  1730         
  1731         for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : found.getElementValues().entrySet()) {
  1732             ExecutableElement ee = entry.getKey();
  1733             AnnotationValue av = entry.getValue();
  1734             if (ee.getSimpleName().contentEquals("data")) {
  1735                 List<? extends Object> values = getAnnoValues(processingEnv, e, found);
  1736                 for (Object v : values) {
  1737                     String sv = v.toString();
  1738                     if (sv.startsWith("data = ") && sv.endsWith(".class")) {
  1739                         return sv.substring(7, sv.length() - 6);
  1740                     }
  1741                 }
  1742                 return "error";
  1743             }
  1744         }
  1745         return null;
  1746     }
  1747 
  1748     static List<? extends Object> getAnnoValues(ProcessingEnvironment pe, Element e, AnnotationMirror am) {
  1749         try {
  1750             Class<?> trees = Class.forName("com.sun.tools.javac.api.JavacTrees");
  1751             Method m = trees.getMethod("instance", ProcessingEnvironment.class);
  1752             Object instance = m.invoke(null, pe);
  1753             m = instance.getClass().getMethod("getPath", Element.class, AnnotationMirror.class);
  1754             Object path = m.invoke(instance, e, am);
  1755             m = path.getClass().getMethod("getLeaf");
  1756             Object leaf = m.invoke(path);
  1757             m = leaf.getClass().getMethod("getArguments");
  1758             return (List) m.invoke(leaf);
  1759         } catch (Exception ex) {
  1760             return Collections.emptyList();
  1761         }
  1762     }
  1763 
  1764     private static class Prprt {
  1765         private final Element e;
  1766         private final AnnotationMirror tm;
  1767         private final Property p;
  1768 
  1769         public Prprt(Element e, AnnotationMirror tm, Property p) {
  1770             this.e = e;
  1771             this.tm = tm;
  1772             this.p = p;
  1773         }
  1774         
  1775         String name() {
  1776             return p.name();
  1777         }
  1778         
  1779         boolean array() {
  1780             return p.array();
  1781         }
  1782 
  1783         String typeName(ProcessingEnvironment env) {
  1784             RuntimeException ex;
  1785             try {
  1786                 return p.type().getName();
  1787             } catch (IncompleteAnnotationException e) {
  1788                 ex = e;
  1789             } catch (AnnotationTypeMismatchException e) {
  1790                 ex = e;
  1791             }
  1792             for (Object v : getAnnoValues(env, e, tm)) {
  1793                 String s = v.toString().replace(" ", "");
  1794                 if (s.startsWith("type=") && s.endsWith(".class")) {
  1795                     return s.substring(5, s.length() - 6);
  1796                 }
  1797             }
  1798             throw ex;
  1799         }
  1800         
  1801         
  1802         static Prprt[] wrap(ProcessingEnvironment pe, Element e, Property[] arr) {
  1803             if (arr.length == 0) {
  1804                 return new Prprt[0];
  1805             }
  1806             
  1807             if (e.getKind() != ElementKind.CLASS) {
  1808                 throw new IllegalStateException("" + e.getKind());
  1809             }
  1810             TypeElement te = (TypeElement)e;
  1811             List<? extends AnnotationValue> val = null;
  1812             for (AnnotationMirror an : te.getAnnotationMirrors()) {
  1813                 for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : an.getElementValues().entrySet()) {
  1814                     if (entry.getKey().getSimpleName().contentEquals("properties")) {
  1815                         val = (List)entry.getValue().getValue();
  1816                         break;
  1817                     }
  1818                 }
  1819             }
  1820             if (val == null || val.size() != arr.length) {
  1821                 pe.getMessager().printMessage(Diagnostic.Kind.ERROR, "" + val, e);
  1822                 return new Prprt[0];
  1823             }
  1824             Prprt[] ret = new Prprt[arr.length];
  1825             BIG: for (int i = 0; i < ret.length; i++) {
  1826                 AnnotationMirror am = (AnnotationMirror)val.get(i).getValue();
  1827                 ret[i] = new Prprt(e, am, arr[i]);
  1828                 
  1829             }
  1830             return ret;
  1831         }
  1832     } // end of Prprt
  1833     
  1834     private static final class GetSet {
  1835         final String name;
  1836         final String getter;
  1837         final String setter;
  1838         final String type;
  1839         final boolean readOnly;
  1840         GetSet(String name, String getter, String setter, String type, boolean readOnly) {
  1841             this.name = name;
  1842             this.getter = getter;
  1843             this.setter = setter;
  1844             this.type = type;
  1845             this.readOnly = readOnly;
  1846         }
  1847     }
  1848 
  1849     @Override
  1850     public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
  1851         final Level l = Level.FINE;
  1852         LOG.log(l, " element: {0}", element);
  1853         LOG.log(l, " annotation: {0}", annotation);
  1854         LOG.log(l, " member: {0}", member);
  1855         LOG.log(l, " userText: {0}", userText);
  1856         LOG.log(l, "str: {0}", annotation.getAnnotationType().toString());
  1857         if (annotation.getAnnotationType().toString().equals(OnReceive.class.getName())) {
  1858             if (member.getSimpleName().contentEquals("method")) {
  1859                 return Arrays.asList(
  1860                     methodOf("GET"),
  1861                     methodOf("POST"),
  1862                     methodOf("PUT"),
  1863                     methodOf("DELETE"),
  1864                     methodOf("HEAD"),
  1865                     methodOf("WebSocket")
  1866                 );
  1867             }
  1868         }
  1869         
  1870         return super.getCompletions(element, annotation, member, userText);
  1871     }
  1872     
  1873     private static final Completion methodOf(String method) {
  1874         ResourceBundle rb = ResourceBundle.getBundle("org.netbeans.html.json.impl.Bundle");
  1875         return Completions.of('"' + method + '"', rb.getString("MSG_Completion_" + method));
  1876     }
  1877     
  1878     private boolean findOnError(ExecutableElement errElem, TypeElement te, String name, String className) {
  1879         String err = null;
  1880         METHODS:
  1881         for (Element e : te.getEnclosedElements()) {
  1882             if (e.getKind() != ElementKind.METHOD) {
  1883                 continue;
  1884             }
  1885             if (!e.getSimpleName().contentEquals(name)) {
  1886                 continue;
  1887             }
  1888             if (!e.getModifiers().contains(Modifier.STATIC)) {
  1889                 errElem = (ExecutableElement) e;
  1890                 err = "Would have to be static";
  1891                 continue;
  1892             }
  1893             ExecutableElement ee = (ExecutableElement) e;
  1894             TypeMirror excType = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
  1895             final List<? extends VariableElement> params = ee.getParameters();
  1896             boolean error = false;
  1897             if (params.size() != 2) {
  1898                 error = true;
  1899             } else {
  1900                 String firstType = params.get(0).asType().toString();
  1901                 int lastDot = firstType.lastIndexOf('.');
  1902                 if (lastDot != -1) {
  1903                     firstType = firstType.substring(lastDot + 1);
  1904                 }
  1905                 if (!firstType.equals(className)) {
  1906                     error = true;
  1907                 }
  1908                 if (!processingEnv.getTypeUtils().isAssignable(excType, params.get(1).asType())) {
  1909                     error = true;
  1910                 }
  1911             }
  1912             if (error) {
  1913                 errElem = (ExecutableElement) e;
  1914                 err = "Error method first argument needs to be " + className + " and second Exception";
  1915                 continue;
  1916             }
  1917             return true;
  1918         }
  1919         if (err == null) {
  1920             err = "Cannot find " + name + "(" + className + ", Exception) method in this class";
  1921         }
  1922         error(err, errElem);
  1923         return false;
  1924     }
  1925     
  1926 }