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