2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
9 * The contents of this file are subject to the terms of either the GNU
10 * General Public License Version 2 only ("GPL") or the Common
11 * Development and Distribution License("CDDL") (collectively, the
12 * "License"). You may not use this file except in compliance with the
13 * License. You can obtain a copy of the License at
14 * http://www.netbeans.org/cddl-gplv2.html
15 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16 * specific language governing permissions and limitations under the
17 * License. When distributing the software, include this License Header
18 * Notice in each file and include the License file at
19 * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
20 * particular file as subject to the "Classpath" exception as provided
21 * by Oracle in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the
23 * License Header, with the fields enclosed by brackets [] replaced by
24 * your own identifying information:
25 * "Portions Copyrighted [year] [name of copyright owner]"
29 * The Original Software is NetBeans. The Initial Developer of the Original
30 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
32 * If you wish your version of this file to be governed by only the CDDL
33 * or only the GPL Version 2, indicate your decision by adding
34 * "[Contributor] elects to include this software in this distribution
35 * under the [CDDL or GPL Version 2] license." If you do not indicate a
36 * single choice of license, a recipient has the option to distribute
37 * your version of this file under either the CDDL, the GPL Version 2 or
38 * to extend the choice of license to its licensees as provided above.
39 * However, if you add GPL Version 2 code and therefore, elected the GPL
40 * Version 2 license, then the option applies only if the new code is
41 * made subject to such option by the copyright holder.
43 package org.netbeans.html.json.impl;
45 import java.io.IOException;
46 import java.io.OutputStreamWriter;
47 import java.io.StringWriter;
48 import java.io.Writer;
49 import java.lang.annotation.AnnotationTypeMismatchException;
50 import java.lang.annotation.IncompleteAnnotationException;
51 import java.lang.reflect.Method;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Collection;
55 import java.util.Collections;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.LinkedHashSet;
59 import java.util.List;
61 import java.util.ResourceBundle;
63 import java.util.WeakHashMap;
64 import java.util.logging.Level;
65 import java.util.logging.Logger;
66 import javax.annotation.processing.AbstractProcessor;
67 import javax.annotation.processing.Completion;
68 import javax.annotation.processing.Completions;
69 import javax.annotation.processing.ProcessingEnvironment;
70 import javax.annotation.processing.Processor;
71 import javax.annotation.processing.RoundEnvironment;
72 import javax.annotation.processing.SupportedAnnotationTypes;
73 import javax.annotation.processing.SupportedSourceVersion;
74 import javax.lang.model.SourceVersion;
75 import javax.lang.model.element.AnnotationMirror;
76 import javax.lang.model.element.AnnotationValue;
77 import javax.lang.model.element.Element;
78 import javax.lang.model.element.ElementKind;
79 import javax.lang.model.element.ExecutableElement;
80 import javax.lang.model.element.Modifier;
81 import javax.lang.model.element.PackageElement;
82 import javax.lang.model.element.TypeElement;
83 import javax.lang.model.element.VariableElement;
84 import javax.lang.model.type.ArrayType;
85 import javax.lang.model.type.DeclaredType;
86 import javax.lang.model.type.MirroredTypeException;
87 import javax.lang.model.type.TypeKind;
88 import javax.lang.model.type.TypeMirror;
89 import javax.lang.model.util.Elements;
90 import javax.lang.model.util.Types;
91 import javax.tools.Diagnostic;
92 import javax.tools.FileObject;
93 import net.java.html.json.ComputedProperty;
94 import net.java.html.json.Model;
95 import net.java.html.json.Function;
96 import net.java.html.json.ModelOperation;
97 import net.java.html.json.OnPropertyChange;
98 import net.java.html.json.OnReceive;
99 import net.java.html.json.Property;
100 import org.openide.util.lookup.ServiceProvider;
102 /** Annotation processor to process {@link Model @Model} annotations and
103 * generate appropriate model classes.
105 * @author Jaroslav Tulach <jtulach@netbeans.org>
107 @ServiceProvider(service=Processor.class)
108 @SupportedSourceVersion(SourceVersion.RELEASE_6)
109 @SupportedAnnotationTypes({
110 "net.java.html.json.Model",
111 "net.java.html.json.ModelOperation",
112 "net.java.html.json.Function",
113 "net.java.html.json.OnReceive",
114 "net.java.html.json.OnPropertyChange",
115 "net.java.html.json.ComputedProperty",
116 "net.java.html.json.Property"
118 public final class ModelProcessor extends AbstractProcessor {
119 private static final Logger LOG = Logger.getLogger(ModelProcessor.class.getName());
120 private final Map<Element,String> models = new WeakHashMap<Element,String>();
121 private final Map<Element,Prprt[]> verify = new WeakHashMap<Element,Prprt[]>();
123 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
125 for (Element e : roundEnv.getElementsAnnotatedWith(Model.class)) {
126 if (!processModel(e)) {
130 if (roundEnv.processingOver()) {
132 for (Map.Entry<Element, Prprt[]> entry : verify.entrySet()) {
133 TypeElement te = (TypeElement)entry.getKey();
134 String fqn = processingEnv.getElementUtils().getBinaryName(te).toString();
135 Element finalElem = processingEnv.getElementUtils().getTypeElement(fqn);
136 if (finalElem == null) {
140 Model m = finalElem.getAnnotation(Model.class);
144 props = Prprt.wrap(processingEnv, finalElem, m.properties());
145 for (Prprt p : props) {
146 boolean[] isModel = { false };
147 boolean[] isEnum = { false };
148 boolean[] isPrimitive = { false };
149 String t = checkType(p, isModel, isEnum, isPrimitive);
153 if (isPrimitive[0]) {
159 if ("java.lang.String".equals(t)) {
162 error("The type " + t + " should be defined by @Model annotation", entry.getKey());
170 private void error(String msg, Element e) {
171 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
174 private boolean processModel(Element e) {
176 Model m = e.getAnnotation(Model.class);
180 String pkg = findPkgName(e);
182 String className = m.className();
183 models.put(e, className);
185 StringWriter body = new StringWriter();
186 StringBuilder onReceiveType = new StringBuilder();
187 List<String> propsGetSet = new ArrayList<String>();
188 List<String> functions = new ArrayList<String>();
189 Map<String, Collection<String>> propsDeps = new HashMap<String, Collection<String>>();
190 Map<String, Collection<String>> functionDeps = new HashMap<String, Collection<String>>();
191 Prprt[] props = createProps(e, m.properties());
193 if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
196 if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
199 if (!generateProperties(e, body, className, props, propsGetSet, propsDeps, functionDeps)) {
202 if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
205 if (!generateReceive(e, body, className, e.getEnclosedElements(), onReceiveType)) {
208 if (!generateOperation(e, body, className, e.getEnclosedElements())) {
211 FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
212 w = new OutputStreamWriter(java.openOutputStream());
214 w.append("package " + pkg + ";\n");
215 w.append("import net.java.html.json.*;\n");
216 w.append("public final class ").append(className).append(" implements Cloneable {\n");
217 w.append(" private static final Html4JavaType TYPE = new Html4JavaType();\n");
218 w.append(" private final org.apidesign.html.json.spi.Proto proto;\n");
219 w.append(body.toString());
220 w.append(" private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
221 w.append(" private ").append(className).append("(net.java.html.BrwsrCtx context) {\n");
222 w.append(" this.proto = TYPE.createProto(this, context);\n");
223 for (Prprt p : props) {
225 final String tn = typeName(e, p);
226 String[] gs = toGetSet(p.name(), tn, p.array());
227 w.write(" this.prop_" + p.name() + " = proto.createList(\""
229 if (functionDeps.containsKey(p.name())) {
230 int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name());
231 w.write(", " + index);
235 Collection<String> dependants = propsDeps.get(p.name());
236 if (dependants != null) {
237 for (String depProp : dependants) {
249 w.append(" public ").append(className).append("() {\n");
250 w.append(" this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
251 for (Prprt p : props) {
253 boolean[] isModel = {false};
254 boolean[] isEnum = {false};
255 boolean isPrimitive[] = {false};
256 String tn = checkType(p, isModel, isEnum, isPrimitive);
258 w.write(" prop_" + p.name() + " = new " + tn + "();\n");
263 if (props.length > 0) {
264 w.append(" public ").append(className).append("(");
265 Prprt firstArray = null;
267 for (Prprt p : props) {
269 if (firstArray == null) {
274 String tn = typeName(e, p);
277 w.write(" " + p.name());
280 if (firstArray != null) {
282 boolean[] isModel = {false};
283 boolean[] isEnum = {false};
284 boolean isPrimitive[] = {false};
285 tn = checkType(firstArray, isModel, isEnum, isPrimitive);
288 w.write("... " + firstArray.name());
291 w.append(" this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
292 for (Prprt p : props) {
296 w.write(" this.prop_" + p.name() + " = " + p.name() + ";\n");
298 if (firstArray != null) {
299 w.write(" proto.initTo(this.prop_" + firstArray.name() + ", " + firstArray.name() + ");\n");
303 w.append(" private static class Html4JavaType extends org.apidesign.html.json.spi.Proto.Type<").append(className).append("> {\n");
304 w.append(" private Html4JavaType() {\n super(").append(className).append(".class, ").
305 append(inPckName(e)).append(".class, " + (propsGetSet.size() / 5) + ", "
306 + (functions.size() / 2) + ");\n");
308 for (int i = 0; i < propsGetSet.size(); i += 5) {
309 w.append(" registerProperty(\"").append(propsGetSet.get(i)).append("\", ");
310 w.append((i / 5) + ", " + (propsGetSet.get(i + 2) == null) + ");\n");
314 for (int i = 0; i < functions.size(); i += 2) {
315 w.append(" registerFunction(\"").append(functions.get(i)).append("\", ");
316 w.append((i / 2) + ");\n");
320 w.append(" @Override public void setValue(" + className + " data, int type, Object value) {\n");
321 w.append(" switch (type) {\n");
322 for (int i = 0; i < propsGetSet.size(); i += 5) {
323 final String set = propsGetSet.get(i + 2);
324 String tn = propsGetSet.get(i + 4);
325 String btn = findBoxedType(tn);
330 w.append(" case " + (i / 5) + ": data." + strip(set) + "(TYPE.extractValue(" + tn + ".class, value)); return;\n");
335 w.append(" @Override public Object getValue(" + className + " data, int type) {\n");
336 w.append(" switch (type) {\n");
337 for (int i = 0; i < propsGetSet.size(); i += 5) {
338 final String get = propsGetSet.get(i + 1);
340 w.append(" case " + (i / 5) + ": return data." + strip(get) + "();\n");
344 w.append(" throw new UnsupportedOperationException();\n");
346 w.append(" @Override public void call(" + className + " model, int type, Object data, Object ev) {\n");
347 w.append(" switch (type) {\n");
348 for (int i = 0; i < functions.size(); i += 2) {
349 final String name = functions.get(i);
350 w.append(" case " + (i / 2) + ": model." + name + "(data, ev); return;\n");
353 w.append(" throw new UnsupportedOperationException();\n");
355 w.append(" @Override public org.apidesign.html.json.spi.Proto protoFor(Object obj) {\n");
356 w.append(" return ((" + className + ")obj).proto;");
358 w.append(" @Override public void onChange(" + className + " model, int type) {\n");
359 w.append(" switch (type) {\n");
361 String[] arr = functionDeps.keySet().toArray(new String[0]);
362 for (int i = 0; i < arr.length; i++) {
363 Collection<String> onChange = functionDeps.get(arr[i]);
364 if (onChange != null) {
365 w.append(" case " + i + ":\n");
366 for (String call : onChange) {
367 w.append(" ").append(call).append("\n");
369 w.write(" return;\n");
374 w.append(" throw new UnsupportedOperationException();\n");
376 w.append(onReceiveType);
377 w.append(" @Override public " + className + " read(net.java.html.BrwsrCtx c, Object json) { return new " + className + "(c, json); }\n");
378 w.append(" @Override public " + className + " cloneTo(" + className + " o, net.java.html.BrwsrCtx c) { return o.clone(c); }\n");
380 w.append(" private ").append(className).append("(net.java.html.BrwsrCtx c, Object json) {\n");
381 w.append(" this(c);\n");
383 for (int i = 0; i < propsGetSet.size(); i += 5) {
384 Prprt p = findPrprt(props, propsGetSet.get(i));
390 w.append(" Object[] ret = new Object[" + values + "];\n");
391 w.append(" proto.extract(json, new String[] {\n");
392 for (int i = 0; i < propsGetSet.size(); i += 5) {
393 Prprt p = findPrprt(props, propsGetSet.get(i));
397 w.append(" \"").append(propsGetSet.get(i)).append("\",\n");
399 w.append(" }, ret);\n");
400 for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i += 5) {
401 final String pn = propsGetSet.get(i);
402 Prprt p = findPrprt(props, pn);
406 boolean[] isModel = { false };
407 boolean[] isEnum = { false };
408 boolean isPrimitive[] = { false };
409 String type = checkType(props[prop++], isModel, isEnum, isPrimitive);
411 w.append(" if (ret[" + cnt + "] instanceof Object[]) {\n");
412 w.append(" for (Object e : ((Object[])ret[" + cnt + "])) {\n");
414 w.append(" this.prop_").append(pn).append(".add(proto.read");
415 w.append("(" + type + ".class, e));\n");
416 } else if (isEnum[0]) {
417 w.append(" this.prop_").append(pn);
418 w.append(".add(e == null ? null : ");
419 w.append(type).append(".valueOf(TYPE.stringValue(e)));\n");
421 if (isPrimitive(type)) {
422 w.append(" this.prop_").append(pn).append(".add(TYPE.numberValue(e).");
423 w.append(type).append("Value());\n");
425 w.append(" this.prop_").append(pn).append(".add((");
426 w.append(type).append(")e);\n");
433 w.append(" try {\n");
434 w.append(" this.prop_").append(pn);
435 w.append(" = ret[" + cnt + "] == null ? null : ");
436 w.append(type).append(".valueOf(TYPE.stringValue(ret[" + cnt + "]));\n");
437 w.append(" } catch (IllegalArgumentException ex) {\n");
438 w.append(" ex.printStackTrace();\n");
440 } else if (isPrimitive(type)) {
441 w.append(" this.prop_").append(pn);
442 w.append(" = ret[" + cnt + "] == null ? ");
443 if ("char".equals(type)) {
444 w.append("0 : (TYPE.charValue(");
445 } else if ("boolean".equals(type)) {
446 w.append("false : (TYPE.boolValue(");
448 w.append("0 : (TYPE.numberValue(");
450 w.append("ret[" + cnt + "])).");
451 w.append(type).append("Value();\n");
452 } else if (isModel[0]) {
453 w.append(" this.prop_").append(pn).append(" = proto.read");
454 w.append("(" + type + ".class, ");
455 w.append("ret[" + cnt + "]);\n");
457 w.append(" this.prop_").append(pn);
458 w.append(" = (").append(type).append(')');
459 w.append("ret[" + cnt + "];\n");
465 writeToString(props, w);
466 writeClone(className, props, w);
467 w.write(" /** Activates this model instance in the current {@link \n"
468 + "net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}. \n"
469 + "In case of using Knockout technology, this means to \n"
470 + "bind JSON like data in this model instance with Knockout tags in \n"
471 + "the surrounding HTML page.\n"
474 w.write(" public " + className + " applyBindings() {\n");
475 w.write(" proto.applyBindings();\n");
476 w.write(" return this;\n");
478 w.write(" public boolean equals(Object o) {\n");
479 w.write(" if (o == this) return true;\n");
480 w.write(" if (!(o instanceof " + className + ")) return false;\n");
481 w.write(" " + className + " p = (" + className + ")o;\n");
482 for (Prprt p : props) {
483 w.write(" if (!TYPE.isSame(prop_" + p.name() + ", p.prop_" + p.name() + ")) return false;\n");
485 w.write(" return true;\n");
487 w.write(" public int hashCode() {\n");
488 w.write(" int h = " + className + ".class.getName().hashCode();\n");
489 for (Prprt p : props) {
490 w.write(" h = TYPE.hashPlus(prop_" + p.name() + ", h);\n");
492 w.write(" return h;\n");
498 } catch (IOException ex) {
499 error("Can't create " + className + ".java", e);
505 private boolean generateProperties(
507 Writer w, String className, Prprt[] properties,
508 Collection<String> props,
509 Map<String,Collection<String>> deps,
510 Map<String,Collection<String>> functionDeps
511 ) throws IOException {
513 for (Prprt p : properties) {
515 tn = typeName(where, p);
516 String[] gs = toGetSet(p.name(), tn, p.array());
520 w.write(" private final java.util.List<" + tn + "> prop_" + p.name() + ";\n");
522 castTo = "java.util.List";
523 w.write(" public java.util.List<" + tn + "> " + gs[0] + "() {\n");
524 w.write(" proto.verifyUnlocked();\n");
525 w.write(" return prop_" + p.name() + ";\n");
529 w.write(" private " + tn + " prop_" + p.name() + ";\n");
530 w.write(" public " + tn + " " + gs[0] + "() {\n");
531 w.write(" proto.verifyUnlocked();\n");
532 w.write(" return prop_" + p.name() + ";\n");
534 w.write(" public void " + gs[1] + "(" + tn + " v) {\n");
535 w.write(" proto.verifyUnlocked();\n");
536 w.write(" if (TYPE.isSame(prop_" + p.name() + ", v)) return;\n");
537 w.write(" prop_" + p.name() + " = v;\n");
538 w.write(" proto.valueHasMutated(\"" + p.name() + "\");\n");
539 Collection<String> dependants = deps.get(p.name());
540 if (dependants != null) {
541 for (String depProp : dependants) {
542 w.write(" proto.valueHasMutated(\"" + depProp + "\");\n");
545 dependants = functionDeps.get(p.name());
546 if (dependants != null) {
547 w.append(className).append(" model = ").append(className).append(".this;\n");
548 for (String call : dependants) {
549 w.append(" ").append(call);
564 private boolean generateComputedProperties(
565 Writer w, Prprt[] fixedProps,
566 Collection<? extends Element> arr, Collection<String> props,
567 Map<String,Collection<String>> deps
568 ) throws IOException {
570 for (Element e : arr) {
571 if (e.getKind() != ElementKind.METHOD) {
574 if (e.getAnnotation(ComputedProperty.class) == null) {
577 if (!e.getModifiers().contains(Modifier.STATIC)) {
578 error("Method " + e.getSimpleName() + " has to be static when annotated by @ComputedProperty", e);
582 ExecutableElement ee = (ExecutableElement)e;
583 final TypeMirror rt = ee.getReturnType();
584 final Types tu = processingEnv.getTypeUtils();
585 TypeMirror ert = tu.erasure(rt);
586 String tn = fqn(ert, ee);
587 boolean array = false;
588 final TypeMirror toCheck;
589 if (tn.equals("java.util.List")) {
591 toCheck = ((DeclaredType)rt).getTypeArguments().get(0);
596 final String sn = ee.getSimpleName().toString();
598 if (toCheck.getKind().isPrimitive()) {
601 TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
602 TypeMirror enumType = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
604 if (tu.isSubtype(toCheck, stringType)) {
606 } else if (tu.isSubtype(tu.erasure(toCheck), tu.erasure(enumType))) {
608 } else if (isModel(toCheck)) {
612 error(sn + " cannot return " + toCheck, e);
616 String[] gs = toGetSet(sn, tn, array);
618 w.write(" public " + tn + " " + gs[0] + "() {\n");
620 for (VariableElement pe : ee.getParameters()) {
621 final String dn = pe.getSimpleName().toString();
623 if (!verifyPropName(pe, dn, fixedProps)) {
627 final String dt = fqn(pe.asType(), ee);
628 String[] call = toGetSet(dn, dt, false);
629 w.write(" " + dt + " arg" + (++arg) + " = ");
630 w.write(call[0] + "();\n");
632 Collection<String> depends = deps.get(dn);
633 if (depends == null) {
634 depends = new LinkedHashSet<String>();
635 deps.put(dn, depends);
640 w.write(" proto.acquireLock();\n");
641 w.write(" return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
643 for (int i = 1; i <= arg; i++) {
649 w.write(" } finally {\n");
650 w.write(" proto.releaseLock();\n");
654 props.add(e.getSimpleName().toString());
664 private static String[] toGetSet(String name, String type, boolean array) {
665 String n = Character.toUpperCase(name.charAt(0)) + name.substring(1);
666 String bck2brwsrType = "L" + type.replace('.', '_') + "_2";
667 if ("int".equals(type)) {
670 if ("double".equals(type)) {
674 if ("boolean".equals(type)) {
678 final String nu = n.replace('.', '_');
680 return new String[] {
683 "get" + nu + "__Ljava_util_List_2",
690 pref + nu + "__" + bck2brwsrType,
691 "set" + nu + "__V" + bck2brwsrType
695 private String typeName(Element where, Prprt p) {
697 boolean[] isModel = { false };
698 boolean[] isEnum = { false };
699 boolean isPrimitive[] = { false };
700 ret = checkType(p, isModel, isEnum, isPrimitive);
702 String bt = findBoxedType(ret);
710 private static String findBoxedType(String ret) {
711 if (ret.equals("boolean")) {
712 return Boolean.class.getName();
714 if (ret.equals("byte")) {
715 return Byte.class.getName();
717 if (ret.equals("short")) {
718 return Short.class.getName();
720 if (ret.equals("char")) {
721 return Character.class.getName();
723 if (ret.equals("int")) {
724 return Integer.class.getName();
726 if (ret.equals("long")) {
727 return Long.class.getName();
729 if (ret.equals("float")) {
730 return Float.class.getName();
732 if (ret.equals("double")) {
733 return Double.class.getName();
738 private boolean verifyPropName(Element e, String propName, Prprt[] existingProps) {
739 StringBuilder sb = new StringBuilder();
741 for (Prprt Prprt : existingProps) {
742 if (Prprt.name().equals(propName)) {
747 sb.append(Prprt.name());
752 propName + " is not one of known properties: " + sb
758 private static String findPkgName(Element e) {
760 if (e.getKind() == ElementKind.PACKAGE) {
761 return ((PackageElement)e).getQualifiedName().toString();
763 e = e.getEnclosingElement();
767 private boolean generateFunctions(
768 Element clazz, StringWriter body, String className,
769 List<? extends Element> enclosedElements, List<String> functions
771 for (Element m : enclosedElements) {
772 if (m.getKind() != ElementKind.METHOD) {
775 ExecutableElement e = (ExecutableElement)m;
776 Function onF = e.getAnnotation(Function.class);
780 if (!e.getModifiers().contains(Modifier.STATIC)) {
781 error("@OnFunction method needs to be static", e);
784 if (e.getModifiers().contains(Modifier.PRIVATE)) {
785 error("@OnFunction method cannot be private", e);
788 if (e.getReturnType().getKind() != TypeKind.VOID) {
789 error("@OnFunction method should return void", e);
792 String n = e.getSimpleName().toString();
793 body.append(" private void ").append(n).append("(Object data, Object ev) {\n");
794 body.append(" ").append(((TypeElement)clazz).getQualifiedName()).append(".").append(n).append("(");
795 body.append(wrapParams(e, null, className, "ev", "data"));
800 functions.add(n + "__VLjava_lang_Object_2Ljava_lang_Object_2");
805 private boolean generateOnChange(Element clazz, Map<String,Collection<String>> propDeps,
806 Prprt[] properties, String className,
807 Map<String, Collection<String>> functionDeps
809 for (Element m : clazz.getEnclosedElements()) {
810 if (m.getKind() != ElementKind.METHOD) {
813 ExecutableElement e = (ExecutableElement) m;
814 OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class);
818 for (String pn : onPC.value()) {
819 if (findPrprt(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
820 error("No Prprt named '" + pn + "' in the model", clazz);
824 if (!e.getModifiers().contains(Modifier.STATIC)) {
825 error("@OnPrprtChange method needs to be static", e);
828 if (e.getModifiers().contains(Modifier.PRIVATE)) {
829 error("@OnPrprtChange method cannot be private", e);
832 if (e.getReturnType().getKind() != TypeKind.VOID) {
833 error("@OnPrprtChange method should return void", e);
836 String n = e.getSimpleName().toString();
839 for (String pn : onPC.value()) {
840 StringBuilder call = new StringBuilder();
841 call.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
842 call.append(wrapPropName(e, className, "name", pn));
845 Collection<String> change = functionDeps.get(pn);
846 if (change == null) {
847 change = new ArrayList<String>();
848 functionDeps.put(pn, change);
850 change.add(call.toString());
851 for (String dpn : findDerivedFrom(propDeps, pn)) {
852 change = functionDeps.get(dpn);
853 if (change == null) {
854 change = new ArrayList<String>();
855 functionDeps.put(dpn, change);
857 change.add(call.toString());
864 private boolean generateOperation(Element clazz,
865 StringWriter body, String className,
866 List<? extends Element> enclosedElements
868 for (Element m : enclosedElements) {
869 if (m.getKind() != ElementKind.METHOD) {
872 ExecutableElement e = (ExecutableElement)m;
873 ModelOperation mO = e.getAnnotation(ModelOperation.class);
877 if (!e.getModifiers().contains(Modifier.STATIC)) {
878 error("@ModelOperation method needs to be static", e);
881 if (e.getModifiers().contains(Modifier.PRIVATE)) {
882 error("@ModelOperation method cannot be private", e);
885 if (e.getReturnType().getKind() != TypeKind.VOID) {
886 error("@ModelOperation method should return void", e);
889 List<String> args = new ArrayList<String>();
891 body.append(" public void ").append(m.getSimpleName()).append("(");
893 boolean checkFirst = true;
894 for (VariableElement ve : e.getParameters()) {
895 final TypeMirror type = ve.asType();
896 CharSequence simpleName;
897 if (type.getKind() == TypeKind.DECLARED) {
898 simpleName = ((DeclaredType)type).asElement().getSimpleName();
900 simpleName = type.toString();
902 if (simpleName.toString().equals(className)) {
906 error("First parameter of @ModelOperation method must be " + className, m);
909 args.add(ve.getSimpleName().toString());
910 body.append(sep).append("final ");
911 body.append(ve.asType().toString()).append(" ");
912 body.append(ve.toString());
916 body.append(") {\n");
917 body.append(" proto.runInBrowser(new Runnable() { public void run() {\n");
918 body.append(" ").append(clazz.getSimpleName()).append(".").append(m.getSimpleName()).append("(");
919 body.append(className).append(".this");
920 for (String s : args) {
921 body.append(", ").append(s);
924 body.append(" }});\n");
933 private boolean generateReceive(
934 Element clazz, StringWriter body, String className,
935 List<? extends Element> enclosedElements, StringBuilder inType
937 inType.append(" @Override public void onMessage(").append(className).append(" model, int index, int type, Object data) {\n");
938 inType.append(" switch (index) {\n");
940 for (Element m : enclosedElements) {
941 if (m.getKind() != ElementKind.METHOD) {
944 ExecutableElement e = (ExecutableElement)m;
945 OnReceive onR = e.getAnnotation(OnReceive.class);
949 if (!e.getModifiers().contains(Modifier.STATIC)) {
950 error("@OnReceive method needs to be static", e);
953 if (e.getModifiers().contains(Modifier.PRIVATE)) {
954 error("@OnReceive method cannot be private", e);
957 if (e.getReturnType().getKind() != TypeKind.VOID) {
958 error("@OnReceive method should return void", e);
961 if (!onR.jsonp().isEmpty() && !"GET".equals(onR.method())) {
962 error("JSONP works only with GET transport method", e);
964 String dataMirror = findDataSpecified(e, onR);
965 if ("PUT".equals(onR.method()) && dataMirror == null) {
966 error("PUT method needs to specify a data() class", e);
969 if ("POST".equals(onR.method()) && dataMirror == null) {
970 error("POST method needs to specify a data() class", e);
973 String modelClass = null;
974 boolean expectsList = false;
975 List<String> args = new ArrayList<String>();
977 for (VariableElement ve : e.getParameters()) {
978 TypeMirror modelType = null;
979 final TypeMirror type = ve.asType();
980 CharSequence simpleName;
981 if (type.getKind() == TypeKind.DECLARED) {
982 simpleName = ((DeclaredType)type).asElement().getSimpleName();
984 simpleName = type.toString();
986 if (simpleName.toString().equals(className)) {
988 } else if (isModel(ve.asType())) {
989 modelType = ve.asType();
990 } else if (ve.asType().getKind() == TypeKind.ARRAY) {
991 modelType = ((ArrayType)ve.asType()).getComponentType();
993 } else if (ve.asType().toString().equals("java.lang.String")) {
994 modelType = ve.asType();
996 if (modelType != null) {
997 if (modelClass != null) {
998 error("There can be only one model class among arguments", e);
1000 modelClass = modelType.toString();
1010 if (modelClass == null) {
1011 error("The method needs to have one @Model class as parameter", e);
1013 String n = e.getSimpleName().toString();
1014 if ("WebSocket".equals(onR.method())) {
1015 body.append(" /** Performs WebSocket communication. Call with <code>null</code> data parameter\n");
1016 body.append(" * to open the connection (even if not required). Call with non-null data to\n");
1017 body.append(" * send messages to server. Call again with <code>null</code> data to close the socket.\n");
1018 body.append(" */\n");
1020 body.append(" public void ").append(n).append("(");
1021 StringBuilder urlBefore = new StringBuilder();
1022 StringBuilder urlAfter = new StringBuilder();
1023 String jsonpVarName = null;
1026 boolean skipJSONP = onR.jsonp().isEmpty();
1027 for (String p : findParamNames(e, onR.url(), onR.jsonp(), urlBefore, urlAfter)) {
1028 if (!skipJSONP && p.equals(onR.jsonp())) {
1034 body.append("String ").append(p);
1039 "Name of jsonp attribute ('" + onR.jsonp() +
1040 "') is not used in url attribute '" + onR.url() + "'", e
1043 if (dataMirror != null) {
1044 body.append(sep).append(dataMirror.toString()).append(" data");
1047 body.append(") {\n");
1048 boolean webSocket = onR.method().equals("WebSocket");
1050 if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList, modelClass, n, args, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
1053 body.append(" }\n");
1054 body.append(" private Object ws_" + e.getSimpleName() + ";\n");
1056 if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList, modelClass, n, args, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
1059 body.append(" }\n");
1062 inType.append(" }\n");
1063 inType.append(" throw new UnsupportedOperationException(\"index: \" + index + \" type: \" + type);\n");
1064 inType.append(" }\n");
1068 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, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
1070 " case " + index + ": {\n" +
1071 " if (type == 2) { /* on error */\n" +
1072 " Exception ex = (Exception)data;\n"
1074 if (onR.onError().isEmpty()) {
1076 " ex.printStackTrace();\n"
1079 if (!findOnError(e, ((TypeElement)clazz), onR.onError(), className)) {
1082 body.append(" ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
1083 body.append("model, ex);\n");
1087 " } else if (type == 1) {\n" +
1088 " Object[] ev = (Object[])data;\n"
1092 " " + modelClass + "[] arr = new " + modelClass + "[ev.length];\n"
1096 " " + modelClass + "[] arr = { null };\n"
1100 " TYPE.copyJSON(model.proto.getContext(), ev, " + modelClass + ".class, arr);\n"
1103 body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
1105 for (String arg : args) {
1110 body.append(");\n");
1117 method.append(" proto.loadJSON(" + index + ",\n ");
1118 method.append(urlBefore).append(", ");
1119 if (jsonpVarName != null) {
1120 method.append(urlAfter);
1122 method.append("null");
1124 if (!"GET".equals(onR.method()) || dataMirror != null) {
1125 method.append(", \"").append(onR.method()).append('"');
1126 if (dataMirror != null) {
1127 method.append(", data");
1129 method.append(", null");
1132 method.append(", null, null");
1134 method.append(");\n");
1138 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, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
1140 " case " + index + ": {\n" +
1141 " if (type == 0) { /* on open */\n" +
1142 " ").append(clazz.getSimpleName()).append(".").append(n).append("(");
1145 for (String arg : args) {
1147 if (arg.startsWith("arr")) {
1148 body.append("null");
1155 body.append(");\n");
1158 " } else if (type == 2) { /* on error */\n" +
1159 " Exception value = (Exception)data;\n"
1161 if (onR.onError().isEmpty()) {
1163 " value.printStackTrace();\n"
1166 if (!findOnError(e, ((TypeElement)clazz), onR.onError(), className)) {
1169 body.append(" ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
1170 body.append("model, value);\n");
1174 " } else if (type == 1) {\n" +
1175 " Object[] ev = (Object[])data;\n"
1179 " " + modelClass + "[] arr = new " + modelClass + "[ev.length];\n"
1183 " " + modelClass + "[] arr = { null };\n"
1187 " TYPE.copyJSON(model.proto.getContext(), ev, " + modelClass + ".class, arr);\n"
1190 body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
1192 for (String arg : args) {
1197 body.append(");\n");
1203 if (!onR.onError().isEmpty()) {
1204 body.append(" else if (type == 3) { /* on close */\n");
1205 body.append(" ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
1206 body.append("model, null);\n");
1213 body.append(" }\n");
1214 method.append(" if (this.ws_").append(e.getSimpleName()).append(" == null) {\n");
1215 method.append(" this.ws_").append(e.getSimpleName());
1216 method.append("= proto.wsOpen(" + index + ", ");
1217 method.append(urlBefore).append(", data);\n");
1218 method.append(" } else {\n");
1219 method.append(" proto.wsSend(this.ws_").append(e.getSimpleName()).append(", ").append(urlBefore).append(", data);\n");
1220 method.append(" }\n");
1224 private CharSequence wrapParams(
1225 ExecutableElement ee, String id, String className, String evName, String dataName
1227 TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
1228 StringBuilder params = new StringBuilder();
1229 boolean first = true;
1230 for (VariableElement ve : ee.getParameters()) {
1232 params.append(", ");
1235 String toCall = null;
1236 String toFinish = null;
1237 boolean addNull = true;
1238 if (ve.asType() == stringType) {
1239 if (ve.getSimpleName().contentEquals("id")) {
1240 params.append('"').append(id).append('"');
1243 toCall = "proto.toString(";
1245 if (ve.asType().getKind() == TypeKind.DOUBLE) {
1246 toCall = "proto.toNumber(";
1247 toFinish = ".doubleValue()";
1249 if (ve.asType().getKind() == TypeKind.INT) {
1250 toCall = "proto.toNumber(";
1251 toFinish = ".intValue()";
1253 if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
1254 toCall = "proto.toModel(" + ve.asType() + ".class, ";
1258 if (toCall != null) {
1259 params.append(toCall);
1260 if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
1261 params.append(dataName);
1263 params.append(", null");
1266 if (evName == null) {
1267 final StringBuilder sb = new StringBuilder();
1268 sb.append("Unexpected string parameter name.");
1269 if (dataName != null) {
1270 sb.append(" Try \"").append(dataName).append("\"");
1272 error(sb.toString(), ee);
1274 params.append(evName);
1275 params.append(", \"");
1276 params.append(ve.getSimpleName().toString());
1277 params.append("\"");
1280 if (toFinish != null) {
1281 params.append(toFinish);
1285 String rn = fqn(ve.asType(), ee);
1286 int last = rn.lastIndexOf('.');
1288 rn = rn.substring(last + 1);
1290 if (rn.equals(className)) {
1291 params.append(className).append(".this");
1295 "The annotated method can only accept " + className + " argument or argument named 'data'",
1303 private CharSequence wrapPropName(
1304 ExecutableElement ee, String className, String propName, String propValue
1306 TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
1307 StringBuilder params = new StringBuilder();
1308 boolean first = true;
1309 for (VariableElement ve : ee.getParameters()) {
1311 params.append(", ");
1314 if (ve.asType() == stringType) {
1315 if (propName != null && ve.getSimpleName().contentEquals(propName)) {
1316 params.append('"').append(propValue).append('"');
1318 error("Unexpected string parameter name. Try \"" + propName + "\".", ee);
1322 String rn = fqn(ve.asType(), ee);
1323 int last = rn.lastIndexOf('.');
1325 rn = rn.substring(last + 1);
1327 if (rn.equals(className)) {
1328 params.append("model");
1332 "@OnPrprtChange method can only accept String or " + className + " arguments",
1338 private boolean isModel(TypeMirror tm) {
1339 if (tm.getKind() == TypeKind.ERROR) {
1342 final Element e = processingEnv.getTypeUtils().asElement(tm);
1346 for (Element ch : e.getEnclosedElements()) {
1347 if (ch.getKind() == ElementKind.METHOD) {
1348 ExecutableElement ee = (ExecutableElement)ch;
1349 if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) {
1354 return models.values().contains(e.getSimpleName().toString());
1357 private void writeToString(Prprt[] props, Writer w) throws IOException {
1358 w.write(" public String toString() {\n");
1359 w.write(" StringBuilder sb = new StringBuilder();\n");
1360 w.write(" sb.append('{');\n");
1362 for (Prprt p : props) {
1364 w.append(" sb.append('\"').append(\"" + p.name() + "\")");
1365 w.append(".append('\"').append(\":\");\n");
1366 w.append(" sb.append(TYPE.toJSON(prop_");
1367 w.append(p.name()).append("));\n");
1368 sep = " sb.append(',');\n";
1370 w.write(" sb.append('}');\n");
1371 w.write(" return sb.toString();\n");
1374 private void writeClone(String className, Prprt[] props, Writer w) throws IOException {
1375 w.write(" public " + className + " clone() {\n");
1376 w.write(" return clone(proto.getContext());\n");
1378 w.write(" private " + className + " clone(net.java.html.BrwsrCtx ctx) {\n");
1379 w.write(" " + className + " ret = new " + className + "(ctx);\n");
1380 for (Prprt p : props) {
1382 boolean isModel[] = { false };
1383 boolean isEnum[] = { false };
1384 boolean isPrimitive[] = { false };
1385 checkType(p, isModel, isEnum, isPrimitive);
1387 w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ";\n");
1390 w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + " == null ? null : prop_" + p.name() + ".clone();\n");
1392 w.write(" proto.cloneList(ret.prop_" + p.name() + ", ctx, prop_" + p.name() + ");\n");
1396 w.write(" return ret;\n");
1400 private String inPckName(Element e) {
1401 StringBuilder sb = new StringBuilder();
1402 while (e.getKind() != ElementKind.PACKAGE) {
1403 if (sb.length() == 0) {
1404 sb.append(e.getSimpleName());
1407 sb.insert(0, e.getSimpleName());
1409 e = e.getEnclosingElement();
1411 return sb.toString();
1414 private String fqn(TypeMirror pt, Element relative) {
1415 if (pt.getKind() == TypeKind.ERROR) {
1416 final Elements eu = processingEnv.getElementUtils();
1417 PackageElement pckg = eu.getPackageOf(relative);
1418 return pckg.getQualifiedName() + "." + pt.toString();
1420 return pt.toString();
1423 private String checkType(Prprt p, boolean[] isModel, boolean[] isEnum, boolean[] isPrimitive) {
1426 String ret = p.typeName(processingEnv);
1427 TypeElement e = processingEnv.getElementUtils().getTypeElement(ret);
1431 isPrimitive[0] = false;
1435 } catch (MirroredTypeException ex) {
1436 tm = ex.getTypeMirror();
1438 tm = processingEnv.getTypeUtils().erasure(tm);
1439 if (isPrimitive[0] = tm.getKind().isPrimitive()) {
1442 return tm.toString();
1444 final Element e = processingEnv.getTypeUtils().asElement(tm);
1445 if (e.getKind() == ElementKind.CLASS && tm.getKind() == TypeKind.ERROR) {
1448 return e.getSimpleName().toString();
1451 final Model m = e == null ? null : e.getAnnotation(Model.class);
1454 ret = findPkgName(e) + '.' + m.className();
1456 models.put(e, m.className());
1457 } else if (findModelForMthd(e)) {
1458 ret = ((TypeElement)e).getQualifiedName().toString();
1461 ret = tm.toString();
1463 TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
1464 enm = processingEnv.getTypeUtils().erasure(enm);
1465 isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
1469 private static boolean findModelForMthd(Element clazz) {
1470 if (clazz == null) {
1473 for (Element e : clazz.getEnclosedElements()) {
1474 if (e.getKind() == ElementKind.METHOD) {
1475 ExecutableElement ee = (ExecutableElement)e;
1477 ee.getSimpleName().contentEquals("modelFor") &&
1478 ee.getParameters().isEmpty()
1487 private Iterable<String> findParamNames(
1488 Element e, String url, String jsonParam, StringBuilder... both
1490 List<String> params = new ArrayList<String>();
1493 for (int pos = 0; ;) {
1494 int next = url.indexOf('{', pos);
1496 both[wasJSON].append('"')
1497 .append(url.substring(pos))
1501 int close = url.indexOf('}', next);
1503 error("Unbalanced '{' and '}' in " + url, e);
1506 final String paramName = url.substring(next + 1, close);
1507 params.add(paramName);
1508 if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
1509 both[wasJSON].append('"')
1510 .append(url.substring(pos, next))
1514 both[wasJSON].append('"')
1515 .append(url.substring(pos, next))
1516 .append("\" + ").append(paramName).append(" + ");
1522 private static Prprt findPrprt(Prprt[] properties, String propName) {
1523 for (Prprt p : properties) {
1524 if (propName.equals(p.name())) {
1531 private boolean isPrimitive(String type) {
1533 "int".equals(type) ||
1534 "double".equals(type) ||
1535 "long".equals(type) ||
1536 "short".equals(type) ||
1537 "byte".equals(type) ||
1538 "char".equals(type) ||
1539 "boolean".equals(type) ||
1540 "float".equals(type);
1543 private static Collection<String> findDerivedFrom(Map<String, Collection<String>> propsDeps, String derivedProp) {
1544 Set<String> names = new HashSet<String>();
1545 for (Map.Entry<String, Collection<String>> e : propsDeps.entrySet()) {
1546 if (e.getValue().contains(derivedProp)) {
1547 names.add(e.getKey());
1553 private Prprt[] createProps(Element e, Property[] arr) {
1554 Prprt[] ret = Prprt.wrap(processingEnv, e, arr);
1555 Prprt[] prev = verify.put(e, ret);
1557 error("Two sets of properties for ", e);
1562 private static String strip(String s) {
1563 int indx = s.indexOf("__");
1565 return s.substring(0, indx);
1571 private String findDataSpecified(ExecutableElement e, OnReceive onR) {
1573 return onR.data().getName();
1574 } catch (MirroredTypeException ex) {
1575 final TypeMirror tm = ex.getTypeMirror();
1577 final Element te = processingEnv.getTypeUtils().asElement(tm);
1578 if (te.getKind() == ElementKind.CLASS && tm.getKind() == TypeKind.ERROR) {
1579 name = te.getSimpleName().toString();
1581 name = tm.toString();
1583 return "java.lang.Object".equals(name) ? null : name;
1584 } catch (Exception ex) {
1588 AnnotationMirror found = null;
1589 for (AnnotationMirror am : e.getAnnotationMirrors()) {
1590 if (am.getAnnotationType().toString().equals(OnReceive.class.getName())) {
1594 if (found == null) {
1598 for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : found.getElementValues().entrySet()) {
1599 ExecutableElement ee = entry.getKey();
1600 AnnotationValue av = entry.getValue();
1601 if (ee.getSimpleName().contentEquals("data")) {
1602 List<? extends Object> values = getAnnoValues(processingEnv, e, found);
1603 for (Object v : values) {
1604 String sv = v.toString();
1605 if (sv.startsWith("data = ") && sv.endsWith(".class")) {
1606 return sv.substring(7, sv.length() - 6);
1615 static List<? extends Object> getAnnoValues(ProcessingEnvironment pe, Element e, AnnotationMirror am) {
1617 Class<?> trees = Class.forName("com.sun.tools.javac.api.JavacTrees");
1618 Method m = trees.getMethod("instance", ProcessingEnvironment.class);
1619 Object instance = m.invoke(null, pe);
1620 m = instance.getClass().getMethod("getPath", Element.class, AnnotationMirror.class);
1621 Object path = m.invoke(instance, e, am);
1622 m = path.getClass().getMethod("getLeaf");
1623 Object leaf = m.invoke(path);
1624 m = leaf.getClass().getMethod("getArguments");
1625 return (List) m.invoke(leaf);
1626 } catch (Exception ex) {
1627 return Collections.emptyList();
1631 private static class Prprt {
1632 private final Element e;
1633 private final AnnotationMirror tm;
1634 private final Property p;
1636 public Prprt(Element e, AnnotationMirror tm, Property p) {
1650 String typeName(ProcessingEnvironment env) {
1651 RuntimeException ex;
1653 return p.type().getName();
1654 } catch (IncompleteAnnotationException e) {
1656 } catch (AnnotationTypeMismatchException e) {
1659 for (Object v : getAnnoValues(env, e, tm)) {
1660 String s = v.toString().replace(" ", "");
1661 if (s.startsWith("type=") && s.endsWith(".class")) {
1662 return s.substring(5, s.length() - 6);
1669 static Prprt[] wrap(ProcessingEnvironment pe, Element e, Property[] arr) {
1670 if (arr.length == 0) {
1671 return new Prprt[0];
1674 if (e.getKind() != ElementKind.CLASS) {
1675 throw new IllegalStateException("" + e.getKind());
1677 TypeElement te = (TypeElement)e;
1678 List<? extends AnnotationValue> val = null;
1679 for (AnnotationMirror an : te.getAnnotationMirrors()) {
1680 for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : an.getElementValues().entrySet()) {
1681 if (entry.getKey().getSimpleName().contentEquals("properties")) {
1682 val = (List)entry.getValue().getValue();
1687 if (val == null || val.size() != arr.length) {
1688 pe.getMessager().printMessage(Diagnostic.Kind.ERROR, "" + val, e);
1689 return new Prprt[0];
1691 Prprt[] ret = new Prprt[arr.length];
1692 BIG: for (int i = 0; i < ret.length; i++) {
1693 AnnotationMirror am = (AnnotationMirror)val.get(i).getValue();
1694 ret[i] = new Prprt(e, am, arr[i]);
1702 public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
1703 final Level l = Level.FINE;
1704 LOG.log(l, " element: {0}", element);
1705 LOG.log(l, " annotation: {0}", annotation);
1706 LOG.log(l, " member: {0}", member);
1707 LOG.log(l, " userText: {0}", userText);
1708 LOG.log(l, "str: {0}", annotation.getAnnotationType().toString());
1709 if (annotation.getAnnotationType().toString().equals(OnReceive.class.getName())) {
1710 if (member.getSimpleName().contentEquals("method")) {
1711 return Arrays.asList(
1717 methodOf("WebSocket")
1722 return super.getCompletions(element, annotation, member, userText);
1725 private static final Completion methodOf(String method) {
1726 ResourceBundle rb = ResourceBundle.getBundle("org.netbeans.html.json.impl.Bundle");
1727 return Completions.of('"' + method + '"', rb.getString("MSG_Completion_" + method));
1730 private boolean findOnError(ExecutableElement errElem, TypeElement te, String name, String className) {
1733 for (Element e : te.getEnclosedElements()) {
1734 if (e.getKind() != ElementKind.METHOD) {
1737 if (!e.getSimpleName().contentEquals(name)) {
1740 if (!e.getModifiers().contains(Modifier.STATIC)) {
1741 errElem = (ExecutableElement) e;
1742 err = "Would have to be static";
1745 ExecutableElement ee = (ExecutableElement) e;
1746 TypeMirror excType = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
1747 final List<? extends VariableElement> params = ee.getParameters();
1748 boolean error = false;
1749 if (params.size() != 2) {
1752 String firstType = params.get(0).asType().toString();
1753 int lastDot = firstType.lastIndexOf('.');
1754 if (lastDot != -1) {
1755 firstType = firstType.substring(lastDot + 1);
1757 if (!firstType.equals(className)) {
1760 if (!processingEnv.getTypeUtils().isAssignable(excType, params.get(1).asType())) {
1765 errElem = (ExecutableElement) e;
1766 err = "Error method first argument needs to be " + className + " and second Exception";
1772 err = "Cannot find " + name + "(" + className + ", Exception) method in this class";
1774 error(err, errElem);