javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 09 Apr 2013 10:06:19 +0200
branchmodel
changeset 960 4887e22cb810
parent 956 7fc6b7e9c982
child 961 3cdaee10e72b
permissions -rw-r--r--
Support for enums
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. Look for COPYING file in the top folder.
    16  * If not, see http://opensource.org/licenses/GPL-2.0.
    17  */
    18 package org.apidesign.bck2brwsr.htmlpage;
    19 
    20 import java.io.IOException;
    21 import java.io.InputStream;
    22 import java.io.OutputStreamWriter;
    23 import java.io.StringWriter;
    24 import java.io.Writer;
    25 import java.util.ArrayList;
    26 import java.util.Collection;
    27 import java.util.Collections;
    28 import java.util.HashMap;
    29 import java.util.HashSet;
    30 import java.util.LinkedHashSet;
    31 import java.util.List;
    32 import java.util.Map;
    33 import java.util.Set;
    34 import java.util.WeakHashMap;
    35 import javax.annotation.processing.AbstractProcessor;
    36 import javax.annotation.processing.Completion;
    37 import javax.annotation.processing.Completions;
    38 import javax.annotation.processing.Messager;
    39 import javax.annotation.processing.Processor;
    40 import javax.annotation.processing.RoundEnvironment;
    41 import javax.annotation.processing.SupportedAnnotationTypes;
    42 import javax.lang.model.element.AnnotationMirror;
    43 import javax.lang.model.element.Element;
    44 import javax.lang.model.element.ElementKind;
    45 import javax.lang.model.element.ExecutableElement;
    46 import javax.lang.model.element.Modifier;
    47 import javax.lang.model.element.PackageElement;
    48 import javax.lang.model.element.TypeElement;
    49 import javax.lang.model.element.VariableElement;
    50 import javax.lang.model.type.ArrayType;
    51 import javax.lang.model.type.MirroredTypeException;
    52 import javax.lang.model.type.TypeKind;
    53 import javax.lang.model.type.TypeMirror;
    54 import javax.lang.model.util.Elements;
    55 import javax.lang.model.util.Types;
    56 import javax.tools.Diagnostic;
    57 import javax.tools.FileObject;
    58 import javax.tools.StandardLocation;
    59 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
    60 import org.apidesign.bck2brwsr.htmlpage.api.Model;
    61 import org.apidesign.bck2brwsr.htmlpage.api.On;
    62 import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
    63 import org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange;
    64 import org.apidesign.bck2brwsr.htmlpage.api.OnReceive;
    65 import org.apidesign.bck2brwsr.htmlpage.api.Page;
    66 import org.apidesign.bck2brwsr.htmlpage.api.Property;
    67 import org.openide.util.lookup.ServiceProvider;
    68 
    69 /** Annotation processor to process an XHTML page and generate appropriate 
    70  * "id" file.
    71  *
    72  * @author Jaroslav Tulach <jtulach@netbeans.org>
    73  */
    74 @ServiceProvider(service=Processor.class)
    75 @SupportedAnnotationTypes({
    76     "org.apidesign.bck2brwsr.htmlpage.api.Model",
    77     "org.apidesign.bck2brwsr.htmlpage.api.Page",
    78     "org.apidesign.bck2brwsr.htmlpage.api.OnFunction",
    79     "org.apidesign.bck2brwsr.htmlpage.api.OnReceive",
    80     "org.apidesign.bck2brwsr.htmlpage.api.OnPropertyChange",
    81     "org.apidesign.bck2brwsr.htmlpage.api.On"
    82 })
    83 public final class PageProcessor extends AbstractProcessor {
    84     private final Map<Element,String> models = new WeakHashMap<>();
    85     @Override
    86     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    87         boolean ok = true;
    88         for (Element e : roundEnv.getElementsAnnotatedWith(Model.class)) {
    89             if (!processModel(e)) {
    90                 ok = false;
    91             }
    92         }
    93         for (Element e : roundEnv.getElementsAnnotatedWith(Page.class)) {
    94             if (!processPage(e)) {
    95                 ok = false;
    96             }
    97         }
    98         if (roundEnv.processingOver()) {
    99             models.clear();
   100         }
   101         return ok;
   102     }
   103 
   104     private InputStream openStream(String pkg, String name) throws IOException {
   105         try {
   106             FileObject fo = processingEnv.getFiler().getResource(
   107                 StandardLocation.SOURCE_PATH, pkg, name);
   108             return fo.openInputStream();
   109         } catch (IOException ex) {
   110             return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, pkg, name).openInputStream();
   111         }
   112     }
   113 
   114     private  Messager err() {
   115         return processingEnv.getMessager();
   116     }
   117     
   118     private boolean processModel(Element e) {
   119         boolean ok = true;
   120         Model m = e.getAnnotation(Model.class);
   121         if (m == null) {
   122             return true;
   123         }
   124         String pkg = findPkgName(e);
   125         Writer w;
   126         String className = m.className();
   127         models.put(e, className);
   128         try {
   129             StringWriter body = new StringWriter();
   130             List<String> propsGetSet = new ArrayList<>();
   131             List<String> functions = new ArrayList<>();
   132             Map<String, Collection<String>> propsDeps = new HashMap<>();
   133             Map<String, Collection<String>> functionDeps = new HashMap<>();
   134             if (!generateComputedProperties(body, m.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
   135                 ok = false;
   136             }
   137             if (!generateOnChange(e, propsDeps, m.properties(), className, functionDeps)) {
   138                 ok = false;
   139             }
   140             if (!generateProperties(e, body, m.properties(), propsGetSet, propsDeps, functionDeps)) {
   141                 ok = false;
   142             }
   143             if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
   144                 ok = false;
   145             }
   146             FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
   147             w = new OutputStreamWriter(java.openOutputStream());
   148             try {
   149                 w.append("package " + pkg + ";\n");
   150                 w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
   151                 w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
   152                 w.append("import org.apidesign.bck2brwsr.core.JavaScriptOnly;\n");
   153                 w.append("final class ").append(className).append(" implements Cloneable {\n");
   154                 w.append("  private boolean locked;\n");
   155                 w.append("  private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
   156                 w.append(body.toString());
   157                 w.append("  private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
   158                 w.append("  public ").append(className).append("() {\n");
   159                 w.append("    intKnckt();\n");
   160                 w.append("  };\n");
   161                 w.append("  private void intKnckt() {\n");
   162                 w.append("    ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(this, ");
   163                 writeStringArray(propsGetSet, w);
   164                 w.append(", ");
   165                 writeStringArray(functions, w);
   166                 w.append("    );\n");
   167                 w.append("  };\n");
   168                 w.append("  ").append(className).append("(Object json) {\n");
   169                 int values = 0;
   170                 for (int i = 0; i < propsGetSet.size(); i += 4) {
   171                     Property p = findProperty(m.properties(), propsGetSet.get(i));
   172                     if (p == null) {
   173                         continue;
   174                     }
   175                     values++;
   176                 }
   177                 w.append("    Object[] ret = new Object[" + values + "];\n");
   178                 w.append("    org.apidesign.bck2brwsr.htmlpage.ConvertTypes.extractJSON(json, new String[] {\n");
   179                 for (int i = 0; i < propsGetSet.size(); i += 4) {
   180                     Property p = findProperty(m.properties(), propsGetSet.get(i));
   181                     if (p == null) {
   182                         continue;
   183                     }
   184                     w.append("      \"").append(propsGetSet.get(i)).append("\",\n");
   185                 }
   186                 w.append("    }, ret);\n");
   187                 for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i += 4) {
   188                     final String pn = propsGetSet.get(i);
   189                     Property p = findProperty(m.properties(), pn);
   190                     if (p == null) {
   191                         continue;
   192                     }
   193                     boolean[] isModel = { false };
   194                     boolean[] isEnum = { false };
   195                     String type = checkType(m.properties()[prop++], isModel, isEnum);
   196                     if (p.array()) {
   197                         w.append("if (ret[" + cnt + "] instanceof Object[]) {\n");
   198                         w.append("  for (Object e : ((Object[])ret[" + cnt + "])) {\n");
   199                         if (isModel[0]) {
   200                             w.append("    this.prop_").append(pn).append(".add(new ");
   201                             w.append(type).append("(e));\n");
   202                         } else if (isEnum[0]) {
   203                             w.append("    this.prop_").append(pn);
   204                             w.append(".add(");
   205                             w.append(type).append(".valueOf((String)e));\n");
   206                         } else {
   207                             if (isPrimitive(type)) {
   208                                 w.append("    this.prop_").append(pn).append(".add(((Number)e).");
   209                                 w.append(type).append("Value());\n");
   210                             } else {
   211                                 w.append("    this.prop_").append(pn).append(".add((");
   212                                 w.append(type).append(")e);\n");
   213                             }
   214                         }
   215                         w.append("  }\n");
   216                         w.append("}\n");
   217                     } else {
   218                         if (isEnum[0]) {
   219                             w.append("    this.prop_").append(pn);
   220                             w.append(" = ");
   221                             w.append(type).append(".valueOf((String)ret[" + cnt + "]);\n");
   222                         } else if (isPrimitive(type)) {
   223                             w.append("    this.prop_").append(pn);
   224                             w.append(" = ((Number)").append("ret[" + cnt + "]).");
   225                             w.append(type).append("Value();\n");
   226                         } else {
   227                             w.append("    this.prop_").append(pn);
   228                             w.append(" = (").append(type).append(')');
   229                             w.append("ret[" + cnt + "];\n");
   230                         }
   231                     }
   232                     cnt++;
   233                 }
   234                 w.append("    intKnckt();\n");
   235                 w.append("  };\n");
   236                 writeToString(m.properties(), w);
   237                 writeClone(className, m.properties(), w);
   238                 w.append("}\n");
   239             } finally {
   240                 w.close();
   241             }
   242         } catch (IOException ex) {
   243             err().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
   244             return false;
   245         }
   246         return ok;
   247     }
   248     
   249     private boolean processPage(Element e) {
   250         boolean ok = true;
   251         Page p = e.getAnnotation(Page.class);
   252         if (p == null) {
   253             return true;
   254         }
   255         String pkg = findPkgName(e);
   256 
   257         ProcessPage pp;
   258         try (InputStream is = openStream(pkg, p.xhtml())) {
   259             pp = ProcessPage.readPage(is);
   260             is.close();
   261         } catch (IOException iOException) {
   262             err().printMessage(Diagnostic.Kind.ERROR, "Can't read " + p.xhtml() + " as " + iOException.getMessage(), e);
   263             ok = false;
   264             pp = null;
   265         }
   266         Writer w;
   267         String className = p.className();
   268         if (className.isEmpty()) {
   269             int indx = p.xhtml().indexOf('.');
   270             className = p.xhtml().substring(0, indx);
   271         }
   272         try {
   273             StringWriter body = new StringWriter();
   274             List<String> propsGetSet = new ArrayList<>();
   275             List<String> functions = new ArrayList<>();
   276             Map<String, Collection<String>> propsDeps = new HashMap<>();
   277             Map<String, Collection<String>> functionDeps = new HashMap<>();
   278             if (!generateComputedProperties(body, p.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
   279                 ok = false;
   280             }
   281             if (!generateOnChange(e, propsDeps, p.properties(), className, functionDeps)) {
   282                 ok = false;
   283             }
   284             if (!generateProperties(e, body, p.properties(), propsGetSet, propsDeps, functionDeps)) {
   285                 ok = false;
   286             }
   287             if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
   288                 ok = false;
   289             }
   290             if (!generateReceive(e, body, className, e.getEnclosedElements(), functions)) {
   291                 ok = false;
   292             }
   293             
   294             FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
   295             w = new OutputStreamWriter(java.openOutputStream());
   296             try {
   297                 w.append("package " + pkg + ";\n");
   298                 w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
   299                 w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
   300                 w.append("final class ").append(className).append(" {\n");
   301                 w.append("  private boolean locked;\n");
   302                 if (!initializeOnClick(className, (TypeElement) e, w, pp)) {
   303                     ok = false;
   304                 } else {
   305                     if (pp != null) for (String id : pp.ids()) {
   306                         String tag = pp.tagNameForId(id);
   307                         String type = type(tag);
   308                         w.append("  ").append("public final ").
   309                             append(type).append(' ').append(cnstnt(id)).append(" = new ").
   310                             append(type).append("(\"").append(id).append("\");\n");
   311                     }
   312                 }
   313                 w.append("  private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
   314                 w.append(body.toString());
   315                 if (!propsGetSet.isEmpty()) {
   316                     w.write("public " + className + " applyBindings() {\n");
   317                     w.write("  ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(");
   318                     w.write(className + ".class, this, ");
   319                     writeStringArray(propsGetSet, w);
   320                     w.append(", ");
   321                     writeStringArray(functions, w);
   322                     w.write(");\n  return this;\n}\n");
   323 
   324                     w.write("public void triggerEvent(Element e, OnEvent ev) {\n");
   325                     w.write("  org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n");
   326                     w.write("}\n");
   327                 }
   328                 w.append("}\n");
   329             } finally {
   330                 w.close();
   331             }
   332         } catch (IOException ex) {
   333             err().printMessage(Diagnostic.Kind.ERROR, "Can't create " + className + ".java", e);
   334             return false;
   335         }
   336         return ok;
   337     }
   338 
   339     private static String type(String tag) {
   340         if (tag.equals("title")) {
   341             return "Title";
   342         }
   343         if (tag.equals("button")) {
   344             return "Button";
   345         }
   346         if (tag.equals("input")) {
   347             return "Input";
   348         }
   349         if (tag.equals("canvas")) {
   350             return "Canvas";
   351         }
   352         if (tag.equals("img")) {
   353             return "Image";
   354         }
   355         return "Element";
   356     }
   357 
   358     private static String cnstnt(String id) {
   359         return id.replace('.', '_').replace('-', '_');
   360     }
   361 
   362     private boolean initializeOnClick(
   363         String className, TypeElement type, Writer w, ProcessPage pp
   364     ) throws IOException {
   365         boolean ok = true;
   366         TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
   367         { //for (Element clazz : pe.getEnclosedElements()) {
   368           //  if (clazz.getKind() != ElementKind.CLASS) {
   369             //    continue;
   370            // }
   371             w.append("  public ").append(className).append("() {\n");
   372             StringBuilder dispatch = new StringBuilder();
   373             int dispatchCnt = 0;
   374             for (Element method : type.getEnclosedElements()) {
   375                 On oc = method.getAnnotation(On.class);
   376                 if (oc != null) {
   377                     for (String id : oc.id()) {
   378                         if (pp == null) {
   379                             err().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " not found in HTML page.");
   380                             ok = false;
   381                             continue;
   382                         }
   383                         if (pp.tagNameForId(id) == null) {
   384                             err().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method);
   385                             ok = false;
   386                             continue;
   387                         }
   388                         ExecutableElement ee = (ExecutableElement)method;
   389                         CharSequence params = wrapParams(ee, id, className, "ev", null);
   390                         if (!ee.getModifiers().contains(Modifier.STATIC)) {
   391                             err().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee);
   392                             ok = false;
   393                             continue;
   394                         }
   395                         if (ee.getModifiers().contains(Modifier.PRIVATE)) {
   396                             err().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee);
   397                             ok = false;
   398                             continue;
   399                         }
   400                         w.append("  OnEvent." + oc.event()).append(".of(").append(cnstnt(id)).
   401                             append(").perform(new OnDispatch(" + dispatchCnt + "));\n");
   402 
   403                         dispatch.
   404                             append("      case ").append(dispatchCnt).append(": ").
   405                             append(type.getSimpleName().toString()).
   406                             append('.').append(ee.getSimpleName()).append("(").
   407                             append(params).
   408                             append("); break;\n");
   409                         
   410                         dispatchCnt++;
   411                     }
   412                 }
   413             }
   414             w.append("  }\n");
   415             if (dispatchCnt > 0) {
   416                 w.append("class OnDispatch implements OnHandler {\n");
   417                 w.append("  private final int dispatch;\n");
   418                 w.append("  OnDispatch(int d) { dispatch = d; }\n");
   419                 w.append("  public void onEvent(Object ev) {\n");
   420                 w.append("    switch (dispatch) {\n");
   421                 w.append(dispatch);
   422                 w.append("    }\n");
   423                 w.append("  }\n");
   424                 w.append("}\n");
   425             }
   426             
   427 
   428         }
   429         return ok;
   430     }
   431 
   432     @Override
   433     public Iterable<? extends Completion> getCompletions(
   434         Element element, AnnotationMirror annotation, 
   435         ExecutableElement member, String userText
   436     ) {
   437         if (!userText.startsWith("\"")) {
   438             return Collections.emptyList();
   439         }
   440         
   441         Element cls = findClass(element);
   442         Page p = cls.getAnnotation(Page.class);
   443         String pkg = findPkgName(cls);
   444         ProcessPage pp;
   445         try {
   446             InputStream is = openStream(pkg, p.xhtml());
   447             pp = ProcessPage.readPage(is);
   448             is.close();
   449         } catch (IOException iOException) {
   450             return Collections.emptyList();
   451         }
   452         
   453         List<Completion> cc = new ArrayList<>();
   454         userText = userText.substring(1);
   455         for (String id : pp.ids()) {
   456             if (id.startsWith(userText)) {
   457                 cc.add(Completions.of("\"" + id + "\"", id));
   458             }
   459         }
   460         return cc;
   461     }
   462     
   463     private static Element findClass(Element e) {
   464         if (e == null) {
   465             return null;
   466         }
   467         Page p = e.getAnnotation(Page.class);
   468         if (p != null) {
   469             return e;
   470         }
   471         return e.getEnclosingElement();
   472     }
   473 
   474     private boolean generateProperties(
   475         Element where,
   476         Writer w, Property[] properties,
   477         Collection<String> props, 
   478         Map<String,Collection<String>> deps,
   479         Map<String,Collection<String>> functionDeps
   480     ) throws IOException {
   481         boolean ok = true;
   482         for (Property p : properties) {
   483             final String tn;
   484             tn = typeName(where, p);
   485             String[] gs = toGetSet(p.name(), tn, p.array());
   486 
   487             if (p.array()) {
   488                 w.write("private KOList<" + tn + "> prop_" + p.name() + " = new KOList<" + tn + ">(\""
   489                     + p.name() + "\"");
   490                 Collection<String> dependants = deps.get(p.name());
   491                 if (dependants != null) {
   492                     for (String depProp : dependants) {
   493                         w.write(", ");
   494                         w.write('\"');
   495                         w.write(depProp);
   496                         w.write('\"');
   497                     }
   498                 }
   499                 w.write(")");
   500                 
   501                 dependants = functionDeps.get(p.name());
   502                 if (dependants != null) {
   503                     w.write(".onChange(new Runnable() { public void run() {\n");
   504                     for (String call : dependants) {
   505                         w.append(call);
   506                     }
   507                     w.write("}})");
   508                 }
   509                 w.write(";\n");
   510                 
   511                 w.write("public java.util.List<" + tn + "> " + gs[0] + "() {\n");
   512                 w.write("  if (locked) throw new IllegalStateException();\n");
   513                 w.write("  prop_" + p.name() + ".assign(ko);\n");
   514                 w.write("  return prop_" + p.name() + ";\n");
   515                 w.write("}\n");
   516             } else {
   517                 w.write("private " + tn + " prop_" + p.name() + ";\n");
   518                 w.write("public " + tn + " " + gs[0] + "() {\n");
   519                 w.write("  if (locked) throw new IllegalStateException();\n");
   520                 w.write("  return prop_" + p.name() + ";\n");
   521                 w.write("}\n");
   522                 w.write("public void " + gs[1] + "(" + tn + " v) {\n");
   523                 w.write("  if (locked) throw new IllegalStateException();\n");
   524                 w.write("  prop_" + p.name() + " = v;\n");
   525                 w.write("  if (ko != null) {\n");
   526                 w.write("    ko.valueHasMutated(\"" + p.name() + "\");\n");
   527                 Collection<String> dependants = deps.get(p.name());
   528                 if (dependants != null) {
   529                     for (String depProp : dependants) {
   530                         w.write("    ko.valueHasMutated(\"" + depProp + "\");\n");
   531                     }
   532                 }
   533                 w.write("  }\n");
   534                 dependants = functionDeps.get(p.name());
   535                 if (dependants != null) {
   536                     for (String call : dependants) {
   537                         w.append(call);
   538                     }
   539                 }
   540                 w.write("}\n");
   541             }
   542             
   543             props.add(p.name());
   544             props.add(gs[2]);
   545             props.add(gs[3]);
   546             props.add(gs[0]);
   547         }
   548         return ok;
   549     }
   550 
   551     private boolean generateComputedProperties(
   552         Writer w, Property[] fixedProps,
   553         Collection<? extends Element> arr, Collection<String> props,
   554         Map<String,Collection<String>> deps
   555     ) throws IOException {
   556         boolean ok = true;
   557         for (Element e : arr) {
   558             if (e.getKind() != ElementKind.METHOD) {
   559                 continue;
   560             }
   561             if (e.getAnnotation(ComputedProperty.class) == null) {
   562                 continue;
   563             }
   564             ExecutableElement ee = (ExecutableElement)e;
   565             final TypeMirror rt = ee.getReturnType();
   566             final Types tu = processingEnv.getTypeUtils();
   567             TypeMirror ert = tu.erasure(rt);
   568             String tn = fqn(ert, ee);
   569             boolean array = false;
   570             if (tn.equals("java.util.List")) {
   571                 array = true;
   572             }
   573             
   574             final String sn = ee.getSimpleName().toString();
   575             String[] gs = toGetSet(sn, tn, array);
   576             
   577             w.write("public " + tn + " " + gs[0] + "() {\n");
   578             w.write("  if (locked) throw new IllegalStateException();\n");
   579             int arg = 0;
   580             for (VariableElement pe : ee.getParameters()) {
   581                 final String dn = pe.getSimpleName().toString();
   582                 
   583                 if (!verifyPropName(pe, dn, fixedProps)) {
   584                     ok = false;
   585                 }
   586                 
   587                 final String dt = fqn(pe.asType(), ee);
   588                 String[] call = toGetSet(dn, dt, false);
   589                 w.write("  " + dt + " arg" + (++arg) + " = ");
   590                 w.write(call[0] + "();\n");
   591                 
   592                 Collection<String> depends = deps.get(dn);
   593                 if (depends == null) {
   594                     depends = new LinkedHashSet<>();
   595                     deps.put(dn, depends);
   596                 }
   597                 depends.add(sn);
   598             }
   599             w.write("  try {\n");
   600             w.write("    locked = true;\n");
   601             w.write("    return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
   602             String sep = "";
   603             for (int i = 1; i <= arg; i++) {
   604                 w.write(sep);
   605                 w.write("arg" + i);
   606                 sep = ", ";
   607             }
   608             w.write(");\n");
   609             w.write("  } finally {\n");
   610             w.write("    locked = false;\n");
   611             w.write("  }\n");
   612             w.write("}\n");
   613 
   614             props.add(e.getSimpleName().toString());
   615             props.add(gs[2]);
   616             props.add(null);
   617             props.add(gs[0]);
   618         }
   619         
   620         return ok;
   621     }
   622 
   623     private static String[] toGetSet(String name, String type, boolean array) {
   624         String n = Character.toUpperCase(name.charAt(0)) + name.substring(1);
   625         String bck2brwsrType = "L" + type.replace('.', '_') + "_2";
   626         if ("int".equals(type)) {
   627             bck2brwsrType = "I";
   628         }
   629         if ("double".equals(type)) {
   630             bck2brwsrType = "D";
   631         }
   632         String pref = "get";
   633         if ("boolean".equals(type)) {
   634             pref = "is";
   635             bck2brwsrType = "Z";
   636         }
   637         final String nu = n.replace('.', '_');
   638         if (array) {
   639             return new String[] { 
   640                 "get" + n,
   641                 null,
   642                 "get" + nu + "__Ljava_util_List_2",
   643                 null
   644             };
   645         }
   646         return new String[]{
   647             pref + n, 
   648             "set" + n, 
   649             pref + nu + "__" + bck2brwsrType,
   650             "set" + nu + "__V" + bck2brwsrType
   651         };
   652     }
   653 
   654     private String typeName(Element where, Property p) {
   655         String ret;
   656         boolean[] isModel = { false };
   657         boolean[] isEnum = { false };
   658         ret = checkType(p, isModel, isEnum);
   659         if (p.array()) {
   660             String bt = findBoxedType(ret);
   661             if (bt != null) {
   662                 return bt;
   663             }
   664         }
   665         if (!isModel[0] && !"java.lang.String".equals(ret) && !isEnum[0]) {
   666             String bt = findBoxedType(ret);
   667             if (bt == null) {
   668                 err().printMessage(
   669                     Diagnostic.Kind.ERROR, 
   670                     "Only primitive types supported in the mapping. Not " + ret,
   671                     where
   672                 );
   673             }
   674         }
   675         return ret;
   676     }
   677     
   678     private static String findBoxedType(String ret) {
   679         if (ret.equals("boolean")) {
   680             return Boolean.class.getName();
   681         }
   682         if (ret.equals("byte")) {
   683             return Byte.class.getName();
   684         }
   685         if (ret.equals("short")) {
   686             return Short.class.getName();
   687         }
   688         if (ret.equals("char")) {
   689             return Character.class.getName();
   690         }
   691         if (ret.equals("int")) {
   692             return Integer.class.getName();
   693         }
   694         if (ret.equals("long")) {
   695             return Long.class.getName();
   696         }
   697         if (ret.equals("float")) {
   698             return Float.class.getName();
   699         }
   700         if (ret.equals("double")) {
   701             return Double.class.getName();
   702         }
   703         return null;
   704     }
   705 
   706     private boolean verifyPropName(Element e, String propName, Property[] existingProps) {
   707         StringBuilder sb = new StringBuilder();
   708         String sep = "";
   709         for (Property property : existingProps) {
   710             if (property.name().equals(propName)) {
   711                 return true;
   712             }
   713             sb.append(sep);
   714             sb.append('"');
   715             sb.append(property.name());
   716             sb.append('"');
   717             sep = ", ";
   718         }
   719         err().printMessage(Diagnostic.Kind.ERROR,
   720             propName + " is not one of known properties: " + sb
   721             , e
   722         );
   723         return false;
   724     }
   725 
   726     private static String findPkgName(Element e) {
   727         for (;;) {
   728             if (e.getKind() == ElementKind.PACKAGE) {
   729                 return ((PackageElement)e).getQualifiedName().toString();
   730             }
   731             e = e.getEnclosingElement();
   732         }
   733     }
   734 
   735     private boolean generateFunctions(
   736         Element clazz, StringWriter body, String className, 
   737         List<? extends Element> enclosedElements, List<String> functions
   738     ) {
   739         for (Element m : enclosedElements) {
   740             if (m.getKind() != ElementKind.METHOD) {
   741                 continue;
   742             }
   743             ExecutableElement e = (ExecutableElement)m;
   744             OnFunction onF = e.getAnnotation(OnFunction.class);
   745             if (onF == null) {
   746                 continue;
   747             }
   748             if (!e.getModifiers().contains(Modifier.STATIC)) {
   749                 err().printMessage(
   750                     Diagnostic.Kind.ERROR, "@OnFunction method needs to be static", e
   751                 );
   752                 return false;
   753             }
   754             if (e.getModifiers().contains(Modifier.PRIVATE)) {
   755                 err().printMessage(
   756                     Diagnostic.Kind.ERROR, "@OnFunction method cannot be private", e
   757                 );
   758                 return false;
   759             }
   760             if (e.getReturnType().getKind() != TypeKind.VOID) {
   761                 err().printMessage(
   762                     Diagnostic.Kind.ERROR, "@OnFunction method should return void", e
   763                 );
   764                 return false;
   765             }
   766             String n = e.getSimpleName().toString();
   767             body.append("private void ").append(n).append("(Object data, Object ev) {\n");
   768             body.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
   769             body.append(wrapParams(e, null, className, "ev", "data"));
   770             body.append(");\n");
   771             body.append("}\n");
   772             
   773             functions.add(n);
   774             functions.add(n + "__VLjava_lang_Object_2Ljava_lang_Object_2");
   775         }
   776         return true;
   777     }
   778 
   779     private boolean generateOnChange(Element clazz, Map<String,Collection<String>> propDeps,
   780         Property[] properties, String className, 
   781         Map<String, Collection<String>> functionDeps
   782     ) {
   783         for (Element m : clazz.getEnclosedElements()) {
   784             if (m.getKind() != ElementKind.METHOD) {
   785                 continue;
   786             }
   787             ExecutableElement e = (ExecutableElement) m;
   788             OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class);
   789             if (onPC == null) {
   790                 continue;
   791             }
   792             for (String pn : onPC.value()) {
   793                 if (findProperty(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
   794                     err().printMessage(Diagnostic.Kind.ERROR, "No property named '" + pn + "' in the model");
   795                     return false;
   796                 }
   797             }
   798             if (!e.getModifiers().contains(Modifier.STATIC)) {
   799                 err().printMessage(
   800                     Diagnostic.Kind.ERROR, "@OnPropertyChange method needs to be static", e);
   801                 return false;
   802             }
   803             if (e.getModifiers().contains(Modifier.PRIVATE)) {
   804                 err().printMessage(
   805                     Diagnostic.Kind.ERROR, "@OnPropertyChange method cannot be private", e);
   806                 return false;
   807             }
   808             if (e.getReturnType().getKind() != TypeKind.VOID) {
   809                 err().printMessage(
   810                     Diagnostic.Kind.ERROR, "@OnPropertyChange method should return void", e);
   811                 return false;
   812             }
   813             String n = e.getSimpleName().toString();
   814             
   815             
   816             for (String pn : onPC.value()) {
   817                 StringBuilder call = new StringBuilder();
   818                 call.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
   819                 call.append(wrapPropName(e, className, "name", pn));
   820                 call.append(");\n");
   821                 
   822                 Collection<String> change = functionDeps.get(pn);
   823                 if (change == null) {
   824                     change = new ArrayList<>();
   825                     functionDeps.put(pn, change);
   826                 }
   827                 change.add(call.toString());
   828                 for (String dpn : findDerivedFrom(propDeps, pn)) {
   829                     change = functionDeps.get(dpn);
   830                     if (change == null) {
   831                         change = new ArrayList<>();
   832                         functionDeps.put(dpn, change);
   833                     }
   834                     change.add(call.toString());
   835                 }
   836             }
   837         }
   838         return true;
   839     }
   840     
   841     private boolean generateReceive(
   842         Element clazz, StringWriter body, String className, 
   843         List<? extends Element> enclosedElements, List<String> functions
   844     ) {
   845         for (Element m : enclosedElements) {
   846             if (m.getKind() != ElementKind.METHOD) {
   847                 continue;
   848             }
   849             ExecutableElement e = (ExecutableElement)m;
   850             OnReceive onR = e.getAnnotation(OnReceive.class);
   851             if (onR == null) {
   852                 continue;
   853             }
   854             if (!e.getModifiers().contains(Modifier.STATIC)) {
   855                 err().printMessage(
   856                     Diagnostic.Kind.ERROR, "@OnReceive method needs to be static", e
   857                 );
   858                 return false;
   859             }
   860             if (e.getModifiers().contains(Modifier.PRIVATE)) {
   861                 err().printMessage(
   862                     Diagnostic.Kind.ERROR, "@OnReceive method cannot be private", e
   863                 );
   864                 return false;
   865             }
   866             if (e.getReturnType().getKind() != TypeKind.VOID) {
   867                 err().printMessage(
   868                     Diagnostic.Kind.ERROR, "@OnReceive method should return void", e
   869                 );
   870                 return false;
   871             }
   872             String modelClass = null;
   873             boolean expectsList = false;
   874             List<String> args = new ArrayList<>();
   875             {
   876                 for (VariableElement ve : e.getParameters()) {
   877                     TypeMirror modelType = null;
   878                     if (ve.asType().toString().equals(className)) {
   879                         args.add(className + ".this");
   880                     } else if (isModel(ve.asType())) {
   881                         modelType = ve.asType();
   882                     } else if (ve.asType().getKind() == TypeKind.ARRAY) {
   883                         modelType = ((ArrayType)ve.asType()).getComponentType();
   884                         expectsList = true;
   885                     }
   886                     if (modelType != null) {
   887                         if (modelClass != null) {
   888                             err().printMessage(Diagnostic.Kind.ERROR, "There can be only one model class among arguments", e);
   889                         } else {
   890                             modelClass = modelType.toString();
   891                             if (expectsList) {
   892                                 args.add("arr");
   893                             } else {
   894                                 args.add("arr[0]");
   895                             }
   896                         }
   897                     }
   898                 }
   899             }
   900             if (modelClass == null) {
   901                 err().printMessage(Diagnostic.Kind.ERROR, "The method needs to have one @Model class as parameter", e);
   902             }
   903             String n = e.getSimpleName().toString();
   904             body.append("public void ").append(n).append("(");
   905             StringBuilder assembleURL = new StringBuilder();
   906             String jsonpVarName = null;
   907             {
   908                 String sep = "";
   909                 boolean skipJSONP = onR.jsonp().isEmpty();
   910                 for (String p : findParamNames(e, onR.url(), assembleURL)) {
   911                     if (!skipJSONP && p.equals(onR.jsonp())) {
   912                         skipJSONP = true;
   913                         jsonpVarName = p;
   914                         continue;
   915                     }
   916                     body.append(sep);
   917                     body.append("String ").append(p);
   918                     sep = ", ";
   919                 }
   920                 if (!skipJSONP) {
   921                     err().printMessage(Diagnostic.Kind.ERROR, 
   922                         "Name of jsonp attribute ('" + onR.jsonp() + 
   923                         "') is not used in url attribute '" + onR.url() + "'"
   924                     );
   925                 }
   926             }
   927             body.append(") {\n");
   928             body.append("  final Object[] result = { null };\n");
   929             body.append(
   930                 "  class ProcessResult implements Runnable {\n" +
   931                 "    @Override\n" +
   932                 "    public void run() {\n" +
   933                 "      Object value = result[0];\n");
   934             body.append(
   935                 "      " + modelClass + "[] arr;\n");
   936             body.append(
   937                 "      if (value instanceof Object[]) {\n" +
   938                 "        Object[] data = ((Object[])value);\n" +
   939                 "        arr = new " + modelClass + "[data.length];\n" +
   940                 "        for (int i = 0; i < data.length; i++) {\n" +
   941                 "          arr[i] = new " + modelClass + "(data[i]);\n" +
   942                 "        }\n" +
   943                 "      } else {\n" +
   944                 "        arr = new " + modelClass + "[1];\n" +
   945                 "        arr[0] = new " + modelClass + "(value);\n" +
   946                 "      }\n"
   947             );
   948             {
   949                 body.append(clazz.getSimpleName()).append(".").append(n).append("(");
   950                 String sep = "";
   951                 for (String arg : args) {
   952                     body.append(sep);
   953                     body.append(arg);
   954                     sep = ", ";
   955                 }
   956                 body.append(");\n");
   957             }
   958             body.append(
   959                 "    }\n" +
   960                 "  }\n"
   961             );
   962             body.append("  ProcessResult pr = new ProcessResult();\n");
   963             if (jsonpVarName != null) {
   964                 body.append("  String ").append(jsonpVarName).
   965                     append(" = org.apidesign.bck2brwsr.htmlpage.ConvertTypes.createJSONP(result, pr);\n");
   966             }
   967             body.append("  org.apidesign.bck2brwsr.htmlpage.ConvertTypes.loadJSON(\n      ");
   968             body.append(assembleURL);
   969             body.append(", result, pr, ").append(jsonpVarName).append("\n  );\n");
   970 //            body.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
   971 //            body.append(wrapParams(e, null, className, "ev", "data"));
   972 //            body.append(");\n");
   973             body.append("}\n");
   974         }
   975         return true;
   976     }
   977 
   978     private CharSequence wrapParams(
   979         ExecutableElement ee, String id, String className, String evName, String dataName
   980     ) {
   981         TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
   982         StringBuilder params = new StringBuilder();
   983         boolean first = true;
   984         for (VariableElement ve : ee.getParameters()) {
   985             if (!first) {
   986                 params.append(", ");
   987             }
   988             first = false;
   989             String toCall = null;
   990             if (ve.asType() == stringType) {
   991                 if (ve.getSimpleName().contentEquals("id")) {
   992                     params.append('"').append(id).append('"');
   993                     continue;
   994                 }
   995                 toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString(";
   996             }
   997             if (ve.asType().getKind() == TypeKind.DOUBLE) {
   998                 toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble(";
   999             }
  1000             if (ve.asType().getKind() == TypeKind.INT) {
  1001                 toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toInt(";
  1002             }
  1003             if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
  1004                 toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toModel(" + ve.asType() + ".class, ";
  1005             }
  1006 
  1007             if (toCall != null) {
  1008                 params.append(toCall);
  1009                 if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
  1010                     params.append(dataName);
  1011                     params.append(", null");
  1012                 } else {
  1013                     if (evName == null) {
  1014                         final StringBuilder sb = new StringBuilder();
  1015                         sb.append("Unexpected string parameter name.");
  1016                         if (dataName != null) {
  1017                             sb.append(" Try \"").append(dataName).append("\"");
  1018                         }
  1019                         err().printMessage(Diagnostic.Kind.ERROR, sb.toString(), ee);
  1020                     }
  1021                     params.append(evName);
  1022                     params.append(", \"");
  1023                     params.append(ve.getSimpleName().toString());
  1024                     params.append("\"");
  1025                 }
  1026                 params.append(")");
  1027                 continue;
  1028             }
  1029             String rn = fqn(ve.asType(), ee);
  1030             int last = rn.lastIndexOf('.');
  1031             if (last >= 0) {
  1032                 rn = rn.substring(last + 1);
  1033             }
  1034             if (rn.equals(className)) {
  1035                 params.append(className).append(".this");
  1036                 continue;
  1037             }
  1038             err().printMessage(Diagnostic.Kind.ERROR, 
  1039                 "@On method can only accept String named 'id' or " + className + " arguments",
  1040                 ee
  1041             );
  1042         }
  1043         return params;
  1044     }
  1045     
  1046     
  1047     private CharSequence wrapPropName(
  1048         ExecutableElement ee, String className, String propName, String propValue
  1049     ) {
  1050         TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
  1051         StringBuilder params = new StringBuilder();
  1052         boolean first = true;
  1053         for (VariableElement ve : ee.getParameters()) {
  1054             if (!first) {
  1055                 params.append(", ");
  1056             }
  1057             first = false;
  1058             if (ve.asType() == stringType) {
  1059                 if (propName != null && ve.getSimpleName().contentEquals(propName)) {
  1060                     params.append('"').append(propValue).append('"');
  1061                 } else {
  1062                     err().printMessage(Diagnostic.Kind.ERROR, "Unexpected string parameter name. Try \"" + propName + "\".");
  1063                 }
  1064                 continue;
  1065             }
  1066             String rn = fqn(ve.asType(), ee);
  1067             int last = rn.lastIndexOf('.');
  1068             if (last >= 0) {
  1069                 rn = rn.substring(last + 1);
  1070             }
  1071             if (rn.equals(className)) {
  1072                 params.append(className).append(".this");
  1073                 continue;
  1074             }
  1075             err().printMessage(Diagnostic.Kind.ERROR,
  1076                 "@OnPropertyChange method can only accept String or " + className + " arguments",
  1077                 ee);
  1078         }
  1079         return params;
  1080     }
  1081     
  1082     private boolean isModel(TypeMirror tm) {
  1083         final Element e = processingEnv.getTypeUtils().asElement(tm);
  1084         if (e == null) {
  1085             return false;
  1086         }
  1087         for (Element ch : e.getEnclosedElements()) {
  1088             if (ch.getKind() == ElementKind.METHOD) {
  1089                 ExecutableElement ee = (ExecutableElement)ch;
  1090                 if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) {
  1091                     return true;
  1092                 }
  1093             }
  1094         }
  1095         return models.values().contains(e.getSimpleName().toString());
  1096     }
  1097 
  1098     private void writeStringArray(List<String> strings, Writer w) throws IOException {
  1099         w.write("new String[] {\n");
  1100         String sep = "";
  1101         for (String n : strings) {
  1102             w.write(sep);
  1103             if (n == null) {
  1104                 w.write("    null");
  1105             } else {
  1106                 w.write("    \"" + n + "\"");
  1107             }
  1108             sep = ",\n";
  1109         }
  1110         w.write("\n  }");
  1111     }
  1112     
  1113     private void writeToString(Property[] props, Writer w) throws IOException {
  1114         w.write("  public String toString() {\n");
  1115         w.write("    StringBuilder sb = new StringBuilder();\n");
  1116         w.write("    sb.append('{');\n");
  1117         String sep = "";
  1118         for (Property p : props) {
  1119             w.write(sep);
  1120             w.append("    sb.append(\"" + p.name() + ": \");\n");
  1121             w.append("    sb.append(org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toJSON(prop_");
  1122             w.append(p.name()).append("));\n");
  1123             sep =    "    sb.append(',');\n";
  1124         }
  1125         w.write("    sb.append('}');\n");
  1126         w.write("    return sb.toString();\n");
  1127         w.write("  }\n");
  1128     }
  1129     private void writeClone(String className, Property[] props, Writer w) throws IOException {
  1130         w.write("  public " + className + " clone() {\n");
  1131         w.write("    " + className + " ret = new " + className + "();\n");
  1132         for (Property p : props) {
  1133             if (!p.array()) {
  1134                 boolean isModel[] = { false };
  1135                 boolean isEnum[] = { false };
  1136                 checkType(p, isModel, isEnum);
  1137                 if (!isModel[0]) {
  1138                     w.write("    ret.prop_" + p.name() + " = prop_" + p.name() + ";\n");
  1139                     continue;
  1140                 }
  1141                 w.write("    ret.prop_" + p.name() + " = prop_" + p.name() + ".clone();\n");
  1142             } else {
  1143                 w.write("    ret.prop_" + p.name() + " = prop_" + p.name() + ".clone();\n");
  1144             }
  1145         }
  1146         
  1147         w.write("    return ret;\n");
  1148         w.write("  }\n");
  1149     }
  1150 
  1151     private String inPckName(Element e) {
  1152         StringBuilder sb = new StringBuilder();
  1153         while (e.getKind() != ElementKind.PACKAGE) {
  1154             if (sb.length() == 0) {
  1155                 sb.append(e.getSimpleName());
  1156             } else {
  1157                 sb.insert(0, '.');
  1158                 sb.insert(0, e.getSimpleName());
  1159             }
  1160             e = e.getEnclosingElement();
  1161         }
  1162         return sb.toString();
  1163     }
  1164 
  1165     private String fqn(TypeMirror pt, Element relative) {
  1166         if (pt.getKind() == TypeKind.ERROR) {
  1167             final Elements eu = processingEnv.getElementUtils();
  1168             PackageElement pckg = eu.getPackageOf(relative);
  1169             return pckg.getQualifiedName() + "." + pt.toString();
  1170         }
  1171         return pt.toString();
  1172     }
  1173 
  1174     private String checkType(Property p, boolean[] isModel, boolean[] isEnum) {
  1175         String ret;
  1176         try {
  1177             ret = p.type().getName();
  1178         } catch (MirroredTypeException ex) {
  1179             TypeMirror tm = processingEnv.getTypeUtils().erasure(ex.getTypeMirror());
  1180             final Element e = processingEnv.getTypeUtils().asElement(tm);
  1181             final Model m = e == null ? null : e.getAnnotation(Model.class);
  1182             if (m != null) {
  1183                 ret = findPkgName(e) + '.' + m.className();
  1184                 isModel[0] = true;
  1185                 models.put(e, m.className());
  1186             } else {
  1187                 ret = tm.toString();
  1188             }
  1189             TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
  1190             enm = processingEnv.getTypeUtils().erasure(enm);
  1191             isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
  1192         }
  1193         return ret;
  1194     }
  1195 
  1196     private Iterable<String> findParamNames(Element e, String url, StringBuilder assembleURL) {
  1197         List<String> params = new ArrayList<>();
  1198 
  1199         for (int pos = 0; ;) {
  1200             int next = url.indexOf('{', pos);
  1201             if (next == -1) {
  1202                 assembleURL.append('"')
  1203                     .append(url.substring(pos))
  1204                     .append('"');
  1205                 return params;
  1206             }
  1207             int close = url.indexOf('}', next);
  1208             if (close == -1) {
  1209                 err().printMessage(Diagnostic.Kind.ERROR, "Unbalanced '{' and '}' in " + url, e);
  1210                 return params;
  1211             }
  1212             final String paramName = url.substring(next + 1, close);
  1213             params.add(paramName);
  1214             assembleURL.append('"')
  1215                 .append(url.substring(pos, next))
  1216                 .append("\" + ").append(paramName).append(" + ");
  1217             pos = close + 1;
  1218         }
  1219     }
  1220 
  1221     private static Property findProperty(Property[] properties, String propName) {
  1222         for (Property p : properties) {
  1223             if (propName.equals(p.name())) {
  1224                 return p;
  1225             }
  1226         }
  1227         return null;
  1228     }
  1229 
  1230     private boolean isPrimitive(String type) {
  1231         return 
  1232             "int".equals(type) ||
  1233             "double".equals(type) ||
  1234             "long".equals(type) ||
  1235             "short".equals(type) ||
  1236             "byte".equals(type) ||
  1237             "float".equals(type);
  1238     }
  1239 
  1240     private static Collection<String> findDerivedFrom(Map<String, Collection<String>> propsDeps, String derivedProp) {
  1241         Set<String> names = new HashSet<>();
  1242         for (Map.Entry<String, Collection<String>> e : propsDeps.entrySet()) {
  1243             if (e.getValue().contains(derivedProp)) {
  1244                 names.add(e.getKey());
  1245             }
  1246         }
  1247         return names;
  1248     }
  1249 }