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.
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
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<GetSet> propsGetSet = new ArrayList<GetSet>();
188 List<Object> functions = new ArrayList<Object>();
189 Map<String, Collection<String[]>> propsDeps = new HashMap<String, Collection<String[]>>();
190 Map<String, Collection<String>> functionDeps = new HashMap<String, Collection<String>>();
191 Prprt[] props = createProps(e, m.properties());
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 int functionsCount = functions.size() / 2;
206 for (int i = 0; i < functions.size(); i += 2) {
207 for (Prprt p : props) {
208 if (p.name().equals(functions.get(i))) {
209 error("Function cannot have the name of an existing property", e);
214 if (!generateReceive(e, body, className, e.getEnclosedElements(), onReceiveType)) {
217 if (!generateOperation(e, body, className, e.getEnclosedElements(), functions)) {
220 FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
221 w = new OutputStreamWriter(java.openOutputStream());
223 w.append("package " + pkg + ";\n");
224 w.append("import net.java.html.json.*;\n");
225 w.append("public final class ").append(className).append(" implements Cloneable {\n");
226 w.append(" private static final Html4JavaType TYPE = new Html4JavaType();\n");
227 w.append(" private final org.netbeans.html.json.spi.Proto proto;\n");
228 w.append(body.toString());
229 w.append(" private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
230 w.append(" private ").append(className).append("(net.java.html.BrwsrCtx context) {\n");
231 w.append(" this.proto = TYPE.createProto(this, context);\n");
232 for (Prprt p : props) {
234 final String tn = typeName(e, p);
235 String[] gs = toGetSet(p.name(), tn, p.array());
236 w.write(" this.prop_" + p.name() + " = proto.createList(\""
238 if (functionDeps.containsKey(p.name())) {
239 int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name());
240 w.write(", " + index);
244 Collection<String[]> dependants = propsDeps.get(p.name());
245 if (dependants != null) {
246 for (String[] depProp : dependants) {
258 w.append(" public ").append(className).append("() {\n");
259 w.append(" this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
260 for (Prprt p : props) {
262 boolean[] isModel = {false};
263 boolean[] isEnum = {false};
264 boolean isPrimitive[] = {false};
265 String tn = checkType(p, isModel, isEnum, isPrimitive);
267 w.write(" prop_" + p.name() + " = new " + tn + "();\n");
272 if (props.length > 0) {
273 w.append(" public ").append(className).append("(");
274 Prprt firstArray = null;
276 for (Prprt p : props) {
278 if (firstArray == null) {
283 String tn = typeName(e, p);
286 w.write(" " + p.name());
289 if (firstArray != null) {
291 boolean[] isModel = {false};
292 boolean[] isEnum = {false};
293 boolean isPrimitive[] = {false};
294 tn = checkType(firstArray, isModel, isEnum, isPrimitive);
297 w.write("... " + firstArray.name());
300 w.append(" this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class));\n");
301 for (Prprt p : props) {
305 w.write(" this.prop_" + p.name() + " = " + p.name() + ";\n");
307 if (firstArray != null) {
308 w.write(" proto.initTo(this.prop_" + firstArray.name() + ", " + firstArray.name() + ");\n");
312 w.append(" private static class Html4JavaType extends org.netbeans.html.json.spi.Proto.Type<").append(className).append("> {\n");
313 w.append(" private Html4JavaType() {\n super(").append(className).append(".class, ").
314 append(inPckName(e)).append(".class, " + propsGetSet.size() + ", "
315 + functionsCount + ");\n");
317 for (int i = 0; i < propsGetSet.size(); i++) {
318 w.append(" registerProperty(\"").append(propsGetSet.get(i).name).append("\", ");
319 w.append((i) + ", " + propsGetSet.get(i).readOnly + ");\n");
323 for (int i = 0; i < functionsCount; i++) {
324 w.append(" registerFunction(\"").append((String)functions.get(i * 2)).append("\", ");
325 w.append(i + ");\n");
329 w.append(" @Override public void setValue(" + className + " data, int type, Object value) {\n");
330 w.append(" switch (type) {\n");
331 for (int i = 0; i < propsGetSet.size(); i++) {
332 final GetSet pgs = propsGetSet.get(i);
336 final String set = pgs.setter;
337 String tn = pgs.type;
338 String btn = findBoxedType(tn);
342 w.append(" case " + i + ": ");
343 if (pgs.setter != null) {
344 w.append("data.").append(strip(pgs.setter)).append("(TYPE.extractValue(" + tn + ".class, value)); return;\n");
346 w.append("TYPE.replaceValue(data.").append(strip(pgs.getter)).append("(), " + tn + ".class, value); return;\n");
350 w.append(" throw new UnsupportedOperationException();\n");
352 w.append(" @Override public Object getValue(" + className + " data, int type) {\n");
353 w.append(" switch (type) {\n");
354 for (int i = 0; i < propsGetSet.size(); i++) {
355 final String get = propsGetSet.get(i).getter;
357 w.append(" case " + i + ": return data." + strip(get) + "();\n");
361 w.append(" throw new UnsupportedOperationException();\n");
363 w.append(" @Override public void call(" + className + " model, int type, Object data, Object ev) throws Exception {\n");
364 w.append(" switch (type) {\n");
365 for (int i = 0; i < functions.size(); i += 2) {
366 final String name = (String)functions.get(i);
367 final Object param = functions.get(i + 1);
368 if (param instanceof ExecutableElement) {
369 ExecutableElement ee = (ExecutableElement)param;
370 w.append(" case " + (i / 2) + ":\n");
371 w.append(" ").append(((TypeElement)e).getQualifiedName()).append(".").append(name).append("(");
372 w.append(wrapParams(ee, null, className, "model", "ev", "data"));
374 w.append(" return;\n");
376 String call = (String)param;
377 w.append(" case " + (i / 2) + ":\n"); // model." + name + "(data, ev); return;\n");
378 w.append(" ").append(call).append("\n");
379 w.append(" return;\n");
384 w.append(" throw new UnsupportedOperationException();\n");
386 w.append(" @Override public org.netbeans.html.json.spi.Proto protoFor(Object obj) {\n");
387 w.append(" return ((" + className + ")obj).proto;");
389 w.append(" @Override public void onChange(" + className + " model, int type) {\n");
390 w.append(" switch (type) {\n");
392 String[] arr = functionDeps.keySet().toArray(new String[0]);
393 for (int i = 0; i < arr.length; i++) {
394 Collection<String> onChange = functionDeps.get(arr[i]);
395 if (onChange != null) {
396 w.append(" case " + i + ":\n");
397 for (String call : onChange) {
398 w.append(" ").append(call).append("\n");
400 w.write(" return;\n");
405 w.append(" throw new UnsupportedOperationException();\n");
407 w.append(onReceiveType);
408 w.append(" @Override public " + className + " read(net.java.html.BrwsrCtx c, Object json) { return new " + className + "(c, json); }\n");
409 w.append(" @Override public " + className + " cloneTo(" + className + " o, net.java.html.BrwsrCtx c) { return o.clone(c); }\n");
411 w.append(" private ").append(className).append("(net.java.html.BrwsrCtx c, Object json) {\n");
412 w.append(" this(c);\n");
414 for (int i = 0; i < propsGetSet.size(); i++) {
415 Prprt p = findPrprt(props, propsGetSet.get(i).name);
421 w.append(" Object[] ret = new Object[" + values + "];\n");
422 w.append(" proto.extract(json, new String[] {\n");
423 for (int i = 0; i < propsGetSet.size(); i++) {
424 Prprt p = findPrprt(props, propsGetSet.get(i).name);
428 w.append(" \"").append(propsGetSet.get(i).name).append("\",\n");
430 w.append(" }, ret);\n");
431 for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i++) {
432 final String pn = propsGetSet.get(i).name;
433 Prprt p = findPrprt(props, pn);
434 if (p == null || prop >= props.length) {
437 boolean[] isModel = { false };
438 boolean[] isEnum = { false };
439 boolean isPrimitive[] = { false };
440 String type = checkType(props[prop++], isModel, isEnum, isPrimitive);
442 w.append(" if (ret[" + cnt + "] instanceof Object[]) {\n");
443 w.append(" for (Object e : ((Object[])ret[" + cnt + "])) {\n");
445 w.append(" this.prop_").append(pn).append(".add(proto.read");
446 w.append("(" + type + ".class, e));\n");
447 } else if (isEnum[0]) {
448 w.append(" this.prop_").append(pn);
449 w.append(".add(e == null ? null : ");
450 w.append(type).append(".valueOf(TYPE.stringValue(e)));\n");
452 if (isPrimitive(type)) {
453 w.append(" this.prop_").append(pn).append(".add(TYPE.numberValue(e).");
454 w.append(type).append("Value());\n");
456 w.append(" this.prop_").append(pn).append(".add((");
457 w.append(type).append(")e);\n");
464 w.append(" try {\n");
465 w.append(" this.prop_").append(pn);
466 w.append(" = ret[" + cnt + "] == null ? null : ");
467 w.append(type).append(".valueOf(TYPE.stringValue(ret[" + cnt + "]));\n");
468 w.append(" } catch (IllegalArgumentException ex) {\n");
469 w.append(" ex.printStackTrace();\n");
471 } else if (isPrimitive(type)) {
472 w.append(" this.prop_").append(pn);
473 w.append(" = ret[" + cnt + "] == null ? ");
474 if ("char".equals(type)) {
475 w.append("0 : (TYPE.charValue(");
476 } else if ("boolean".equals(type)) {
477 w.append("false : (TYPE.boolValue(");
479 w.append("0 : (TYPE.numberValue(");
481 w.append("ret[" + cnt + "])).");
482 w.append(type).append("Value();\n");
483 } else if (isModel[0]) {
484 w.append(" this.prop_").append(pn).append(" = proto.read");
485 w.append("(" + type + ".class, ");
486 w.append("ret[" + cnt + "]);\n");
488 w.append(" this.prop_").append(pn);
489 w.append(" = (").append(type).append(')');
490 w.append("ret[" + cnt + "];\n");
496 writeToString(props, w);
497 writeClone(className, props, w);
498 w.write(" /** Activates this model instance in the current {@link \n"
499 + "net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}. \n"
500 + "In case of using Knockout technology, this means to \n"
501 + "bind JSON like data in this model instance with Knockout tags in \n"
502 + "the surrounding HTML page.\n"
505 w.write(" public " + className + " applyBindings() {\n");
506 w.write(" proto.applyBindings();\n");
507 w.write(" return this;\n");
509 w.write(" public boolean equals(Object o) {\n");
510 w.write(" if (o == this) return true;\n");
511 w.write(" if (!(o instanceof " + className + ")) return false;\n");
512 w.write(" " + className + " p = (" + className + ")o;\n");
513 for (Prprt p : props) {
514 w.write(" if (!TYPE.isSame(prop_" + p.name() + ", p.prop_" + p.name() + ")) return false;\n");
516 w.write(" return true;\n");
518 w.write(" public int hashCode() {\n");
519 w.write(" int h = " + className + ".class.getName().hashCode();\n");
520 for (Prprt p : props) {
521 w.write(" h = TYPE.hashPlus(prop_" + p.name() + ", h);\n");
523 w.write(" return h;\n");
529 } catch (IOException ex) {
530 error("Can't create " + className + ".java", e);
536 private boolean generateProperties(
538 Writer w, String className, Prprt[] properties,
540 Map<String,Collection<String[]>> deps,
541 Map<String,Collection<String>> functionDeps
542 ) throws IOException {
544 for (Prprt p : properties) {
546 tn = typeName(where, p);
547 String[] gs = toGetSet(p.name(), tn, p.array());
551 w.write(" private final java.util.List<" + tn + "> prop_" + p.name() + ";\n");
553 castTo = "java.util.List";
554 w.write(" public java.util.List<" + tn + "> " + gs[0] + "() {\n");
555 w.write(" proto.accessProperty(\"" + p.name() + "\");\n");
556 w.write(" return prop_" + p.name() + ";\n");
560 w.write(" private " + tn + " prop_" + p.name() + ";\n");
561 w.write(" public " + tn + " " + gs[0] + "() {\n");
562 w.write(" proto.accessProperty(\"" + p.name() + "\");\n");
563 w.write(" return prop_" + p.name() + ";\n");
565 w.write(" public void " + gs[1] + "(" + tn + " v) {\n");
566 w.write(" proto.verifyUnlocked();\n");
567 w.write(" if (TYPE.isSame(prop_" + p.name() + ", v)) return;\n");
568 w.write(" Object o = prop_" + p.name() + ";\n");
569 w.write(" prop_" + p.name() + " = v;\n");
570 w.write(" proto.valueHasMutated(\"" + p.name() + "\", o, v);\n");
572 Collection<String[]> dependants = deps.get(p.name());
573 if (dependants != null) {
574 for (String[] pair : dependants) {
575 w.write(" proto.valueHasMutated(\"" + pair[0] + "\", null, " + pair[1] + "());\n");
580 Collection<String> dependants = functionDeps.get(p.name());
581 if (dependants != null) {
582 w.append(className).append(" model = ").append(className).append(".this;\n");
583 for (String call : dependants) {
584 w.append(" ").append(call);
591 for (int i = 0; i < props.size(); i++) {
592 if (props.get(i).name.equals(p.name())) {
593 error("Cannot have the name " + p.name() + " defined twice", where);
598 props.add(new GetSet(
603 gs[3] == null && !p.array()
609 private boolean generateComputedProperties(
610 Writer w, Prprt[] fixedProps,
611 Collection<? extends Element> arr, Collection<GetSet> props,
612 Map<String,Collection<String[]>> deps
613 ) throws IOException {
615 for (Element e : arr) {
616 if (e.getKind() != ElementKind.METHOD) {
619 final ComputedProperty cp = e.getAnnotation(ComputedProperty.class);
620 final Transitive tp = e.getAnnotation(Transitive.class);
624 if (!e.getModifiers().contains(Modifier.STATIC)) {
625 error("Method " + e.getSimpleName() + " has to be static when annotated by @ComputedProperty", e);
629 ExecutableElement ee = (ExecutableElement)e;
630 final TypeMirror rt = ee.getReturnType();
631 final Types tu = processingEnv.getTypeUtils();
632 TypeMirror ert = tu.erasure(rt);
633 String tn = fqn(ert, ee);
634 boolean array = false;
635 final TypeMirror toCheck;
636 if (tn.equals("java.util.List")) {
638 toCheck = ((DeclaredType)rt).getTypeArguments().get(0);
643 final String sn = ee.getSimpleName().toString();
645 if (toCheck.getKind().isPrimitive()) {
648 TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
649 TypeMirror enumType = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
651 if (tu.isSubtype(toCheck, stringType)) {
653 } else if (tu.isSubtype(tu.erasure(toCheck), tu.erasure(enumType))) {
655 } else if (isModel(toCheck)) {
659 tu.unboxedType(toCheck);
660 // boxed types are OK
661 } catch (IllegalArgumentException ex) {
663 error(sn + " cannot return " + toCheck, e);
668 String[] gs = toGetSet(sn, tn, array);
670 w.write(" public " + tn);
672 w.write("<" + toCheck + ">");
674 w.write(" " + gs[0] + "() {\n");
676 boolean deep = false;
677 for (VariableElement pe : ee.getParameters()) {
678 final String dn = pe.getSimpleName().toString();
680 if (!verifyPropName(pe, dn, fixedProps)) {
683 final TypeMirror pt = pe.asType();
687 final String dt = fqn(pt, ee);
688 if (dt.startsWith("java.util.List") && pt instanceof DeclaredType) {
689 final List<? extends TypeMirror> ptArgs = ((DeclaredType)pt).getTypeArguments();
690 if (ptArgs.size() == 1 && isModel(ptArgs.get(0))) {
694 String[] call = toGetSet(dn, dt, false);
695 w.write(" " + dt + " arg" + (++arg) + " = ");
696 w.write(call[0] + "();\n");
698 Collection<String[]> depends = deps.get(dn);
699 if (depends == null) {
700 depends = new LinkedHashSet<String[]>();
701 deps.put(dn, depends);
703 depends.add(new String[] { sn, gs[0] });
710 w.write(" proto.acquireLock(\"" + sn + "\");\n");
712 w.write(" proto.acquireLock();\n");
714 w.write(" return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
716 for (int i = 1; i <= arg; i++) {
722 w.write(" } finally {\n");
723 w.write(" proto.releaseLock();\n");
727 props.add(new GetSet(
728 e.getSimpleName().toString(),
739 private static String[] toGetSet(String name, String type, boolean array) {
740 String n = Character.toUpperCase(name.charAt(0)) + name.substring(1);
741 String bck2brwsrType = "L" + type.replace('.', '_') + "_2";
742 if ("int".equals(type)) {
745 if ("double".equals(type)) {
749 if ("boolean".equals(type)) {
753 final String nu = n.replace('.', '_');
755 return new String[] {
758 "get" + nu + "__Ljava_util_List_2",
765 pref + nu + "__" + bck2brwsrType,
766 "set" + nu + "__V" + bck2brwsrType
770 private String typeName(Element where, Prprt p) {
772 boolean[] isModel = { false };
773 boolean[] isEnum = { false };
774 boolean isPrimitive[] = { false };
775 ret = checkType(p, isModel, isEnum, isPrimitive);
777 String bt = findBoxedType(ret);
785 private static String findBoxedType(String ret) {
786 if (ret.equals("boolean")) {
787 return Boolean.class.getName();
789 if (ret.equals("byte")) {
790 return Byte.class.getName();
792 if (ret.equals("short")) {
793 return Short.class.getName();
795 if (ret.equals("char")) {
796 return Character.class.getName();
798 if (ret.equals("int")) {
799 return Integer.class.getName();
801 if (ret.equals("long")) {
802 return Long.class.getName();
804 if (ret.equals("float")) {
805 return Float.class.getName();
807 if (ret.equals("double")) {
808 return Double.class.getName();
813 private boolean verifyPropName(Element e, String propName, Prprt[] existingProps) {
814 StringBuilder sb = new StringBuilder();
816 for (Prprt Prprt : existingProps) {
817 if (Prprt.name().equals(propName)) {
822 sb.append(Prprt.name());
827 propName + " is not one of known properties: " + sb
833 private static String findPkgName(Element e) {
835 if (e.getKind() == ElementKind.PACKAGE) {
836 return ((PackageElement)e).getQualifiedName().toString();
838 e = e.getEnclosingElement();
842 private boolean generateFunctions(
843 Element clazz, StringWriter body, String className,
844 List<? extends Element> enclosedElements, List<Object> functions
846 for (Element m : enclosedElements) {
847 if (m.getKind() != ElementKind.METHOD) {
850 ExecutableElement e = (ExecutableElement)m;
851 Function onF = e.getAnnotation(Function.class);
855 if (!e.getModifiers().contains(Modifier.STATIC)) {
856 error("@OnFunction method needs to be static", e);
859 if (e.getModifiers().contains(Modifier.PRIVATE)) {
860 error("@OnFunction method cannot be private", e);
863 if (e.getReturnType().getKind() != TypeKind.VOID) {
864 error("@OnFunction method should return void", e);
867 String n = e.getSimpleName().toString();
874 private boolean generateOnChange(Element clazz, Map<String,Collection<String[]>> propDeps,
875 Prprt[] properties, String className,
876 Map<String, Collection<String>> functionDeps
878 for (Element m : clazz.getEnclosedElements()) {
879 if (m.getKind() != ElementKind.METHOD) {
882 ExecutableElement e = (ExecutableElement) m;
883 OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class);
887 for (String pn : onPC.value()) {
888 if (findPrprt(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
889 error("No Prprt named '" + pn + "' in the model", clazz);
893 if (!e.getModifiers().contains(Modifier.STATIC)) {
894 error("@OnPrprtChange method needs to be static", e);
897 if (e.getModifiers().contains(Modifier.PRIVATE)) {
898 error("@OnPrprtChange method cannot be private", e);
901 if (e.getReturnType().getKind() != TypeKind.VOID) {
902 error("@OnPrprtChange method should return void", e);
905 String n = e.getSimpleName().toString();
908 for (String pn : onPC.value()) {
909 StringBuilder call = new StringBuilder();
910 call.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
911 call.append(wrapPropName(e, className, "name", pn));
914 Collection<String> change = functionDeps.get(pn);
915 if (change == null) {
916 change = new ArrayList<String>();
917 functionDeps.put(pn, change);
919 change.add(call.toString());
920 for (String dpn : findDerivedFrom(propDeps, pn)) {
921 change = functionDeps.get(dpn);
922 if (change == null) {
923 change = new ArrayList<String>();
924 functionDeps.put(dpn, change);
926 change.add(call.toString());
933 private boolean generateOperation(Element clazz,
934 StringWriter body, String className,
935 List<? extends Element> enclosedElements,
936 List<Object> functions
938 for (Element m : enclosedElements) {
939 if (m.getKind() != ElementKind.METHOD) {
942 ExecutableElement e = (ExecutableElement)m;
943 ModelOperation mO = e.getAnnotation(ModelOperation.class);
947 if (!e.getModifiers().contains(Modifier.STATIC)) {
948 error("@ModelOperation method needs to be static", e);
951 if (e.getModifiers().contains(Modifier.PRIVATE)) {
952 error("@ModelOperation method cannot be private", e);
955 if (e.getReturnType().getKind() != TypeKind.VOID) {
956 error("@ModelOperation method should return void", e);
959 List<String> args = new ArrayList<String>();
961 body.append(" public void ").append(m.getSimpleName()).append("(");
963 boolean checkFirst = true;
964 for (VariableElement ve : e.getParameters()) {
965 final TypeMirror type = ve.asType();
966 CharSequence simpleName;
967 if (type.getKind() == TypeKind.DECLARED) {
968 simpleName = ((DeclaredType)type).asElement().getSimpleName();
970 simpleName = type.toString();
972 if (checkFirst && simpleName.toString().equals(className)) {
976 error("First parameter of @ModelOperation method must be " + className, m);
979 args.add(ve.getSimpleName().toString());
980 body.append(sep).append("final ");
981 body.append(ve.asType().toString()).append(" ");
982 body.append(ve.toString());
986 body.append(") {\n");
987 int idx = functions.size() / 2;
988 functions.add(m.getSimpleName().toString());
989 body.append(" proto.runInBrowser(" + idx);
990 for (String s : args) {
991 body.append(", ").append(s);
996 StringBuilder call = new StringBuilder();
997 call.append("{ Object[] arr = (Object[])data; ");
998 call.append(inPckName(clazz)).append(".").append(m.getSimpleName()).append("(");
1000 for (VariableElement ve : e.getParameters()) {
1002 call.append("model");
1005 String type = ve.asType().toString();
1006 String boxedType = findBoxedType(type);
1007 if (boxedType != null) {
1010 call.append(", ").append("(").append(type).append(")arr[").append(i - 2).append("]");
1012 call.append("); }");
1013 functions.add(call.toString());
1021 private boolean generateReceive(
1022 Element clazz, StringWriter body, String className,
1023 List<? extends Element> enclosedElements, StringBuilder inType
1025 inType.append(" @Override public void onMessage(").append(className).append(" model, int index, int type, Object data, Object[] params) {\n");
1026 inType.append(" switch (index) {\n");
1028 for (Element m : enclosedElements) {
1029 if (m.getKind() != ElementKind.METHOD) {
1032 ExecutableElement e = (ExecutableElement)m;
1033 OnReceive onR = e.getAnnotation(OnReceive.class);
1037 if (!e.getModifiers().contains(Modifier.STATIC)) {
1038 error("@OnReceive method needs to be static", e);
1041 if (e.getModifiers().contains(Modifier.PRIVATE)) {
1042 error("@OnReceive method cannot be private", e);
1045 if (e.getReturnType().getKind() != TypeKind.VOID) {
1046 error("@OnReceive method should return void", e);
1049 if (!onR.jsonp().isEmpty() && !"GET".equals(onR.method())) {
1050 error("JSONP works only with GET transport method", e);
1052 String dataMirror = findDataSpecified(e, onR);
1053 if ("PUT".equals(onR.method()) && dataMirror == null) {
1054 error("PUT method needs to specify a data() class", e);
1057 if ("POST".equals(onR.method()) && dataMirror == null) {
1058 error("POST method needs to specify a data() class", e);
1061 if (e.getParameters().size() < 2) {
1062 error("@OnReceive method needs at least two parameters", e);
1064 int expectsList = 0;
1065 List<String> args = new ArrayList<String>();
1066 List<String> params = new ArrayList<String>();
1067 // first argument is model class
1069 TypeMirror type = e.getParameters().get(0).asType();
1070 CharSequence simpleName;
1071 if (type.getKind() == TypeKind.DECLARED) {
1072 simpleName = ((DeclaredType) type).asElement().getSimpleName();
1074 simpleName = type.toString();
1076 if (simpleName.toString().equals(className)) {
1079 error("First parameter needs to be " + className, e);
1086 final Types tu = processingEnv.getTypeUtils();
1087 TypeMirror type = e.getParameters().get(1).asType();
1088 TypeMirror modelType = null;
1089 TypeMirror ert = tu.erasure(type);
1091 if (isModel(type)) {
1093 } else if (type.getKind() == TypeKind.ARRAY) {
1094 modelType = ((ArrayType)type).getComponentType();
1096 } else if ("java.util.List".equals(fqn(ert, e))) {
1097 List<? extends TypeMirror> typeArgs = ((DeclaredType)type).getTypeArguments();
1098 if (typeArgs.size() == 1) {
1099 modelType = typeArgs.get(0);
1102 } else if (type.toString().equals("java.lang.String")) {
1105 if (modelType == null) {
1106 error("Second arguments needs to be a model, String or array or List of models", e);
1109 modelClass = modelType.toString();
1110 if (expectsList == 1) {
1112 } else if (expectsList == 2) {
1113 args.add("java.util.Arrays.asList(arr)");
1118 String n = e.getSimpleName().toString();
1119 final boolean isWebSocket = "WebSocket".equals(onR.method());
1121 body.append(" /** Performs WebSocket communication. Call with <code>null</code> data parameter\n");
1122 body.append(" * to open the connection (even if not required). Call with non-null data to\n");
1123 body.append(" * send messages to server. Call again with <code>null</code> data to close the socket.\n");
1124 body.append(" */\n");
1126 body.append(" public void ").append(n).append("(");
1127 StringBuilder urlBefore = new StringBuilder();
1128 StringBuilder urlAfter = new StringBuilder();
1129 String jsonpVarName = null;
1132 boolean skipJSONP = onR.jsonp().isEmpty();
1133 for (String p : findParamNames(e, onR.url(), onR.jsonp(), urlBefore, urlAfter)) {
1134 if (!skipJSONP && p.equals(onR.jsonp())) {
1140 body.append("String ").append(p);
1145 "Name of jsonp attribute ('" + onR.jsonp() +
1146 "') is not used in url attribute '" + onR.url() + "'", e
1149 if (dataMirror != null) {
1150 body.append(sep).append(dataMirror.toString()).append(" data");
1152 for (int i = 2; i < e.getParameters().size(); i++) {
1154 error("@OnReceive(method=\"WebSocket\") can only have two arguments", e);
1158 VariableElement ve = e.getParameters().get(i);
1159 body.append(sep).append(ve.asType().toString()).append(" ").append(ve.getSimpleName());
1160 final String tp = ve.asType().toString();
1161 String btn = findBoxedType(tp);
1165 args.add("(" + btn + ")params[" + (i - 2) + "]");
1166 params.add(ve.getSimpleName().toString());
1170 body.append(") {\n");
1171 boolean webSocket = onR.method().equals("WebSocket");
1173 if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
1176 body.append(" }\n");
1177 body.append(" private Object ws_" + e.getSimpleName() + ";\n");
1179 if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
1182 body.append(" }\n");
1185 inType.append(" }\n");
1186 inType.append(" throw new UnsupportedOperationException(\"index: \" + index + \" type: \" + type);\n");
1187 inType.append(" }\n");
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) {
1193 " case " + index + ": {\n" +
1194 " if (type == 2) { /* on error */\n" +
1195 " Exception ex = (Exception)data;\n"
1197 if (onR.onError().isEmpty()) {
1199 " ex.printStackTrace();\n"
1202 if (!findOnError(e, ((TypeElement)clazz), onR.onError(), className)) {
1205 body.append(" ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
1206 body.append("model, ex);\n");
1210 " } else if (type == 1) {\n" +
1211 " Object[] ev = (Object[])data;\n"
1215 " " + modelClass + "[] arr = new " + modelClass + "[ev.length];\n"
1219 " " + modelClass + "[] arr = { null };\n"
1223 " TYPE.copyJSON(model.proto.getContext(), ev, " + modelClass + ".class, arr);\n"
1226 body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
1228 for (String arg : args) {
1233 body.append(");\n");
1240 method.append(" proto.loadJSON(" + index + ",\n ");
1241 method.append(urlBefore).append(", ");
1242 if (jsonpVarName != null) {
1243 method.append(urlAfter);
1245 method.append("null");
1247 if (!"GET".equals(onR.method()) || dataMirror != null) {
1248 method.append(", \"").append(onR.method()).append('"');
1249 if (dataMirror != null) {
1250 method.append(", data");
1252 method.append(", null");
1255 method.append(", null, null");
1257 for (String a : params) {
1258 method.append(", ").append(a);
1260 method.append(");\n");
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) {
1266 " case " + index + ": {\n" +
1267 " if (type == 0) { /* on open */\n" +
1268 " ").append(clazz.getSimpleName()).append(".").append(n).append("(");
1271 for (String arg : args) {
1273 if (arg.startsWith("arr") || arg.startsWith("java.util.Array")) {
1274 body.append("null");
1281 body.append(");\n");
1284 " } else if (type == 2) { /* on error */\n" +
1285 " Exception value = (Exception)data;\n"
1287 if (onR.onError().isEmpty()) {
1289 " value.printStackTrace();\n"
1292 if (!findOnError(e, ((TypeElement)clazz), onR.onError(), className)) {
1295 body.append(" ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
1296 body.append("model, value);\n");
1300 " } else if (type == 1) {\n" +
1301 " Object[] ev = (Object[])data;\n"
1305 " " + modelClass + "[] arr = new " + modelClass + "[ev.length];\n"
1309 " " + modelClass + "[] arr = { null };\n"
1313 " TYPE.copyJSON(model.proto.getContext(), ev, " + modelClass + ".class, arr);\n"
1316 body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
1318 for (String arg : args) {
1323 body.append(");\n");
1329 if (!onR.onError().isEmpty()) {
1330 body.append(" else if (type == 3) { /* on close */\n");
1331 body.append(" ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
1332 body.append("model, null);\n");
1339 body.append(" }\n");
1340 method.append(" if (this.ws_").append(e.getSimpleName()).append(" == null) {\n");
1341 method.append(" this.ws_").append(e.getSimpleName());
1342 method.append("= proto.wsOpen(" + index + ", ");
1343 method.append(urlBefore).append(", data);\n");
1344 method.append(" } else {\n");
1345 method.append(" proto.wsSend(this.ws_").append(e.getSimpleName()).append(", ").append(urlBefore).append(", data");
1346 for (String a : params) {
1347 method.append(", ").append(a);
1349 method.append(");\n");
1350 method.append(" }\n");
1354 private CharSequence wrapParams(
1355 ExecutableElement ee, String id, String className, String classRef, String evName, String dataName
1357 TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
1358 StringBuilder params = new StringBuilder();
1359 boolean first = true;
1360 for (VariableElement ve : ee.getParameters()) {
1362 params.append(", ");
1365 String toCall = null;
1366 String toFinish = null;
1367 boolean addNull = true;
1368 if (ve.asType() == stringType) {
1369 if (ve.getSimpleName().contentEquals("id")) {
1370 params.append('"').append(id).append('"');
1373 toCall = classRef + ".proto.toString(";
1375 if (ve.asType().getKind() == TypeKind.DOUBLE) {
1376 toCall = classRef + ".proto.toNumber(";
1377 toFinish = ".doubleValue()";
1379 if (ve.asType().getKind() == TypeKind.INT) {
1380 toCall = classRef + ".proto.toNumber(";
1381 toFinish = ".intValue()";
1383 if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
1384 toCall = classRef + ".proto.toModel(" + ve.asType() + ".class, ";
1388 if (toCall != null) {
1389 params.append(toCall);
1390 if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
1391 params.append(dataName);
1393 params.append(", null");
1396 if (evName == null) {
1397 final StringBuilder sb = new StringBuilder();
1398 sb.append("Unexpected string parameter name.");
1399 if (dataName != null) {
1400 sb.append(" Try \"").append(dataName).append("\"");
1402 error(sb.toString(), ee);
1404 params.append(evName);
1405 params.append(", \"");
1406 params.append(ve.getSimpleName().toString());
1407 params.append("\"");
1410 if (toFinish != null) {
1411 params.append(toFinish);
1415 String rn = fqn(ve.asType(), ee);
1416 int last = rn.lastIndexOf('.');
1418 rn = rn.substring(last + 1);
1420 if (rn.equals(className)) {
1421 params.append(classRef);
1425 "The annotated method can only accept " + className + " argument or argument named 'data'",
1433 private CharSequence wrapPropName(
1434 ExecutableElement ee, String className, String propName, String propValue
1436 TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
1437 StringBuilder params = new StringBuilder();
1438 boolean first = true;
1439 for (VariableElement ve : ee.getParameters()) {
1441 params.append(", ");
1444 if (ve.asType() == stringType) {
1445 if (propName != null && ve.getSimpleName().contentEquals(propName)) {
1446 params.append('"').append(propValue).append('"');
1448 error("Unexpected string parameter name. Try \"" + propName + "\".", ee);
1452 String rn = fqn(ve.asType(), ee);
1453 int last = rn.lastIndexOf('.');
1455 rn = rn.substring(last + 1);
1457 if (rn.equals(className)) {
1458 params.append("model");
1462 "@OnPrprtChange method can only accept String or " + className + " arguments",
1468 private boolean isModel(TypeMirror tm) {
1469 if (tm.getKind() == TypeKind.ERROR) {
1472 final Element e = processingEnv.getTypeUtils().asElement(tm);
1476 for (Element ch : e.getEnclosedElements()) {
1477 if (ch.getKind() == ElementKind.METHOD) {
1478 ExecutableElement ee = (ExecutableElement)ch;
1479 if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) {
1484 return models.values().contains(e.getSimpleName().toString());
1487 private void writeToString(Prprt[] props, Writer w) throws IOException {
1488 w.write(" public String toString() {\n");
1489 w.write(" StringBuilder sb = new StringBuilder();\n");
1490 w.write(" sb.append('{');\n");
1492 for (Prprt p : props) {
1494 w.append(" sb.append('\"').append(\"" + p.name() + "\")");
1495 w.append(".append('\"').append(\":\");\n");
1496 w.append(" sb.append(TYPE.toJSON(prop_");
1497 w.append(p.name()).append("));\n");
1498 sep = " sb.append(',');\n";
1500 w.write(" sb.append('}');\n");
1501 w.write(" return sb.toString();\n");
1504 private void writeClone(String className, Prprt[] props, Writer w) throws IOException {
1505 w.write(" public " + className + " clone() {\n");
1506 w.write(" return clone(proto.getContext());\n");
1508 w.write(" private " + className + " clone(net.java.html.BrwsrCtx ctx) {\n");
1509 w.write(" " + className + " ret = new " + className + "(ctx);\n");
1510 for (Prprt p : props) {
1512 boolean isModel[] = { false };
1513 boolean isEnum[] = { false };
1514 boolean isPrimitive[] = { false };
1515 checkType(p, isModel, isEnum, isPrimitive);
1517 w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + ";\n");
1520 w.write(" ret.prop_" + p.name() + " = prop_" + p.name() + " == null ? null : prop_" + p.name() + ".clone();\n");
1522 w.write(" proto.cloneList(ret.prop_" + p.name() + ", ctx, prop_" + p.name() + ");\n");
1526 w.write(" return ret;\n");
1530 private String inPckName(Element e) {
1531 StringBuilder sb = new StringBuilder();
1532 while (e.getKind() != ElementKind.PACKAGE) {
1533 if (sb.length() == 0) {
1534 sb.append(e.getSimpleName());
1537 sb.insert(0, e.getSimpleName());
1539 e = e.getEnclosingElement();
1541 return sb.toString();
1544 private String fqn(TypeMirror pt, Element relative) {
1545 if (pt.getKind() == TypeKind.ERROR) {
1546 final Elements eu = processingEnv.getElementUtils();
1547 PackageElement pckg = eu.getPackageOf(relative);
1548 return pckg.getQualifiedName() + "." + pt.toString();
1550 return pt.toString();
1553 private String checkType(Prprt p, boolean[] isModel, boolean[] isEnum, boolean[] isPrimitive) {
1556 String ret = p.typeName(processingEnv);
1557 TypeElement e = processingEnv.getElementUtils().getTypeElement(ret);
1561 isPrimitive[0] = false;
1565 } catch (MirroredTypeException ex) {
1566 tm = ex.getTypeMirror();
1568 tm = processingEnv.getTypeUtils().erasure(tm);
1569 if (isPrimitive[0] = tm.getKind().isPrimitive()) {
1572 return tm.toString();
1574 final Element e = processingEnv.getTypeUtils().asElement(tm);
1575 if (e.getKind() == ElementKind.CLASS && tm.getKind() == TypeKind.ERROR) {
1578 return e.getSimpleName().toString();
1581 final Model m = e == null ? null : e.getAnnotation(Model.class);
1584 ret = findPkgName(e) + '.' + m.className();
1586 models.put(e, m.className());
1587 } else if (findModelForMthd(e)) {
1588 ret = ((TypeElement)e).getQualifiedName().toString();
1591 ret = tm.toString();
1593 TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
1594 enm = processingEnv.getTypeUtils().erasure(enm);
1595 isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
1599 private static boolean findModelForMthd(Element clazz) {
1600 if (clazz == null) {
1603 for (Element e : clazz.getEnclosedElements()) {
1604 if (e.getKind() == ElementKind.METHOD) {
1605 ExecutableElement ee = (ExecutableElement)e;
1607 ee.getSimpleName().contentEquals("modelFor") &&
1608 ee.getParameters().isEmpty()
1617 private Iterable<String> findParamNames(
1618 Element e, String url, String jsonParam, StringBuilder... both
1620 List<String> params = new ArrayList<String>();
1623 for (int pos = 0; ;) {
1624 int next = url.indexOf('{', pos);
1626 both[wasJSON].append('"')
1627 .append(url.substring(pos))
1631 int close = url.indexOf('}', next);
1633 error("Unbalanced '{' and '}' in " + url, e);
1636 final String paramName = url.substring(next + 1, close);
1637 params.add(paramName);
1638 if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
1639 both[wasJSON].append('"')
1640 .append(url.substring(pos, next))
1644 both[wasJSON].append('"')
1645 .append(url.substring(pos, next))
1646 .append("\" + ").append(paramName).append(" + ");
1652 private static Prprt findPrprt(Prprt[] properties, String propName) {
1653 for (Prprt p : properties) {
1654 if (propName.equals(p.name())) {
1661 private boolean isPrimitive(String type) {
1663 "int".equals(type) ||
1664 "double".equals(type) ||
1665 "long".equals(type) ||
1666 "short".equals(type) ||
1667 "byte".equals(type) ||
1668 "char".equals(type) ||
1669 "boolean".equals(type) ||
1670 "float".equals(type);
1673 private static Collection<String> findDerivedFrom(Map<String, Collection<String[]>> propsDeps, String derivedProp) {
1674 Set<String> names = new HashSet<String>();
1675 for (Map.Entry<String, Collection<String[]>> e : propsDeps.entrySet()) {
1676 for (String[] pair : e.getValue()) {
1677 if (pair[0].equals(derivedProp)) {
1678 names.add(e.getKey());
1686 private Prprt[] createProps(Element e, Property[] arr) {
1687 Prprt[] ret = Prprt.wrap(processingEnv, e, arr);
1688 Prprt[] prev = verify.put(e, ret);
1690 error("Two sets of properties for ", e);
1695 private static String strip(String s) {
1696 int indx = s.indexOf("__");
1698 return s.substring(0, indx);
1704 private String findDataSpecified(ExecutableElement e, OnReceive onR) {
1706 return onR.data().getName();
1707 } catch (MirroredTypeException ex) {
1708 final TypeMirror tm = ex.getTypeMirror();
1710 final Element te = processingEnv.getTypeUtils().asElement(tm);
1711 if (te.getKind() == ElementKind.CLASS && tm.getKind() == TypeKind.ERROR) {
1712 name = te.getSimpleName().toString();
1714 name = tm.toString();
1716 return "java.lang.Object".equals(name) ? null : name;
1717 } catch (Exception ex) {
1721 AnnotationMirror found = null;
1722 for (AnnotationMirror am : e.getAnnotationMirrors()) {
1723 if (am.getAnnotationType().toString().equals(OnReceive.class.getName())) {
1727 if (found == null) {
1731 for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : found.getElementValues().entrySet()) {
1732 ExecutableElement ee = entry.getKey();
1733 AnnotationValue av = entry.getValue();
1734 if (ee.getSimpleName().contentEquals("data")) {
1735 List<? extends Object> values = getAnnoValues(processingEnv, e, found);
1736 for (Object v : values) {
1737 String sv = v.toString();
1738 if (sv.startsWith("data = ") && sv.endsWith(".class")) {
1739 return sv.substring(7, sv.length() - 6);
1748 static List<? extends Object> getAnnoValues(ProcessingEnvironment pe, Element e, AnnotationMirror am) {
1750 Class<?> trees = Class.forName("com.sun.tools.javac.api.JavacTrees");
1751 Method m = trees.getMethod("instance", ProcessingEnvironment.class);
1752 Object instance = m.invoke(null, pe);
1753 m = instance.getClass().getMethod("getPath", Element.class, AnnotationMirror.class);
1754 Object path = m.invoke(instance, e, am);
1755 m = path.getClass().getMethod("getLeaf");
1756 Object leaf = m.invoke(path);
1757 m = leaf.getClass().getMethod("getArguments");
1758 return (List) m.invoke(leaf);
1759 } catch (Exception ex) {
1760 return Collections.emptyList();
1764 private static class Prprt {
1765 private final Element e;
1766 private final AnnotationMirror tm;
1767 private final Property p;
1769 public Prprt(Element e, AnnotationMirror tm, Property p) {
1783 String typeName(ProcessingEnvironment env) {
1784 RuntimeException ex;
1786 return p.type().getName();
1787 } catch (IncompleteAnnotationException e) {
1789 } catch (AnnotationTypeMismatchException e) {
1792 for (Object v : getAnnoValues(env, e, tm)) {
1793 String s = v.toString().replace(" ", "");
1794 if (s.startsWith("type=") && s.endsWith(".class")) {
1795 return s.substring(5, s.length() - 6);
1802 static Prprt[] wrap(ProcessingEnvironment pe, Element e, Property[] arr) {
1803 if (arr.length == 0) {
1804 return new Prprt[0];
1807 if (e.getKind() != ElementKind.CLASS) {
1808 throw new IllegalStateException("" + e.getKind());
1810 TypeElement te = (TypeElement)e;
1811 List<? extends AnnotationValue> val = null;
1812 for (AnnotationMirror an : te.getAnnotationMirrors()) {
1813 for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : an.getElementValues().entrySet()) {
1814 if (entry.getKey().getSimpleName().contentEquals("properties")) {
1815 val = (List)entry.getValue().getValue();
1820 if (val == null || val.size() != arr.length) {
1821 pe.getMessager().printMessage(Diagnostic.Kind.ERROR, "" + val, e);
1822 return new Prprt[0];
1824 Prprt[] ret = new Prprt[arr.length];
1825 BIG: for (int i = 0; i < ret.length; i++) {
1826 AnnotationMirror am = (AnnotationMirror)val.get(i).getValue();
1827 ret[i] = new Prprt(e, am, arr[i]);
1834 private static final class GetSet {
1836 final String getter;
1837 final String setter;
1839 final boolean readOnly;
1840 GetSet(String name, String getter, String setter, String type, boolean readOnly) {
1842 this.getter = getter;
1843 this.setter = setter;
1845 this.readOnly = readOnly;
1850 public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
1851 final Level l = Level.FINE;
1852 LOG.log(l, " element: {0}", element);
1853 LOG.log(l, " annotation: {0}", annotation);
1854 LOG.log(l, " member: {0}", member);
1855 LOG.log(l, " userText: {0}", userText);
1856 LOG.log(l, "str: {0}", annotation.getAnnotationType().toString());
1857 if (annotation.getAnnotationType().toString().equals(OnReceive.class.getName())) {
1858 if (member.getSimpleName().contentEquals("method")) {
1859 return Arrays.asList(
1865 methodOf("WebSocket")
1870 return super.getCompletions(element, annotation, member, userText);
1873 private static final Completion methodOf(String method) {
1874 ResourceBundle rb = ResourceBundle.getBundle("org.netbeans.html.json.impl.Bundle");
1875 return Completions.of('"' + method + '"', rb.getString("MSG_Completion_" + method));
1878 private boolean findOnError(ExecutableElement errElem, TypeElement te, String name, String className) {
1881 for (Element e : te.getEnclosedElements()) {
1882 if (e.getKind() != ElementKind.METHOD) {
1885 if (!e.getSimpleName().contentEquals(name)) {
1888 if (!e.getModifiers().contains(Modifier.STATIC)) {
1889 errElem = (ExecutableElement) e;
1890 err = "Would have to be static";
1893 ExecutableElement ee = (ExecutableElement) e;
1894 TypeMirror excType = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
1895 final List<? extends VariableElement> params = ee.getParameters();
1896 boolean error = false;
1897 if (params.size() != 2) {
1900 String firstType = params.get(0).asType().toString();
1901 int lastDot = firstType.lastIndexOf('.');
1902 if (lastDot != -1) {
1903 firstType = firstType.substring(lastDot + 1);
1905 if (!firstType.equals(className)) {
1908 if (!processingEnv.getTypeUtils().isAssignable(excType, params.get(1).asType())) {
1913 errElem = (ExecutableElement) e;
1914 err = "Error method first argument needs to be " + className + " and second Exception";
1920 err = "Cannot find " + name + "(" + className + ", Exception) method in this class";
1922 error(err, errElem);