Initial version of the JSON model APIs. Taken from bck2brwsr rev. be21afc3d48a
authorJaroslav Tulach <jtulach@netbeans.org>
Fri, 19 Apr 2013 10:01:02 +0200
changeset 0be1853f59242
child 1 e6b8e971468b
Initial version of the JSON model APIs. Taken from bck2brwsr rev. be21afc3d48a
.hgignore
COPYING
json/pom.xml
json/src/main/java/net/java/html/json/ComputedProperty.java
json/src/main/java/net/java/html/json/Function.java
json/src/main/java/net/java/html/json/Model.java
json/src/main/java/net/java/html/json/OnPropertyChange.java
json/src/main/java/net/java/html/json/OnReceive.java
json/src/main/java/net/java/html/json/Property.java
json/src/main/java/org/apidesign/html/json/impl/PageProcessor.java
json/src/test/java/net/java/html/json/ModelTest.java
json/src/test/java/net/java/html/json/PersonImpl.java
json/src/test/java/net/java/html/json/Sex.java
pom.xml
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/.hgignore	Fri Apr 19 10:01:02 2013 +0200
     1.3 @@ -0,0 +1,3 @@
     1.4 +.*~
     1.5 +.*\.orig$
     1.6 +.*target/.*
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/COPYING	Fri Apr 19 10:01:02 2013 +0200
     2.3 @@ -0,0 +1,18 @@
     2.4 +HTML via Java(tm) Language Bindings
     2.5 +Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     2.6 +
     2.7 +This program is free software: you can redistribute it and/or modify
     2.8 +it under the terms of the GNU General Public License as published by
     2.9 +the Free Software Foundation, version 2 of the License.
    2.10 +
    2.11 +This program is distributed in the hope that it will be useful,
    2.12 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.13 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.14 +GNU General Public License for more details. apidesign.org 
    2.15 +designates this particular file as subject to the 
    2.16 +"Classpath" exception as provided by apidesign.org 
    2.17 +in the License file that accompanied this code.
    2.18 +
    2.19 +You should have received a copy of the GNU General Public License
    2.20 +along with this program. Look for COPYING file in the top folder.
    2.21 +If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/json/pom.xml	Fri Apr 19 10:01:02 2013 +0200
     3.3 @@ -0,0 +1,29 @@
     3.4 +<?xml version="1.0"?>
     3.5 +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
     3.6 +    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     3.7 +  <modelVersion>4.0.0</modelVersion>
     3.8 +  <parent>
     3.9 +    <groupId>org.apidesign</groupId>
    3.10 +    <artifactId>html</artifactId>
    3.11 +    <version>0.1-SNAPSHOT</version>
    3.12 +  </parent>
    3.13 +  <groupId>org.apidesign.html</groupId>
    3.14 +  <artifactId>net.java.html.json</artifactId>
    3.15 +  <version>0.1-SNAPSHOT</version>
    3.16 +  <description>JSON Model in Java</description>
    3.17 +  <url>http://maven.apache.org</url>
    3.18 +  <properties>
    3.19 +    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    3.20 +  </properties>
    3.21 +  <dependencies>
    3.22 +    <dependency>
    3.23 +      <groupId>org.testng</groupId>
    3.24 +      <artifactId>testng</artifactId>
    3.25 +      <scope>test</scope>
    3.26 +    </dependency>
    3.27 +    <dependency>
    3.28 +      <groupId>org.netbeans.api</groupId>
    3.29 +      <artifactId>org-openide-util-lookup</artifactId>
    3.30 +    </dependency>
    3.31 +  </dependencies>
    3.32 +</project>
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/json/src/main/java/net/java/html/json/ComputedProperty.java	Fri Apr 19 10:01:02 2013 +0200
     4.3 @@ -0,0 +1,41 @@
     4.4 +/**
     4.5 + * HTML via Java(tm) Language Bindings
     4.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4.7 + *
     4.8 + * This program is free software: you can redistribute it and/or modify
     4.9 + * it under the terms of the GNU General Public License as published by
    4.10 + * the Free Software Foundation, version 2 of the License.
    4.11 + *
    4.12 + * This program is distributed in the hope that it will be useful,
    4.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.15 + * GNU General Public License for more details. apidesign.org
    4.16 + * designates this particular file as subject to the
    4.17 + * "Classpath" exception as provided by apidesign.org
    4.18 + * in the License file that accompanied this code.
    4.19 + *
    4.20 + * You should have received a copy of the GNU General Public License
    4.21 + * along with this program. Look for COPYING file in the top folder.
    4.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    4.23 + */
    4.24 +package net.java.html.json;
    4.25 +
    4.26 +import java.lang.annotation.ElementType;
    4.27 +import java.lang.annotation.Retention;
    4.28 +import java.lang.annotation.RetentionPolicy;
    4.29 +import java.lang.annotation.Target;
    4.30 +
    4.31 +/** Can be used in classes annotated with {@link Page} annotation to
    4.32 + * define a derived property. Value of derived property is based on values
    4.33 + * of {@link Property} as enumerated by {@link Page#properties()}.
    4.34 + * <p>
    4.35 + * The name of the derived property is the name of the method. The arguments
    4.36 + * of the method define the property names (from {@link Page#properties()} list)
    4.37 + * the value of property depends on. 
    4.38 + *
    4.39 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.40 + */
    4.41 +@Retention(RetentionPolicy.SOURCE)
    4.42 +@Target(ElementType.METHOD)
    4.43 +public @interface ComputedProperty {
    4.44 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/json/src/main/java/net/java/html/json/Function.java	Fri Apr 19 10:01:02 2013 +0200
     5.3 @@ -0,0 +1,37 @@
     5.4 +/**
     5.5 + * HTML via Java(tm) Language Bindings
     5.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     5.7 + *
     5.8 + * This program is free software: you can redistribute it and/or modify
     5.9 + * it under the terms of the GNU General Public License as published by
    5.10 + * the Free Software Foundation, version 2 of the License.
    5.11 + *
    5.12 + * This program is distributed in the hope that it will be useful,
    5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.15 + * GNU General Public License for more details. apidesign.org
    5.16 + * designates this particular file as subject to the
    5.17 + * "Classpath" exception as provided by apidesign.org
    5.18 + * in the License file that accompanied this code.
    5.19 + *
    5.20 + * You should have received a copy of the GNU General Public License
    5.21 + * along with this program. Look for COPYING file in the top folder.
    5.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    5.23 + */
    5.24 +package net.java.html.json;
    5.25 +
    5.26 +import java.lang.annotation.ElementType;
    5.27 +import java.lang.annotation.Retention;
    5.28 +import java.lang.annotation.RetentionPolicy;
    5.29 +import java.lang.annotation.Target;
    5.30 +
    5.31 +/** Methods in class annotated by {@link Model} or {@link Page} can be 
    5.32 + * annotated by this annotation to signal that they should be available
    5.33 + * as functions to users of the model classes.
    5.34 + *
    5.35 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    5.36 + */
    5.37 +@Target(ElementType.METHOD)
    5.38 +@Retention(RetentionPolicy.SOURCE)
    5.39 +public @interface Function {
    5.40 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/json/src/main/java/net/java/html/json/Model.java	Fri Apr 19 10:01:02 2013 +0200
     6.3 @@ -0,0 +1,47 @@
     6.4 +/**
     6.5 + * HTML via Java(tm) Language Bindings
     6.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     6.7 + *
     6.8 + * This program is free software: you can redistribute it and/or modify
     6.9 + * it under the terms of the GNU General Public License as published by
    6.10 + * the Free Software Foundation, version 2 of the License.
    6.11 + *
    6.12 + * This program is distributed in the hope that it will be useful,
    6.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.15 + * GNU General Public License for more details. apidesign.org
    6.16 + * designates this particular file as subject to the
    6.17 + * "Classpath" exception as provided by apidesign.org
    6.18 + * in the License file that accompanied this code.
    6.19 + *
    6.20 + * You should have received a copy of the GNU General Public License
    6.21 + * along with this program. Look for COPYING file in the top folder.
    6.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    6.23 + */
    6.24 +package net.java.html.json;
    6.25 +
    6.26 +import java.lang.annotation.ElementType;
    6.27 +import java.lang.annotation.Retention;
    6.28 +import java.lang.annotation.RetentionPolicy;
    6.29 +import java.lang.annotation.Target;
    6.30 +
    6.31 +/** Defines a model class named {@link #className()} which contains
    6.32 + * defined {@link #properties()}. This class can have methods 
    6.33 + * annotated by {@link ComputedProperty} which define derived
    6.34 + * properties in the model class.
    6.35 + * <p>
    6.36 + * The {@link #className() generated class}'s <code>toString</code>
    6.37 + * converts the state of the object into 
    6.38 + * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> format.
    6.39 + *
    6.40 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    6.41 + */
    6.42 +@Retention(RetentionPolicy.SOURCE)
    6.43 +@Target(ElementType.TYPE)
    6.44 +public @interface Model {
    6.45 +    /** Name of the model class */
    6.46 +    String className();
    6.47 +    /** List of properties in the model.
    6.48 +     */
    6.49 +    Property[] properties();
    6.50 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/json/src/main/java/net/java/html/json/OnPropertyChange.java	Fri Apr 19 10:01:02 2013 +0200
     7.3 @@ -0,0 +1,41 @@
     7.4 +/**
     7.5 + * HTML via Java(tm) Language Bindings
     7.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     7.7 + *
     7.8 + * This program is free software: you can redistribute it and/or modify
     7.9 + * it under the terms of the GNU General Public License as published by
    7.10 + * the Free Software Foundation, version 2 of the License.
    7.11 + *
    7.12 + * This program is distributed in the hope that it will be useful,
    7.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    7.15 + * GNU General Public License for more details. apidesign.org
    7.16 + * designates this particular file as subject to the
    7.17 + * "Classpath" exception as provided by apidesign.org
    7.18 + * in the License file that accompanied this code.
    7.19 + *
    7.20 + * You should have received a copy of the GNU General Public License
    7.21 + * along with this program. Look for COPYING file in the top folder.
    7.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    7.23 + */
    7.24 +package net.java.html.json;
    7.25 +
    7.26 +import java.lang.annotation.ElementType;
    7.27 +import java.lang.annotation.Retention;
    7.28 +import java.lang.annotation.RetentionPolicy;
    7.29 +import java.lang.annotation.Target;
    7.30 +
    7.31 +/** Represents a property. Either in a generated model of an HTML
    7.32 + * {@link Page} or in a class defined by {@link Model}.
    7.33 + *
    7.34 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    7.35 + */
    7.36 +@Retention(RetentionPolicy.SOURCE)
    7.37 +@Target(ElementType.METHOD)
    7.38 +public @interface OnPropertyChange {
    7.39 +    /** Name(s) of the properties. One wishes to observe.
    7.40 +     * 
    7.41 +     * @return valid java identifier
    7.42 +     */
    7.43 +    String[] value();
    7.44 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/json/src/main/java/net/java/html/json/OnReceive.java	Fri Apr 19 10:01:02 2013 +0200
     8.3 @@ -0,0 +1,96 @@
     8.4 +/**
     8.5 + * HTML via Java(tm) Language Bindings
     8.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     8.7 + *
     8.8 + * This program is free software: you can redistribute it and/or modify
     8.9 + * it under the terms of the GNU General Public License as published by
    8.10 + * the Free Software Foundation, version 2 of the License.
    8.11 + *
    8.12 + * This program is distributed in the hope that it will be useful,
    8.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    8.15 + * GNU General Public License for more details. apidesign.org
    8.16 + * designates this particular file as subject to the
    8.17 + * "Classpath" exception as provided by apidesign.org
    8.18 + * in the License file that accompanied this code.
    8.19 + *
    8.20 + * You should have received a copy of the GNU General Public License
    8.21 + * along with this program. Look for COPYING file in the top folder.
    8.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    8.23 + */
    8.24 +package net.java.html.json;
    8.25 +
    8.26 +import java.lang.annotation.ElementType;
    8.27 +import java.lang.annotation.Retention;
    8.28 +import java.lang.annotation.RetentionPolicy;
    8.29 +import java.lang.annotation.Target;
    8.30 +
    8.31 +/** Static methods in classes annotated by {@link Page}
    8.32 + * can be marked by this annotation to establish a 
    8.33 + * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a>
    8.34 + * communication point.
    8.35 + * The associated model page then gets new method to invoke a network
    8.36 + * connection. Example follows:
    8.37 + * 
    8.38 + * <pre>
    8.39 + * {@link Page @Page}(className="MyModel", xhtml="page.html", properties={
    8.40 + *   {@link Property @Property}(name = "people", type=Person.class, array=true)
    8.41 + * })
    8.42 + * class MyModelImpl {
    8.43 + *   {@link Model @Model}(className="Person", properties={
    8.44 + *     {@link Property @Property}(name = "firstName", type=String.class),
    8.45 + *     {@link Property @Property}(name = "lastName", type=String.class)
    8.46 + *   })
    8.47 + *   static class PersonImpl {
    8.48 + *     {@link ComputedProperty @ComputedProperty}
    8.49 + *     static String fullName(String firstName, String lastName) {
    8.50 + *       return firstName + " " + lastName;
    8.51 + *     }
    8.52 + *   }
    8.53 + * 
    8.54 + *   {@link OnReceive @OnReceive}(url = "{protocol}://your.server.com/person/{name}")
    8.55 + *   static void getANewPerson(MyModel m, Person p) {
    8.56 + *     {@link Element#alert Element.alert}("Adding " + p.getFullName() + '!');
    8.57 + *     m.getPeople().add(p);
    8.58 + *   }
    8.59 + * 
    8.60 + *   // the above will generate method <code>getANewPerson</code> in class <code>MyModel</code>.
    8.61 + *   // with <code>protocol</code> and <code>name</code> arguments
    8.62 + *   // which asynchronously contacts the server and in case of success calls
    8.63 + *   // your {@link OnReceive @OnReceive} with parsed in data
    8.64 + * 
    8.65 + *   {@link On @On}(event={@link OnEvent#CLICK OnEvent.CLICK}, id="rqst")
    8.66 + *   static void requestSmith(MyModel m) {
    8.67 + *     m.getANewPerson("http", "Smith");
    8.68 + *   }
    8.69 + * }
    8.70 + * </pre>
    8.71 + * When the server returns <code>{ "firstName" : "John", "lastName" : "Smith" }</code>
    8.72 + * the browser will show alert message <em>Adding John Smith!</em>.
    8.73 + * 
    8.74 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    8.75 + * @since 0.6
    8.76 + */
    8.77 +@Retention(RetentionPolicy.SOURCE)
    8.78 +@Target(ElementType.METHOD)
    8.79 +public @interface OnReceive {
    8.80 +    /** The URL to connect to. Can contain variable names surrounded by '{' and '}'.
    8.81 +     * Those parameters will then become variables of the associated method.
    8.82 +     * 
    8.83 +     * @return the (possibly parametrized) url to connect to
    8.84 +     */
    8.85 +    String url();
    8.86 +    
    8.87 +    /** Support for <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a> requires
    8.88 +     * a callback from the server generated page to a function defined in the
    8.89 +     * system. The name of such function is usually specified as a property
    8.90 +     * (of possibly different names). By defining the <code>jsonp</code> attribute
    8.91 +     * one turns on the <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a> 
    8.92 +     * transmission and specifies the name of the property. The property should
    8.93 +     * also be used in the {@link #url()} attribute on appropriate place.
    8.94 +     * 
    8.95 +     * @return name of a property to carry the name of <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
    8.96 +     *    callback function.
    8.97 +     */
    8.98 +    String jsonp() default "";
    8.99 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/json/src/main/java/net/java/html/json/Property.java	Fri Apr 19 10:01:02 2013 +0200
     9.3 @@ -0,0 +1,58 @@
     9.4 +/**
     9.5 + * HTML via Java(tm) Language Bindings
     9.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     9.7 + *
     9.8 + * This program is free software: you can redistribute it and/or modify
     9.9 + * it under the terms of the GNU General Public License as published by
    9.10 + * the Free Software Foundation, version 2 of the License.
    9.11 + *
    9.12 + * This program is distributed in the hope that it will be useful,
    9.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    9.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    9.15 + * GNU General Public License for more details. apidesign.org
    9.16 + * designates this particular file as subject to the
    9.17 + * "Classpath" exception as provided by apidesign.org
    9.18 + * in the License file that accompanied this code.
    9.19 + *
    9.20 + * You should have received a copy of the GNU General Public License
    9.21 + * along with this program. Look for COPYING file in the top folder.
    9.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
    9.23 + */
    9.24 +package net.java.html.json;
    9.25 +
    9.26 +import java.lang.annotation.Retention;
    9.27 +import java.lang.annotation.RetentionPolicy;
    9.28 +import java.lang.annotation.Target;
    9.29 +import java.util.List;
    9.30 +
    9.31 +/** Represents a property. Either in a generated model of an HTML
    9.32 + * {@link Page} or in a class defined by {@link Model}.
    9.33 + *
    9.34 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    9.35 + */
    9.36 +@Retention(RetentionPolicy.SOURCE)
    9.37 +@Target({})
    9.38 +public @interface Property {
    9.39 +    /** Name of the property. Will be used to define proper getter and setter
    9.40 +     * in the associated class.
    9.41 +     * 
    9.42 +     * @return valid java identifier
    9.43 +     */
    9.44 +    String name();
    9.45 +    
    9.46 +    /** Type of the property. Can either be primitive type (like <code>int.class</code>,
    9.47 +     * <code>double.class</code>, etc.), {@link String} or complex model
    9.48 +     * class (defined by {@link Model} property).
    9.49 +     * 
    9.50 +     * @return the class of the property
    9.51 +     */
    9.52 +    Class<?> type();
    9.53 +    
    9.54 +    /** Is this property an array of the {@link #type()} or a single value?
    9.55 +     * If the property is an array, only its getter (returning mutable {@link List} of
    9.56 +     * the boxed {@link #type()}).
    9.57 +     * 
    9.58 +     * @return true, if this is supposed to be an array of values.
    9.59 +     */
    9.60 +    boolean array() default false;
    9.61 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/PageProcessor.java	Fri Apr 19 10:01:02 2013 +0200
    10.3 @@ -0,0 +1,1180 @@
    10.4 +/**
    10.5 + * HTML via Java(tm) Language Bindings
    10.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    10.7 + *
    10.8 + * This program is free software: you can redistribute it and/or modify
    10.9 + * it under the terms of the GNU General Public License as published by
   10.10 + * the Free Software Foundation, version 2 of the License.
   10.11 + *
   10.12 + * This program is distributed in the hope that it will be useful,
   10.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10.15 + * GNU General Public License for more details. apidesign.org
   10.16 + * designates this particular file as subject to the
   10.17 + * "Classpath" exception as provided by apidesign.org
   10.18 + * in the License file that accompanied this code.
   10.19 + *
   10.20 + * You should have received a copy of the GNU General Public License
   10.21 + * along with this program. Look for COPYING file in the top folder.
   10.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
   10.23 + */
   10.24 +package org.apidesign.html.json.impl;
   10.25 +
   10.26 +import java.io.IOException;
   10.27 +import java.io.InputStream;
   10.28 +import java.io.OutputStreamWriter;
   10.29 +import java.io.StringWriter;
   10.30 +import java.io.Writer;
   10.31 +import java.lang.annotation.AnnotationTypeMismatchException;
   10.32 +import java.lang.annotation.IncompleteAnnotationException;
   10.33 +import java.lang.reflect.Method;
   10.34 +import java.util.ArrayList;
   10.35 +import java.util.Collection;
   10.36 +import java.util.Collections;
   10.37 +import java.util.HashMap;
   10.38 +import java.util.HashSet;
   10.39 +import java.util.LinkedHashSet;
   10.40 +import java.util.List;
   10.41 +import java.util.Map;
   10.42 +import java.util.Set;
   10.43 +import java.util.WeakHashMap;
   10.44 +import javax.annotation.processing.AbstractProcessor;
   10.45 +import javax.annotation.processing.ProcessingEnvironment;
   10.46 +import javax.annotation.processing.Processor;
   10.47 +import javax.annotation.processing.RoundEnvironment;
   10.48 +import javax.annotation.processing.SupportedAnnotationTypes;
   10.49 +import javax.lang.model.element.AnnotationMirror;
   10.50 +import javax.lang.model.element.AnnotationValue;
   10.51 +import javax.lang.model.element.Element;
   10.52 +import javax.lang.model.element.ElementKind;
   10.53 +import javax.lang.model.element.ExecutableElement;
   10.54 +import javax.lang.model.element.Modifier;
   10.55 +import javax.lang.model.element.PackageElement;
   10.56 +import javax.lang.model.element.TypeElement;
   10.57 +import javax.lang.model.element.VariableElement;
   10.58 +import javax.lang.model.type.ArrayType;
   10.59 +import javax.lang.model.type.MirroredTypeException;
   10.60 +import javax.lang.model.type.TypeKind;
   10.61 +import javax.lang.model.type.TypeMirror;
   10.62 +import javax.lang.model.util.Elements;
   10.63 +import javax.lang.model.util.Types;
   10.64 +import javax.tools.Diagnostic;
   10.65 +import javax.tools.FileObject;
   10.66 +import javax.tools.StandardLocation;
   10.67 +import net.java.html.json.ComputedProperty;
   10.68 +import net.java.html.json.Model;
   10.69 +import net.java.html.json.Function;
   10.70 +import net.java.html.json.OnPropertyChange;
   10.71 +import net.java.html.json.OnReceive;
   10.72 +import net.java.html.json.Property;
   10.73 +import org.openide.util.lookup.ServiceProvider;
   10.74 +
   10.75 +/** Annotation processor to process an XHTML page and generate appropriate 
   10.76 + * "id" file.
   10.77 + *
   10.78 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   10.79 + */
   10.80 +@ServiceProvider(service=Processor.class)
   10.81 +@SupportedAnnotationTypes({
   10.82 +    "net.java.html.model.Model",
   10.83 +    "net.java.html.model.Function",
   10.84 +    "net.java.html.model.OnReceive",
   10.85 +    "net.java.html.model.OnPropertyChange",
   10.86 +    "net.java.html.model.ComputedProperty",
   10.87 +    "net.java.html.model.Property"
   10.88 +})
   10.89 +public final class PageProcessor extends AbstractProcessor {
   10.90 +    private final Map<Element,String> models = new WeakHashMap<>();
   10.91 +    private final Map<Element,Prprt[]> verify = new WeakHashMap<>();
   10.92 +    @Override
   10.93 +    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   10.94 +        boolean ok = true;
   10.95 +        for (Element e : roundEnv.getElementsAnnotatedWith(Model.class)) {
   10.96 +            if (!processModel(e)) {
   10.97 +                ok = false;
   10.98 +            }
   10.99 +        }
  10.100 +        if (roundEnv.processingOver()) {
  10.101 +            models.clear();
  10.102 +            for (Map.Entry<Element, Prprt[]> entry : verify.entrySet()) {
  10.103 +                TypeElement te = (TypeElement)entry.getKey();
  10.104 +                String fqn = processingEnv.getElementUtils().getBinaryName(te).toString();
  10.105 +                Element finalElem = processingEnv.getElementUtils().getTypeElement(fqn);
  10.106 +                if (finalElem == null) {
  10.107 +                    continue;
  10.108 +                }
  10.109 +                Prprt[] props;
  10.110 +                Model m = finalElem.getAnnotation(Model.class);
  10.111 +                props = Prprt.wrap(processingEnv, finalElem, m.properties());
  10.112 +                for (Prprt p : props) {
  10.113 +                    boolean[] isModel = { false };
  10.114 +                    boolean[] isEnum = { false };
  10.115 +                    boolean[] isPrimitive = { false };
  10.116 +                    String t = checkType(p, isModel, isEnum, isPrimitive);
  10.117 +                    if (isEnum[0]) {
  10.118 +                        continue;
  10.119 +                    }
  10.120 +                    if (isPrimitive[0]) {
  10.121 +                        continue;
  10.122 +                    }
  10.123 +                    if (isModel[0]) {
  10.124 +                        continue;
  10.125 +                    }
  10.126 +                    if ("java.lang.String".equals(t)) {
  10.127 +                        continue;
  10.128 +                    }
  10.129 +                    error("The type " + t + " should be defined by @Model annotation", entry.getKey());
  10.130 +                }
  10.131 +            }
  10.132 +            verify.clear();
  10.133 +        }
  10.134 +        return ok;
  10.135 +    }
  10.136 +
  10.137 +    private InputStream openStream(String pkg, String name) throws IOException {
  10.138 +        try {
  10.139 +            FileObject fo = processingEnv.getFiler().getResource(
  10.140 +                StandardLocation.SOURCE_PATH, pkg, name);
  10.141 +            return fo.openInputStream();
  10.142 +        } catch (IOException ex) {
  10.143 +            return processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, pkg, name).openInputStream();
  10.144 +        }
  10.145 +    }
  10.146 +
  10.147 +    private void error(String msg, Element e) {
  10.148 +        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
  10.149 +    }
  10.150 +    
  10.151 +    private boolean processModel(Element e) {
  10.152 +        boolean ok = true;
  10.153 +        Model m = e.getAnnotation(Model.class);
  10.154 +        if (m == null) {
  10.155 +            return true;
  10.156 +        }
  10.157 +        String pkg = findPkgName(e);
  10.158 +        Writer w;
  10.159 +        String className = m.className();
  10.160 +        models.put(e, className);
  10.161 +        try {
  10.162 +            StringWriter body = new StringWriter();
  10.163 +            List<String> propsGetSet = new ArrayList<>();
  10.164 +            List<String> functions = new ArrayList<>();
  10.165 +            Map<String, Collection<String>> propsDeps = new HashMap<>();
  10.166 +            Map<String, Collection<String>> functionDeps = new HashMap<>();
  10.167 +            Prprt[] props = createProps(e, m.properties());
  10.168 +            
  10.169 +            if (!generateComputedProperties(body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
  10.170 +                ok = false;
  10.171 +            }
  10.172 +            if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
  10.173 +                ok = false;
  10.174 +            }
  10.175 +            if (!generateProperties(e, body, props, propsGetSet, propsDeps, functionDeps)) {
  10.176 +                ok = false;
  10.177 +            }
  10.178 +            if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
  10.179 +                ok = false;
  10.180 +            }
  10.181 +            FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
  10.182 +            w = new OutputStreamWriter(java.openOutputStream());
  10.183 +            try {
  10.184 +                w.append("package " + pkg + ";\n");
  10.185 +                w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
  10.186 +                w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n");
  10.187 +                w.append("import org.apidesign.bck2brwsr.core.JavaScriptOnly;\n");
  10.188 +                w.append("public final class ").append(className).append(" implements Cloneable {\n");
  10.189 +                w.append("  private boolean locked;\n");
  10.190 +                w.append("  private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
  10.191 +                w.append(body.toString());
  10.192 +                w.append("  private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
  10.193 +                w.append("  public ").append(className).append("() {\n");
  10.194 +                w.append("  };\n");
  10.195 +                w.append("  private org.apidesign.bck2brwsr.htmlpage.Knockout intKnckt() {\n");
  10.196 +                w.append("    if (ko != null) return ko;\n");
  10.197 +                w.append("    return ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(this, ");
  10.198 +                writeStringArray(propsGetSet, w);
  10.199 +                w.append(", ");
  10.200 +                writeStringArray(functions, w);
  10.201 +                w.append("    );\n");
  10.202 +                w.append("  };\n");
  10.203 +                w.append("  ").append(className).append("(Object json) {\n");
  10.204 +                int values = 0;
  10.205 +                for (int i = 0; i < propsGetSet.size(); i += 4) {
  10.206 +                    Prprt p = findPrprt(props, propsGetSet.get(i));
  10.207 +                    if (p == null) {
  10.208 +                        continue;
  10.209 +                    }
  10.210 +                    values++;
  10.211 +                }
  10.212 +                w.append("    Object[] ret = new Object[" + values + "];\n");
  10.213 +                w.append("    org.apidesign.bck2brwsr.htmlpage.ConvertTypes.extractJSON(json, new String[] {\n");
  10.214 +                for (int i = 0; i < propsGetSet.size(); i += 4) {
  10.215 +                    Prprt p = findPrprt(props, propsGetSet.get(i));
  10.216 +                    if (p == null) {
  10.217 +                        continue;
  10.218 +                    }
  10.219 +                    w.append("      \"").append(propsGetSet.get(i)).append("\",\n");
  10.220 +                }
  10.221 +                w.append("    }, ret);\n");
  10.222 +                for (int i = 0, cnt = 0, prop = 0; i < propsGetSet.size(); i += 4) {
  10.223 +                    final String pn = propsGetSet.get(i);
  10.224 +                    Prprt p = findPrprt(props, pn);
  10.225 +                    if (p == null) {
  10.226 +                        continue;
  10.227 +                    }
  10.228 +                    boolean[] isModel = { false };
  10.229 +                    boolean[] isEnum = { false };
  10.230 +                    boolean isPrimitive[] = { false };
  10.231 +                    String type = checkType(props[prop++], isModel, isEnum, isPrimitive);
  10.232 +                    if (p.array()) {
  10.233 +                        w.append("if (ret[" + cnt + "] instanceof Object[]) {\n");
  10.234 +                        w.append("  for (Object e : ((Object[])ret[" + cnt + "])) {\n");
  10.235 +                        if (isModel[0]) {
  10.236 +                            w.append("    this.prop_").append(pn).append(".add(new ");
  10.237 +                            w.append(type).append("(e));\n");
  10.238 +                        } else if (isEnum[0]) {
  10.239 +                            w.append("    this.prop_").append(pn);
  10.240 +                            w.append(".add(e == null ? null : ");
  10.241 +                            w.append(type).append(".valueOf((String)e));\n");
  10.242 +                        } else {
  10.243 +                            if (isPrimitive(type)) {
  10.244 +                                w.append("    this.prop_").append(pn).append(".add(((Number)e).");
  10.245 +                                w.append(type).append("Value());\n");
  10.246 +                            } else {
  10.247 +                                w.append("    this.prop_").append(pn).append(".add((");
  10.248 +                                w.append(type).append(")e);\n");
  10.249 +                            }
  10.250 +                        }
  10.251 +                        w.append("  }\n");
  10.252 +                        w.append("}\n");
  10.253 +                    } else {
  10.254 +                        if (isEnum[0]) {
  10.255 +                            w.append("    this.prop_").append(pn);
  10.256 +                            w.append(" = ret[" + cnt + "] == null ? null : ");
  10.257 +                            w.append(type).append(".valueOf((String)ret[" + cnt + "]);\n");
  10.258 +                        } else if (isPrimitive(type)) {
  10.259 +                            w.append("    this.prop_").append(pn);
  10.260 +                            w.append(" = ((Number)").append("ret[" + cnt + "]).");
  10.261 +                            w.append(type).append("Value();\n");
  10.262 +                        } else {
  10.263 +                            w.append("    this.prop_").append(pn);
  10.264 +                            w.append(" = (").append(type).append(')');
  10.265 +                            w.append("ret[" + cnt + "];\n");
  10.266 +                        }
  10.267 +                    }
  10.268 +                    cnt++;
  10.269 +                }
  10.270 +                w.append("  };\n");
  10.271 +                writeToString(props, w);
  10.272 +                writeClone(className, props, w);
  10.273 +                w.append("  public Object koData() {\n");
  10.274 +                w.append("    return intKnckt().koData();\n");
  10.275 +                w.append("  }\n");
  10.276 +                w.append("}\n");
  10.277 +            } finally {
  10.278 +                w.close();
  10.279 +            }
  10.280 +        } catch (IOException ex) {
  10.281 +            error("Can't create " + className + ".java", e);
  10.282 +            return false;
  10.283 +        }
  10.284 +        return ok;
  10.285 +    }
  10.286 +    
  10.287 +    private static String type(String tag) {
  10.288 +        if (tag.equals("title")) {
  10.289 +            return "Title";
  10.290 +        }
  10.291 +        if (tag.equals("button")) {
  10.292 +            return "Button";
  10.293 +        }
  10.294 +        if (tag.equals("input")) {
  10.295 +            return "Input";
  10.296 +        }
  10.297 +        if (tag.equals("canvas")) {
  10.298 +            return "Canvas";
  10.299 +        }
  10.300 +        if (tag.equals("img")) {
  10.301 +            return "Image";
  10.302 +        }
  10.303 +        return "Element";
  10.304 +    }
  10.305 +
  10.306 +    private boolean generateProperties(
  10.307 +        Element where,
  10.308 +        Writer w, Prprt[] properties,
  10.309 +        Collection<String> props, 
  10.310 +        Map<String,Collection<String>> deps,
  10.311 +        Map<String,Collection<String>> functionDeps
  10.312 +    ) throws IOException {
  10.313 +        boolean ok = true;
  10.314 +        for (Prprt p : properties) {
  10.315 +            final String tn;
  10.316 +            tn = typeName(where, p);
  10.317 +            String[] gs = toGetSet(p.name(), tn, p.array());
  10.318 +
  10.319 +            if (p.array()) {
  10.320 +                w.write("private KOList<" + tn + "> prop_" + p.name() + " = new KOList<" + tn + ">(\""
  10.321 +                    + p.name() + "\"");
  10.322 +                Collection<String> dependants = deps.get(p.name());
  10.323 +                if (dependants != null) {
  10.324 +                    for (String depProp : dependants) {
  10.325 +                        w.write(", ");
  10.326 +                        w.write('\"');
  10.327 +                        w.write(depProp);
  10.328 +                        w.write('\"');
  10.329 +                    }
  10.330 +                }
  10.331 +                w.write(")");
  10.332 +                
  10.333 +                dependants = functionDeps.get(p.name());
  10.334 +                if (dependants != null) {
  10.335 +                    w.write(".onChange(new Runnable() { public void run() {\n");
  10.336 +                    for (String call : dependants) {
  10.337 +                        w.append(call);
  10.338 +                    }
  10.339 +                    w.write("}})");
  10.340 +                }
  10.341 +                w.write(";\n");
  10.342 +                
  10.343 +                w.write("public java.util.List<" + tn + "> " + gs[0] + "() {\n");
  10.344 +                w.write("  if (locked) throw new IllegalStateException();\n");
  10.345 +                w.write("  prop_" + p.name() + ".assign(ko);\n");
  10.346 +                w.write("  return prop_" + p.name() + ";\n");
  10.347 +                w.write("}\n");
  10.348 +            } else {
  10.349 +                w.write("private " + tn + " prop_" + p.name() + ";\n");
  10.350 +                w.write("public " + tn + " " + gs[0] + "() {\n");
  10.351 +                w.write("  if (locked) throw new IllegalStateException();\n");
  10.352 +                w.write("  return prop_" + p.name() + ";\n");
  10.353 +                w.write("}\n");
  10.354 +                w.write("public void " + gs[1] + "(" + tn + " v) {\n");
  10.355 +                w.write("  if (locked) throw new IllegalStateException();\n");
  10.356 +                w.write("  prop_" + p.name() + " = v;\n");
  10.357 +                w.write("  if (ko != null) {\n");
  10.358 +                w.write("    ko.valueHasMutated(\"" + p.name() + "\");\n");
  10.359 +                Collection<String> dependants = deps.get(p.name());
  10.360 +                if (dependants != null) {
  10.361 +                    for (String depProp : dependants) {
  10.362 +                        w.write("    ko.valueHasMutated(\"" + depProp + "\");\n");
  10.363 +                    }
  10.364 +                }
  10.365 +                w.write("  }\n");
  10.366 +                dependants = functionDeps.get(p.name());
  10.367 +                if (dependants != null) {
  10.368 +                    for (String call : dependants) {
  10.369 +                        w.append(call);
  10.370 +                    }
  10.371 +                }
  10.372 +                w.write("}\n");
  10.373 +            }
  10.374 +            
  10.375 +            props.add(p.name());
  10.376 +            props.add(gs[2]);
  10.377 +            props.add(gs[3]);
  10.378 +            props.add(gs[0]);
  10.379 +        }
  10.380 +        return ok;
  10.381 +    }
  10.382 +
  10.383 +    private boolean generateComputedProperties(
  10.384 +        Writer w, Prprt[] fixedProps,
  10.385 +        Collection<? extends Element> arr, Collection<String> props,
  10.386 +        Map<String,Collection<String>> deps
  10.387 +    ) throws IOException {
  10.388 +        boolean ok = true;
  10.389 +        for (Element e : arr) {
  10.390 +            if (e.getKind() != ElementKind.METHOD) {
  10.391 +                continue;
  10.392 +            }
  10.393 +            if (e.getAnnotation(ComputedProperty.class) == null) {
  10.394 +                continue;
  10.395 +            }
  10.396 +            ExecutableElement ee = (ExecutableElement)e;
  10.397 +            final TypeMirror rt = ee.getReturnType();
  10.398 +            final Types tu = processingEnv.getTypeUtils();
  10.399 +            TypeMirror ert = tu.erasure(rt);
  10.400 +            String tn = fqn(ert, ee);
  10.401 +            boolean array = false;
  10.402 +            if (tn.equals("java.util.List")) {
  10.403 +                array = true;
  10.404 +            }
  10.405 +            
  10.406 +            final String sn = ee.getSimpleName().toString();
  10.407 +            String[] gs = toGetSet(sn, tn, array);
  10.408 +            
  10.409 +            w.write("public " + tn + " " + gs[0] + "() {\n");
  10.410 +            w.write("  if (locked) throw new IllegalStateException();\n");
  10.411 +            int arg = 0;
  10.412 +            for (VariableElement pe : ee.getParameters()) {
  10.413 +                final String dn = pe.getSimpleName().toString();
  10.414 +                
  10.415 +                if (!verifyPropName(pe, dn, fixedProps)) {
  10.416 +                    ok = false;
  10.417 +                }
  10.418 +                
  10.419 +                final String dt = fqn(pe.asType(), ee);
  10.420 +                String[] call = toGetSet(dn, dt, false);
  10.421 +                w.write("  " + dt + " arg" + (++arg) + " = ");
  10.422 +                w.write(call[0] + "();\n");
  10.423 +                
  10.424 +                Collection<String> depends = deps.get(dn);
  10.425 +                if (depends == null) {
  10.426 +                    depends = new LinkedHashSet<>();
  10.427 +                    deps.put(dn, depends);
  10.428 +                }
  10.429 +                depends.add(sn);
  10.430 +            }
  10.431 +            w.write("  try {\n");
  10.432 +            w.write("    locked = true;\n");
  10.433 +            w.write("    return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
  10.434 +            String sep = "";
  10.435 +            for (int i = 1; i <= arg; i++) {
  10.436 +                w.write(sep);
  10.437 +                w.write("arg" + i);
  10.438 +                sep = ", ";
  10.439 +            }
  10.440 +            w.write(");\n");
  10.441 +            w.write("  } finally {\n");
  10.442 +            w.write("    locked = false;\n");
  10.443 +            w.write("  }\n");
  10.444 +            w.write("}\n");
  10.445 +
  10.446 +            props.add(e.getSimpleName().toString());
  10.447 +            props.add(gs[2]);
  10.448 +            props.add(null);
  10.449 +            props.add(gs[0]);
  10.450 +        }
  10.451 +        
  10.452 +        return ok;
  10.453 +    }
  10.454 +
  10.455 +    private static String[] toGetSet(String name, String type, boolean array) {
  10.456 +        String n = Character.toUpperCase(name.charAt(0)) + name.substring(1);
  10.457 +        String bck2brwsrType = "L" + type.replace('.', '_') + "_2";
  10.458 +        if ("int".equals(type)) {
  10.459 +            bck2brwsrType = "I";
  10.460 +        }
  10.461 +        if ("double".equals(type)) {
  10.462 +            bck2brwsrType = "D";
  10.463 +        }
  10.464 +        String pref = "get";
  10.465 +        if ("boolean".equals(type)) {
  10.466 +            pref = "is";
  10.467 +            bck2brwsrType = "Z";
  10.468 +        }
  10.469 +        final String nu = n.replace('.', '_');
  10.470 +        if (array) {
  10.471 +            return new String[] { 
  10.472 +                "get" + n,
  10.473 +                null,
  10.474 +                "get" + nu + "__Ljava_util_List_2",
  10.475 +                null
  10.476 +            };
  10.477 +        }
  10.478 +        return new String[]{
  10.479 +            pref + n, 
  10.480 +            "set" + n, 
  10.481 +            pref + nu + "__" + bck2brwsrType,
  10.482 +            "set" + nu + "__V" + bck2brwsrType
  10.483 +        };
  10.484 +    }
  10.485 +
  10.486 +    private String typeName(Element where, Prprt p) {
  10.487 +        String ret;
  10.488 +        boolean[] isModel = { false };
  10.489 +        boolean[] isEnum = { false };
  10.490 +        boolean isPrimitive[] = { false };
  10.491 +        ret = checkType(p, isModel, isEnum, isPrimitive);
  10.492 +        if (p.array()) {
  10.493 +            String bt = findBoxedType(ret);
  10.494 +            if (bt != null) {
  10.495 +                return bt;
  10.496 +            }
  10.497 +        }
  10.498 +        return ret;
  10.499 +    }
  10.500 +    
  10.501 +    private static String findBoxedType(String ret) {
  10.502 +        if (ret.equals("boolean")) {
  10.503 +            return Boolean.class.getName();
  10.504 +        }
  10.505 +        if (ret.equals("byte")) {
  10.506 +            return Byte.class.getName();
  10.507 +        }
  10.508 +        if (ret.equals("short")) {
  10.509 +            return Short.class.getName();
  10.510 +        }
  10.511 +        if (ret.equals("char")) {
  10.512 +            return Character.class.getName();
  10.513 +        }
  10.514 +        if (ret.equals("int")) {
  10.515 +            return Integer.class.getName();
  10.516 +        }
  10.517 +        if (ret.equals("long")) {
  10.518 +            return Long.class.getName();
  10.519 +        }
  10.520 +        if (ret.equals("float")) {
  10.521 +            return Float.class.getName();
  10.522 +        }
  10.523 +        if (ret.equals("double")) {
  10.524 +            return Double.class.getName();
  10.525 +        }
  10.526 +        return null;
  10.527 +    }
  10.528 +
  10.529 +    private boolean verifyPropName(Element e, String propName, Prprt[] existingProps) {
  10.530 +        StringBuilder sb = new StringBuilder();
  10.531 +        String sep = "";
  10.532 +        for (Prprt Prprt : existingProps) {
  10.533 +            if (Prprt.name().equals(propName)) {
  10.534 +                return true;
  10.535 +            }
  10.536 +            sb.append(sep);
  10.537 +            sb.append('"');
  10.538 +            sb.append(Prprt.name());
  10.539 +            sb.append('"');
  10.540 +            sep = ", ";
  10.541 +        }
  10.542 +        error(
  10.543 +            propName + " is not one of known properties: " + sb
  10.544 +            , e
  10.545 +        );
  10.546 +        return false;
  10.547 +    }
  10.548 +
  10.549 +    private static String findPkgName(Element e) {
  10.550 +        for (;;) {
  10.551 +            if (e.getKind() == ElementKind.PACKAGE) {
  10.552 +                return ((PackageElement)e).getQualifiedName().toString();
  10.553 +            }
  10.554 +            e = e.getEnclosingElement();
  10.555 +        }
  10.556 +    }
  10.557 +
  10.558 +    private boolean generateFunctions(
  10.559 +        Element clazz, StringWriter body, String className, 
  10.560 +        List<? extends Element> enclosedElements, List<String> functions
  10.561 +    ) {
  10.562 +        for (Element m : enclosedElements) {
  10.563 +            if (m.getKind() != ElementKind.METHOD) {
  10.564 +                continue;
  10.565 +            }
  10.566 +            ExecutableElement e = (ExecutableElement)m;
  10.567 +            Function onF = e.getAnnotation(Function.class);
  10.568 +            if (onF == null) {
  10.569 +                continue;
  10.570 +            }
  10.571 +            if (!e.getModifiers().contains(Modifier.STATIC)) {
  10.572 +                error("@OnFunction method needs to be static", e);
  10.573 +                return false;
  10.574 +            }
  10.575 +            if (e.getModifiers().contains(Modifier.PRIVATE)) {
  10.576 +                error("@OnFunction method cannot be private", e);
  10.577 +                return false;
  10.578 +            }
  10.579 +            if (e.getReturnType().getKind() != TypeKind.VOID) {
  10.580 +                error("@OnFunction method should return void", e);
  10.581 +                return false;
  10.582 +            }
  10.583 +            String n = e.getSimpleName().toString();
  10.584 +            body.append("private void ").append(n).append("(Object data, Object ev) {\n");
  10.585 +            body.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  10.586 +            body.append(wrapParams(e, null, className, "ev", "data"));
  10.587 +            body.append(");\n");
  10.588 +            body.append("}\n");
  10.589 +            
  10.590 +            functions.add(n);
  10.591 +            functions.add(n + "__VLjava_lang_Object_2Ljava_lang_Object_2");
  10.592 +        }
  10.593 +        return true;
  10.594 +    }
  10.595 +
  10.596 +    private boolean generateOnChange(Element clazz, Map<String,Collection<String>> propDeps,
  10.597 +        Prprt[] properties, String className, 
  10.598 +        Map<String, Collection<String>> functionDeps
  10.599 +    ) {
  10.600 +        for (Element m : clazz.getEnclosedElements()) {
  10.601 +            if (m.getKind() != ElementKind.METHOD) {
  10.602 +                continue;
  10.603 +            }
  10.604 +            ExecutableElement e = (ExecutableElement) m;
  10.605 +            OnPropertyChange onPC = e.getAnnotation(OnPropertyChange.class);
  10.606 +            if (onPC == null) {
  10.607 +                continue;
  10.608 +            }
  10.609 +            for (String pn : onPC.value()) {
  10.610 +                if (findPrprt(properties, pn) == null && findDerivedFrom(propDeps, pn).isEmpty()) {
  10.611 +                    error("No Prprt named '" + pn + "' in the model", clazz);
  10.612 +                    return false;
  10.613 +                }
  10.614 +            }
  10.615 +            if (!e.getModifiers().contains(Modifier.STATIC)) {
  10.616 +                error("@OnPrprtChange method needs to be static", e);
  10.617 +                return false;
  10.618 +            }
  10.619 +            if (e.getModifiers().contains(Modifier.PRIVATE)) {
  10.620 +                error("@OnPrprtChange method cannot be private", e);
  10.621 +                return false;
  10.622 +            }
  10.623 +            if (e.getReturnType().getKind() != TypeKind.VOID) {
  10.624 +                error("@OnPrprtChange method should return void", e);
  10.625 +                return false;
  10.626 +            }
  10.627 +            String n = e.getSimpleName().toString();
  10.628 +            
  10.629 +            
  10.630 +            for (String pn : onPC.value()) {
  10.631 +                StringBuilder call = new StringBuilder();
  10.632 +                call.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  10.633 +                call.append(wrapPropName(e, className, "name", pn));
  10.634 +                call.append(");\n");
  10.635 +                
  10.636 +                Collection<String> change = functionDeps.get(pn);
  10.637 +                if (change == null) {
  10.638 +                    change = new ArrayList<>();
  10.639 +                    functionDeps.put(pn, change);
  10.640 +                }
  10.641 +                change.add(call.toString());
  10.642 +                for (String dpn : findDerivedFrom(propDeps, pn)) {
  10.643 +                    change = functionDeps.get(dpn);
  10.644 +                    if (change == null) {
  10.645 +                        change = new ArrayList<>();
  10.646 +                        functionDeps.put(dpn, change);
  10.647 +                    }
  10.648 +                    change.add(call.toString());
  10.649 +                }
  10.650 +            }
  10.651 +        }
  10.652 +        return true;
  10.653 +    }
  10.654 +    
  10.655 +    private boolean generateReceive(
  10.656 +        Element clazz, StringWriter body, String className, 
  10.657 +        List<? extends Element> enclosedElements, List<String> functions
  10.658 +    ) {
  10.659 +        for (Element m : enclosedElements) {
  10.660 +            if (m.getKind() != ElementKind.METHOD) {
  10.661 +                continue;
  10.662 +            }
  10.663 +            ExecutableElement e = (ExecutableElement)m;
  10.664 +            OnReceive onR = e.getAnnotation(OnReceive.class);
  10.665 +            if (onR == null) {
  10.666 +                continue;
  10.667 +            }
  10.668 +            if (!e.getModifiers().contains(Modifier.STATIC)) {
  10.669 +                error("@OnReceive method needs to be static", e);
  10.670 +                return false;
  10.671 +            }
  10.672 +            if (e.getModifiers().contains(Modifier.PRIVATE)) {
  10.673 +                error("@OnReceive method cannot be private", e);
  10.674 +                return false;
  10.675 +            }
  10.676 +            if (e.getReturnType().getKind() != TypeKind.VOID) {
  10.677 +                error("@OnReceive method should return void", e);
  10.678 +                return false;
  10.679 +            }
  10.680 +            String modelClass = null;
  10.681 +            boolean expectsList = false;
  10.682 +            List<String> args = new ArrayList<>();
  10.683 +            {
  10.684 +                for (VariableElement ve : e.getParameters()) {
  10.685 +                    TypeMirror modelType = null;
  10.686 +                    if (ve.asType().toString().equals(className)) {
  10.687 +                        args.add(className + ".this");
  10.688 +                    } else if (isModel(ve.asType())) {
  10.689 +                        modelType = ve.asType();
  10.690 +                    } else if (ve.asType().getKind() == TypeKind.ARRAY) {
  10.691 +                        modelType = ((ArrayType)ve.asType()).getComponentType();
  10.692 +                        expectsList = true;
  10.693 +                    }
  10.694 +                    if (modelType != null) {
  10.695 +                        if (modelClass != null) {
  10.696 +                            error("There can be only one model class among arguments", e);
  10.697 +                        } else {
  10.698 +                            modelClass = modelType.toString();
  10.699 +                            if (expectsList) {
  10.700 +                                args.add("arr");
  10.701 +                            } else {
  10.702 +                                args.add("arr[0]");
  10.703 +                            }
  10.704 +                        }
  10.705 +                    }
  10.706 +                }
  10.707 +            }
  10.708 +            if (modelClass == null) {
  10.709 +                error("The method needs to have one @Model class as parameter", e);
  10.710 +            }
  10.711 +            String n = e.getSimpleName().toString();
  10.712 +            body.append("public void ").append(n).append("(");
  10.713 +            StringBuilder assembleURL = new StringBuilder();
  10.714 +            String jsonpVarName = null;
  10.715 +            {
  10.716 +                String sep = "";
  10.717 +                boolean skipJSONP = onR.jsonp().isEmpty();
  10.718 +                for (String p : findParamNames(e, onR.url(), assembleURL)) {
  10.719 +                    if (!skipJSONP && p.equals(onR.jsonp())) {
  10.720 +                        skipJSONP = true;
  10.721 +                        jsonpVarName = p;
  10.722 +                        continue;
  10.723 +                    }
  10.724 +                    body.append(sep);
  10.725 +                    body.append("String ").append(p);
  10.726 +                    sep = ", ";
  10.727 +                }
  10.728 +                if (!skipJSONP) {
  10.729 +                    error(
  10.730 +                        "Name of jsonp attribute ('" + onR.jsonp() + 
  10.731 +                        "') is not used in url attribute '" + onR.url() + "'", e
  10.732 +                    );
  10.733 +                }
  10.734 +            }
  10.735 +            body.append(") {\n");
  10.736 +            body.append("  final Object[] result = { null };\n");
  10.737 +            body.append(
  10.738 +                "  class ProcessResult implements Runnable {\n" +
  10.739 +                "    @Override\n" +
  10.740 +                "    public void run() {\n" +
  10.741 +                "      Object value = result[0];\n");
  10.742 +            body.append(
  10.743 +                "      " + modelClass + "[] arr;\n");
  10.744 +            body.append(
  10.745 +                "      if (value instanceof Object[]) {\n" +
  10.746 +                "        Object[] data = ((Object[])value);\n" +
  10.747 +                "        arr = new " + modelClass + "[data.length];\n" +
  10.748 +                "        for (int i = 0; i < data.length; i++) {\n" +
  10.749 +                "          arr[i] = new " + modelClass + "(data[i]);\n" +
  10.750 +                "        }\n" +
  10.751 +                "      } else {\n" +
  10.752 +                "        arr = new " + modelClass + "[1];\n" +
  10.753 +                "        arr[0] = new " + modelClass + "(value);\n" +
  10.754 +                "      }\n"
  10.755 +            );
  10.756 +            {
  10.757 +                body.append(clazz.getSimpleName()).append(".").append(n).append("(");
  10.758 +                String sep = "";
  10.759 +                for (String arg : args) {
  10.760 +                    body.append(sep);
  10.761 +                    body.append(arg);
  10.762 +                    sep = ", ";
  10.763 +                }
  10.764 +                body.append(");\n");
  10.765 +            }
  10.766 +            body.append(
  10.767 +                "    }\n" +
  10.768 +                "  }\n"
  10.769 +            );
  10.770 +            body.append("  ProcessResult pr = new ProcessResult();\n");
  10.771 +            if (jsonpVarName != null) {
  10.772 +                body.append("  String ").append(jsonpVarName).
  10.773 +                    append(" = org.apidesign.bck2brwsr.htmlpage.ConvertTypes.createJSONP(result, pr);\n");
  10.774 +            }
  10.775 +            body.append("  org.apidesign.bck2brwsr.htmlpage.ConvertTypes.loadJSON(\n      ");
  10.776 +            body.append(assembleURL);
  10.777 +            body.append(", result, pr, ").append(jsonpVarName).append("\n  );\n");
  10.778 +//            body.append("  ").append(clazz.getSimpleName()).append(".").append(n).append("(");
  10.779 +//            body.append(wrapParams(e, null, className, "ev", "data"));
  10.780 +//            body.append(");\n");
  10.781 +            body.append("}\n");
  10.782 +        }
  10.783 +        return true;
  10.784 +    }
  10.785 +
  10.786 +    private CharSequence wrapParams(
  10.787 +        ExecutableElement ee, String id, String className, String evName, String dataName
  10.788 +    ) {
  10.789 +        TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
  10.790 +        StringBuilder params = new StringBuilder();
  10.791 +        boolean first = true;
  10.792 +        for (VariableElement ve : ee.getParameters()) {
  10.793 +            if (!first) {
  10.794 +                params.append(", ");
  10.795 +            }
  10.796 +            first = false;
  10.797 +            String toCall = null;
  10.798 +            if (ve.asType() == stringType) {
  10.799 +                if (ve.getSimpleName().contentEquals("id")) {
  10.800 +                    params.append('"').append(id).append('"');
  10.801 +                    continue;
  10.802 +                }
  10.803 +                toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString(";
  10.804 +            }
  10.805 +            if (ve.asType().getKind() == TypeKind.DOUBLE) {
  10.806 +                toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble(";
  10.807 +            }
  10.808 +            if (ve.asType().getKind() == TypeKind.INT) {
  10.809 +                toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toInt(";
  10.810 +            }
  10.811 +            if (dataName != null && ve.getSimpleName().contentEquals(dataName) && isModel(ve.asType())) {
  10.812 +                toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toModel(" + ve.asType() + ".class, ";
  10.813 +            }
  10.814 +
  10.815 +            if (toCall != null) {
  10.816 +                params.append(toCall);
  10.817 +                if (dataName != null && ve.getSimpleName().contentEquals(dataName)) {
  10.818 +                    params.append(dataName);
  10.819 +                    params.append(", null");
  10.820 +                } else {
  10.821 +                    if (evName == null) {
  10.822 +                        final StringBuilder sb = new StringBuilder();
  10.823 +                        sb.append("Unexpected string parameter name.");
  10.824 +                        if (dataName != null) {
  10.825 +                            sb.append(" Try \"").append(dataName).append("\"");
  10.826 +                        }
  10.827 +                        error(sb.toString(), ee);
  10.828 +                    }
  10.829 +                    params.append(evName);
  10.830 +                    params.append(", \"");
  10.831 +                    params.append(ve.getSimpleName().toString());
  10.832 +                    params.append("\"");
  10.833 +                }
  10.834 +                params.append(")");
  10.835 +                continue;
  10.836 +            }
  10.837 +            String rn = fqn(ve.asType(), ee);
  10.838 +            int last = rn.lastIndexOf('.');
  10.839 +            if (last >= 0) {
  10.840 +                rn = rn.substring(last + 1);
  10.841 +            }
  10.842 +            if (rn.equals(className)) {
  10.843 +                params.append(className).append(".this");
  10.844 +                continue;
  10.845 +            }
  10.846 +            error(
  10.847 +                "@On method can only accept String named 'id' or " + className + " arguments",
  10.848 +                ee
  10.849 +            );
  10.850 +        }
  10.851 +        return params;
  10.852 +    }
  10.853 +    
  10.854 +    
  10.855 +    private CharSequence wrapPropName(
  10.856 +        ExecutableElement ee, String className, String propName, String propValue
  10.857 +    ) {
  10.858 +        TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
  10.859 +        StringBuilder params = new StringBuilder();
  10.860 +        boolean first = true;
  10.861 +        for (VariableElement ve : ee.getParameters()) {
  10.862 +            if (!first) {
  10.863 +                params.append(", ");
  10.864 +            }
  10.865 +            first = false;
  10.866 +            if (ve.asType() == stringType) {
  10.867 +                if (propName != null && ve.getSimpleName().contentEquals(propName)) {
  10.868 +                    params.append('"').append(propValue).append('"');
  10.869 +                } else {
  10.870 +                    error("Unexpected string parameter name. Try \"" + propName + "\".", ee);
  10.871 +                }
  10.872 +                continue;
  10.873 +            }
  10.874 +            String rn = fqn(ve.asType(), ee);
  10.875 +            int last = rn.lastIndexOf('.');
  10.876 +            if (last >= 0) {
  10.877 +                rn = rn.substring(last + 1);
  10.878 +            }
  10.879 +            if (rn.equals(className)) {
  10.880 +                params.append(className).append(".this");
  10.881 +                continue;
  10.882 +            }
  10.883 +            error(
  10.884 +                "@OnPrprtChange method can only accept String or " + className + " arguments",
  10.885 +                ee);
  10.886 +        }
  10.887 +        return params;
  10.888 +    }
  10.889 +    
  10.890 +    private boolean isModel(TypeMirror tm) {
  10.891 +        final Element e = processingEnv.getTypeUtils().asElement(tm);
  10.892 +        if (e == null) {
  10.893 +            return false;
  10.894 +        }
  10.895 +        for (Element ch : e.getEnclosedElements()) {
  10.896 +            if (ch.getKind() == ElementKind.METHOD) {
  10.897 +                ExecutableElement ee = (ExecutableElement)ch;
  10.898 +                if (ee.getParameters().isEmpty() && ee.getSimpleName().contentEquals("modelFor")) {
  10.899 +                    return true;
  10.900 +                }
  10.901 +            }
  10.902 +        }
  10.903 +        return models.values().contains(e.getSimpleName().toString());
  10.904 +    }
  10.905 +
  10.906 +    private void writeStringArray(List<String> strings, Writer w) throws IOException {
  10.907 +        w.write("new String[] {\n");
  10.908 +        String sep = "";
  10.909 +        for (String n : strings) {
  10.910 +            w.write(sep);
  10.911 +            if (n == null) {
  10.912 +                w.write("    null");
  10.913 +            } else {
  10.914 +                w.write("    \"" + n + "\"");
  10.915 +            }
  10.916 +            sep = ",\n";
  10.917 +        }
  10.918 +        w.write("\n  }");
  10.919 +    }
  10.920 +    
  10.921 +    private void writeToString(Prprt[] props, Writer w) throws IOException {
  10.922 +        w.write("  public String toString() {\n");
  10.923 +        w.write("    StringBuilder sb = new StringBuilder();\n");
  10.924 +        w.write("    sb.append('{');\n");
  10.925 +        String sep = "";
  10.926 +        for (Prprt p : props) {
  10.927 +            w.write(sep);
  10.928 +            w.append("    sb.append(\"" + p.name() + ": \");\n");
  10.929 +            w.append("    sb.append(org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toJSON(prop_");
  10.930 +            w.append(p.name()).append("));\n");
  10.931 +            sep =    "    sb.append(',');\n";
  10.932 +        }
  10.933 +        w.write("    sb.append('}');\n");
  10.934 +        w.write("    return sb.toString();\n");
  10.935 +        w.write("  }\n");
  10.936 +    }
  10.937 +    private void writeClone(String className, Prprt[] props, Writer w) throws IOException {
  10.938 +        w.write("  public " + className + " clone() {\n");
  10.939 +        w.write("    " + className + " ret = new " + className + "();\n");
  10.940 +        for (Prprt p : props) {
  10.941 +            if (!p.array()) {
  10.942 +                boolean isModel[] = { false };
  10.943 +                boolean isEnum[] = { false };
  10.944 +                boolean isPrimitive[] = { false };
  10.945 +                checkType(p, isModel, isEnum, isPrimitive);
  10.946 +                if (!isModel[0]) {
  10.947 +                    w.write("    ret.prop_" + p.name() + " = prop_" + p.name() + ";\n");
  10.948 +                    continue;
  10.949 +                }
  10.950 +                w.write("    ret.prop_" + p.name() + " = prop_" + p.name() + ".clone();\n");
  10.951 +            } else {
  10.952 +                w.write("    ret.prop_" + p.name() + " = prop_" + p.name() + ".clone();\n");
  10.953 +            }
  10.954 +        }
  10.955 +        
  10.956 +        w.write("    return ret;\n");
  10.957 +        w.write("  }\n");
  10.958 +    }
  10.959 +
  10.960 +    private String inPckName(Element e) {
  10.961 +        StringBuilder sb = new StringBuilder();
  10.962 +        while (e.getKind() != ElementKind.PACKAGE) {
  10.963 +            if (sb.length() == 0) {
  10.964 +                sb.append(e.getSimpleName());
  10.965 +            } else {
  10.966 +                sb.insert(0, '.');
  10.967 +                sb.insert(0, e.getSimpleName());
  10.968 +            }
  10.969 +            e = e.getEnclosingElement();
  10.970 +        }
  10.971 +        return sb.toString();
  10.972 +    }
  10.973 +
  10.974 +    private String fqn(TypeMirror pt, Element relative) {
  10.975 +        if (pt.getKind() == TypeKind.ERROR) {
  10.976 +            final Elements eu = processingEnv.getElementUtils();
  10.977 +            PackageElement pckg = eu.getPackageOf(relative);
  10.978 +            return pckg.getQualifiedName() + "." + pt.toString();
  10.979 +        }
  10.980 +        return pt.toString();
  10.981 +    }
  10.982 +
  10.983 +    private String checkType(Prprt p, boolean[] isModel, boolean[] isEnum, boolean[] isPrimitive) {
  10.984 +        TypeMirror tm;
  10.985 +        try {
  10.986 +            String ret = p.typeName(processingEnv);
  10.987 +            TypeElement e = processingEnv.getElementUtils().getTypeElement(ret);
  10.988 +            if (e == null) {
  10.989 +                isModel[0] = true;
  10.990 +                isEnum[0] = false;
  10.991 +                isPrimitive[0] = false;
  10.992 +                return ret;
  10.993 +            }
  10.994 +            tm = e.asType();
  10.995 +        } catch (MirroredTypeException ex) {
  10.996 +            tm = ex.getTypeMirror();
  10.997 +        }
  10.998 +        tm = processingEnv.getTypeUtils().erasure(tm);
  10.999 +        isPrimitive[0] = tm.getKind().isPrimitive();
 10.1000 +        final Element e = processingEnv.getTypeUtils().asElement(tm);
 10.1001 +        final Model m = e == null ? null : e.getAnnotation(Model.class);
 10.1002 +        
 10.1003 +        String ret;
 10.1004 +        if (m != null) {
 10.1005 +            ret = findPkgName(e) + '.' + m.className();
 10.1006 +            isModel[0] = true;
 10.1007 +            models.put(e, m.className());
 10.1008 +        } else if (findModelForMthd(e)) {
 10.1009 +            ret = ((TypeElement)e).getQualifiedName().toString();
 10.1010 +            isModel[0] = true;
 10.1011 +        } else {
 10.1012 +            ret = tm.toString();
 10.1013 +        }
 10.1014 +        TypeMirror enm = processingEnv.getElementUtils().getTypeElement("java.lang.Enum").asType();
 10.1015 +        enm = processingEnv.getTypeUtils().erasure(enm);
 10.1016 +        isEnum[0] = processingEnv.getTypeUtils().isSubtype(tm, enm);
 10.1017 +        return ret;
 10.1018 +    }
 10.1019 +    
 10.1020 +    private static boolean findModelForMthd(Element clazz) {
 10.1021 +        if (clazz == null) {
 10.1022 +            return false;
 10.1023 +        }
 10.1024 +        for (Element e : clazz.getEnclosedElements()) {
 10.1025 +            if (e.getKind() == ElementKind.METHOD) {
 10.1026 +                ExecutableElement ee = (ExecutableElement)e;
 10.1027 +                if (
 10.1028 +                    ee.getSimpleName().contentEquals("modelFor") &&
 10.1029 +                    ee.getParameters().isEmpty()
 10.1030 +                ) {
 10.1031 +                    return true;
 10.1032 +                }
 10.1033 +            }
 10.1034 +        }
 10.1035 +        return false;
 10.1036 +    }
 10.1037 +
 10.1038 +    private Iterable<String> findParamNames(Element e, String url, StringBuilder assembleURL) {
 10.1039 +        List<String> params = new ArrayList<>();
 10.1040 +
 10.1041 +        for (int pos = 0; ;) {
 10.1042 +            int next = url.indexOf('{', pos);
 10.1043 +            if (next == -1) {
 10.1044 +                assembleURL.append('"')
 10.1045 +                    .append(url.substring(pos))
 10.1046 +                    .append('"');
 10.1047 +                return params;
 10.1048 +            }
 10.1049 +            int close = url.indexOf('}', next);
 10.1050 +            if (close == -1) {
 10.1051 +                error("Unbalanced '{' and '}' in " + url, e);
 10.1052 +                return params;
 10.1053 +            }
 10.1054 +            final String paramName = url.substring(next + 1, close);
 10.1055 +            params.add(paramName);
 10.1056 +            assembleURL.append('"')
 10.1057 +                .append(url.substring(pos, next))
 10.1058 +                .append("\" + ").append(paramName).append(" + ");
 10.1059 +            pos = close + 1;
 10.1060 +        }
 10.1061 +    }
 10.1062 +
 10.1063 +    private static Prprt findPrprt(Prprt[] properties, String propName) {
 10.1064 +        for (Prprt p : properties) {
 10.1065 +            if (propName.equals(p.name())) {
 10.1066 +                return p;
 10.1067 +            }
 10.1068 +        }
 10.1069 +        return null;
 10.1070 +    }
 10.1071 +
 10.1072 +    private boolean isPrimitive(String type) {
 10.1073 +        return 
 10.1074 +            "int".equals(type) ||
 10.1075 +            "double".equals(type) ||
 10.1076 +            "long".equals(type) ||
 10.1077 +            "short".equals(type) ||
 10.1078 +            "byte".equals(type) ||
 10.1079 +            "float".equals(type);
 10.1080 +    }
 10.1081 +
 10.1082 +    private static Collection<String> findDerivedFrom(Map<String, Collection<String>> propsDeps, String derivedProp) {
 10.1083 +        Set<String> names = new HashSet<>();
 10.1084 +        for (Map.Entry<String, Collection<String>> e : propsDeps.entrySet()) {
 10.1085 +            if (e.getValue().contains(derivedProp)) {
 10.1086 +                names.add(e.getKey());
 10.1087 +            }
 10.1088 +        }
 10.1089 +        return names;
 10.1090 +    }
 10.1091 +    
 10.1092 +    private Prprt[] createProps(Element e, Property[] arr) {
 10.1093 +        Prprt[] ret = Prprt.wrap(processingEnv, e, arr);
 10.1094 +        Prprt[] prev = verify.put(e, ret);
 10.1095 +        if (prev != null) {
 10.1096 +            error("Two sets of properties for ", e);
 10.1097 +        }
 10.1098 +        return ret;
 10.1099 +    }
 10.1100 +    
 10.1101 +    private static class Prprt {
 10.1102 +        private final Element e;
 10.1103 +        private final AnnotationMirror tm;
 10.1104 +        private final Property p;
 10.1105 +
 10.1106 +        public Prprt(Element e, AnnotationMirror tm, Property p) {
 10.1107 +            this.e = e;
 10.1108 +            this.tm = tm;
 10.1109 +            this.p = p;
 10.1110 +        }
 10.1111 +        
 10.1112 +        String name() {
 10.1113 +            return p.name();
 10.1114 +        }
 10.1115 +        
 10.1116 +        boolean array() {
 10.1117 +            return p.array();
 10.1118 +        }
 10.1119 +
 10.1120 +        String typeName(ProcessingEnvironment env) {
 10.1121 +            try {
 10.1122 +                return p.type().getName();
 10.1123 +            } catch (IncompleteAnnotationException | AnnotationTypeMismatchException ex) {
 10.1124 +                for (Object v : getAnnoValues(env)) {
 10.1125 +                    String s = v.toString().replace(" ", "");
 10.1126 +                    if (s.startsWith("type=") && s.endsWith(".class")) {
 10.1127 +                        return s.substring(5, s.length() - 6);
 10.1128 +                    }
 10.1129 +                }
 10.1130 +                throw ex;
 10.1131 +            }
 10.1132 +        }
 10.1133 +        
 10.1134 +        
 10.1135 +        static Prprt[] wrap(ProcessingEnvironment pe, Element e, Property[] arr) {
 10.1136 +            if (arr.length == 0) {
 10.1137 +                return new Prprt[0];
 10.1138 +            }
 10.1139 +            
 10.1140 +            if (e.getKind() != ElementKind.CLASS) {
 10.1141 +                throw new IllegalStateException("" + e.getKind());
 10.1142 +            }
 10.1143 +            TypeElement te = (TypeElement)e;
 10.1144 +            List<? extends AnnotationValue> val = null;
 10.1145 +            for (AnnotationMirror an : te.getAnnotationMirrors()) {
 10.1146 +                for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : an.getElementValues().entrySet()) {
 10.1147 +                    if (entry.getKey().getSimpleName().contentEquals("properties")) {
 10.1148 +                        val = (List)entry.getValue().getValue();
 10.1149 +                        break;
 10.1150 +                    }
 10.1151 +                }
 10.1152 +            }
 10.1153 +            if (val == null || val.size() != arr.length) {
 10.1154 +                pe.getMessager().printMessage(Diagnostic.Kind.ERROR, "" + val, e);
 10.1155 +                return new Prprt[0];
 10.1156 +            }
 10.1157 +            Prprt[] ret = new Prprt[arr.length];
 10.1158 +            BIG: for (int i = 0; i < ret.length; i++) {
 10.1159 +                AnnotationMirror am = (AnnotationMirror)val.get(i).getValue();
 10.1160 +                ret[i] = new Prprt(e, am, arr[i]);
 10.1161 +                
 10.1162 +            }
 10.1163 +            return ret;
 10.1164 +        }
 10.1165 +        
 10.1166 +        private List<? extends Object> getAnnoValues(ProcessingEnvironment pe) {
 10.1167 +            try {
 10.1168 +                Class<?> trees = Class.forName("com.sun.tools.javac.api.JavacTrees");
 10.1169 +                Method m = trees.getMethod("instance", ProcessingEnvironment.class);
 10.1170 +                Object instance = m.invoke(null, pe);
 10.1171 +                m = instance.getClass().getMethod("getPath", Element.class, AnnotationMirror.class);
 10.1172 +                Object path = m.invoke(instance, e, tm);
 10.1173 +                m = path.getClass().getMethod("getLeaf");
 10.1174 +                Object leaf = m.invoke(path);
 10.1175 +                m = leaf.getClass().getMethod("getArguments");
 10.1176 +                return (List)m.invoke(leaf);
 10.1177 +            } catch (Exception ex) {
 10.1178 +                return Collections.emptyList();
 10.1179 +            }
 10.1180 +        }
 10.1181 +    }
 10.1182 +    
 10.1183 +}
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/json/src/test/java/net/java/html/json/ModelTest.java	Fri Apr 19 10:01:02 2013 +0200
    11.3 @@ -0,0 +1,241 @@
    11.4 +/**
    11.5 + * HTML via Java(tm) Language Bindings
    11.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    11.7 + *
    11.8 + * This program is free software: you can redistribute it and/or modify
    11.9 + * it under the terms of the GNU General Public License as published by
   11.10 + * the Free Software Foundation, version 2 of the License.
   11.11 + *
   11.12 + * This program is distributed in the hope that it will be useful,
   11.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11.15 + * GNU General Public License for more details. apidesign.org
   11.16 + * designates this particular file as subject to the
   11.17 + * "Classpath" exception as provided by apidesign.org
   11.18 + * in the License file that accompanied this code.
   11.19 + *
   11.20 + * You should have received a copy of the GNU General Public License
   11.21 + * along with this program. Look for COPYING file in the top folder.
   11.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
   11.23 + */
   11.24 +package net.java.html.json;
   11.25 +
   11.26 +import java.util.ArrayList;
   11.27 +import java.util.Collections;
   11.28 +import java.util.Iterator;
   11.29 +import java.util.List;
   11.30 +import java.util.ListIterator;
   11.31 +import static org.testng.Assert.*;
   11.32 +import org.testng.annotations.BeforeMethod;
   11.33 +import org.testng.annotations.Test;
   11.34 +
   11.35 +/**
   11.36 + *
   11.37 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   11.38 + */
   11.39 +@Model(className = "Modelik", properties = {
   11.40 +    @Property(name = "value", type = int.class),
   11.41 +    @Property(name = "count", type = int.class),
   11.42 +    @Property(name = "unrelated", type = long.class),
   11.43 +    @Property(name = "names", type = String.class, array = true),
   11.44 +    @Property(name = "values", type = int.class, array = true),
   11.45 +    @Property(name = "people", type = Person.class, array = true),
   11.46 +    @Property(name = "changedProperty", type=String.class)
   11.47 +})
   11.48 +public class ModelTest {
   11.49 +    private Modelik model;
   11.50 +    private static Modelik leakedModel;
   11.51 +    
   11.52 +    @BeforeMethod
   11.53 +    public void createModel() {
   11.54 +        model = new Modelik();
   11.55 +    }
   11.56 +    
   11.57 +    @Test public void classGeneratedWithSetterGetter() {
   11.58 +        model.setValue(10);
   11.59 +        assertEquals(10, model.getValue(), "Value changed");
   11.60 +    }
   11.61 +    
   11.62 +    @Test public void computedMethod() {
   11.63 +        model.setValue(4);
   11.64 +        assertEquals(16, model.getPowerValue());
   11.65 +    }
   11.66 +    
   11.67 +    @Test public void arrayIsMutable() {
   11.68 +        assertEquals(model.getNames().size(), 0, "Is empty");
   11.69 +        model.getNames().add("Jarda");
   11.70 +        assertEquals(model.getNames().size(), 1, "One element");
   11.71 +    }
   11.72 +/*    
   11.73 +    @Test public void arrayChangesNotified() {
   11.74 +        MockKnockout my = new MockKnockout();
   11.75 +        MockKnockout.next = my;
   11.76 +        
   11.77 +        model.applyBindings();
   11.78 +        
   11.79 +        model.getNames().add("Hello");
   11.80 +        
   11.81 +        assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
   11.82 +        assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
   11.83 +
   11.84 +        my.mutated.clear();
   11.85 +        
   11.86 +        Iterator<String> it = model.getNames().iterator();
   11.87 +        assertEquals(it.next(), "Hello");
   11.88 +        it.remove();
   11.89 +        
   11.90 +        assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
   11.91 +        assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
   11.92 +
   11.93 +        my.mutated.clear();
   11.94 +        
   11.95 +        ListIterator<String> lit = model.getNames().listIterator();
   11.96 +        lit.add("Jarda");
   11.97 +        
   11.98 +        assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
   11.99 +        assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
  11.100 +    }
  11.101 +    
  11.102 +    @Test public void autoboxedArray() {
  11.103 +        MockKnockout my = new MockKnockout();
  11.104 +        MockKnockout.next = my;
  11.105 +        
  11.106 +        model.applyBindings();
  11.107 +        
  11.108 +        model.getValues().add(10);
  11.109 +        
  11.110 +        assertEquals(model.getValues().get(0), Integer.valueOf(10), "Really ten");
  11.111 +    }
  11.112 +
  11.113 +    @Test public void derivedArrayProp() {
  11.114 +        MockKnockout my = new MockKnockout();
  11.115 +        MockKnockout.next = my;
  11.116 +        
  11.117 +        model.applyBindings();
  11.118 +        
  11.119 +        model.setCount(10);
  11.120 +        
  11.121 +        List<String> arr = model.getRepeat();
  11.122 +        assertEquals(arr.size(), 10, "Ten items: " + arr);
  11.123 +        
  11.124 +        my.mutated.clear();
  11.125 +        
  11.126 +        model.setCount(5);
  11.127 +        
  11.128 +        arr = model.getRepeat();
  11.129 +        assertEquals(arr.size(), 5, "Five items: " + arr);
  11.130 +
  11.131 +        assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
  11.132 +        assertTrue(my.mutated.contains("repeat"), "Array is in there: " + my.mutated);
  11.133 +        assertTrue(my.mutated.contains("count"), "Count is in there: " + my.mutated);
  11.134 +    }
  11.135 +    
  11.136 +    @Test public void derivedPropertiesAreNotified() {
  11.137 +        MockKnockout my = new MockKnockout();
  11.138 +        MockKnockout.next = my;
  11.139 +        
  11.140 +        model.applyBindings();
  11.141 +        
  11.142 +        model.setValue(33);
  11.143 +        
  11.144 +        // not interested in change of this property
  11.145 +        my.mutated.remove("changedProperty");
  11.146 +        
  11.147 +        assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
  11.148 +        assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated);
  11.149 +        assertTrue(my.mutated.contains("value"), "Simple value is in there: " + my.mutated);
  11.150 +        
  11.151 +        my.mutated.clear();
  11.152 +        
  11.153 +        model.setUnrelated(44);
  11.154 +        
  11.155 +        
  11.156 +        // not interested in change of this property
  11.157 +        my.mutated.remove("changedProperty");
  11.158 +        assertEquals(my.mutated.size(), 1, "One property changed: " + my.mutated);
  11.159 +        assertTrue(my.mutated.contains("unrelated"), "Its name is unrelated");
  11.160 +    }
  11.161 +    */
  11.162 +    @Test public void computedPropertyCannotWriteToModel() {
  11.163 +        leakedModel = model;
  11.164 +        try {
  11.165 +            String res = model.getNotAllowedWrite();
  11.166 +            fail("We should not be allowed to write to the model: " + res);
  11.167 +        } catch (IllegalStateException ex) {
  11.168 +            // OK, we can't read
  11.169 +        }
  11.170 +    }
  11.171 +
  11.172 +    @Test public void computedPropertyCannotReadToModel() {
  11.173 +        leakedModel = model;
  11.174 +        try {
  11.175 +            String res = model.getNotAllowedRead();
  11.176 +            fail("We should not be allowed to read from the model: " + res);
  11.177 +        } catch (IllegalStateException ex) {
  11.178 +            // OK, we can't read
  11.179 +        }
  11.180 +    }
  11.181 +    
  11.182 +    @Function 
  11.183 +    static void doSomething() {
  11.184 +    }
  11.185 +    
  11.186 +    @ComputedProperty
  11.187 +    static int powerValue(int value) {
  11.188 +        return value * value;
  11.189 +    }
  11.190 +    
  11.191 +    @OnPropertyChange({ "powerValue", "unrelated" })
  11.192 +    static void aPropertyChanged(Modelik m, String name) {
  11.193 +        m.setChangedProperty(name);
  11.194 +    }
  11.195 +
  11.196 +    @OnPropertyChange({ "values" })
  11.197 +    static void anArrayPropertyChanged(String name, Modelik m) {
  11.198 +        m.setChangedProperty(name);
  11.199 +    }
  11.200 +    
  11.201 +    @Test public void changeAnything() {
  11.202 +        model.setCount(44);
  11.203 +        assertNull(model.getChangedProperty(), "No observed value change");
  11.204 +    }
  11.205 +    @Test public void changeValue() {
  11.206 +        model.setValue(33);
  11.207 +        assertEquals(model.getChangedProperty(), "powerValue", "power property changed");
  11.208 +    }
  11.209 +    @Test public void changeUnrelated() {
  11.210 +        model.setUnrelated(333);
  11.211 +        assertEquals(model.getChangedProperty(), "unrelated", "unrelated changed");
  11.212 +    }
  11.213 +
  11.214 +    @Test public void changeInArray() {
  11.215 +        model.getValues().add(10);
  11.216 +        assertEquals(model.getChangedProperty(), "values", "Something added into the array");
  11.217 +    }
  11.218 +    
  11.219 +    @ComputedProperty
  11.220 +    static String notAllowedRead() {
  11.221 +        return "Not allowed callback: " + leakedModel.getUnrelated();
  11.222 +    }
  11.223 +
  11.224 +    @ComputedProperty
  11.225 +    static String notAllowedWrite() {
  11.226 +        leakedModel.setUnrelated(11);
  11.227 +        return "Not allowed callback!";
  11.228 +    }
  11.229 +    
  11.230 +    @ComputedProperty
  11.231 +    static List<String> repeat(int count) {
  11.232 +        return Collections.nCopies(count, "Hello");
  11.233 +    }
  11.234 +    
  11.235 +    public @Test void hasPersonPropertyAndComputedFullName() {
  11.236 +        List<Person> arr = model.getPeople();
  11.237 +        assertEquals(arr.size(), 0, "By default empty");
  11.238 +        Person p = null;
  11.239 +        if (p != null) {
  11.240 +            String fullNameGenerated = p.getFullName();
  11.241 +            assertNotNull(fullNameGenerated);
  11.242 +        }
  11.243 +    }
  11.244 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/json/src/test/java/net/java/html/json/PersonImpl.java	Fri Apr 19 10:01:02 2013 +0200
    12.3 @@ -0,0 +1,60 @@
    12.4 +/**
    12.5 + * HTML via Java(tm) Language Bindings
    12.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    12.7 + *
    12.8 + * This program is free software: you can redistribute it and/or modify
    12.9 + * it under the terms of the GNU General Public License as published by
   12.10 + * the Free Software Foundation, version 2 of the License.
   12.11 + *
   12.12 + * This program is distributed in the hope that it will be useful,
   12.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12.15 + * GNU General Public License for more details. apidesign.org
   12.16 + * designates this particular file as subject to the
   12.17 + * "Classpath" exception as provided by apidesign.org
   12.18 + * in the License file that accompanied this code.
   12.19 + *
   12.20 + * You should have received a copy of the GNU General Public License
   12.21 + * along with this program. Look for COPYING file in the top folder.
   12.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
   12.23 + */
   12.24 +package net.java.html.json;
   12.25 +
   12.26 +/**
   12.27 + *
   12.28 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   12.29 + */
   12.30 +@Model(className = "Person", properties = {
   12.31 +    @Property(name = "firstName", type = String.class),
   12.32 +    @Property(name = "lastName", type = String.class),
   12.33 +    @Property(name = "sex", type = Sex.class)
   12.34 +})
   12.35 +final class PersonImpl {
   12.36 +    @ComputedProperty 
   12.37 +    public static String fullName(String firstName, String lastName) {
   12.38 +        return firstName + " " + lastName;
   12.39 +    }
   12.40 +    
   12.41 +    @ComputedProperty
   12.42 +    public static String sexType(Sex sex) {
   12.43 +        return sex == null ? "unknown" : sex.toString();
   12.44 +    }
   12.45 +    
   12.46 +    @Function
   12.47 +    static void changeSex(Person p) {
   12.48 +        if (p.getSex() == Sex.MALE) {
   12.49 +            p.setSex(Sex.FEMALE);
   12.50 +        } else {
   12.51 +            p.setSex(Sex.MALE);
   12.52 +        }
   12.53 +    }
   12.54 +    
   12.55 +    @Model(className = "People", properties = {
   12.56 +        @Property(array = true, name = "info", type = Person.class),
   12.57 +        @Property(array = true, name = "nicknames", type = String.class),
   12.58 +        @Property(array = true, name = "age", type = int.class),
   12.59 +        @Property(array = true, name = "sex", type = Sex.class)
   12.60 +    })
   12.61 +    public class PeopleImpl {
   12.62 +    }
   12.63 +}
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/json/src/test/java/net/java/html/json/Sex.java	Fri Apr 19 10:01:02 2013 +0200
    13.3 @@ -0,0 +1,29 @@
    13.4 +/**
    13.5 + * HTML via Java(tm) Language Bindings
    13.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    13.7 + *
    13.8 + * This program is free software: you can redistribute it and/or modify
    13.9 + * it under the terms of the GNU General Public License as published by
   13.10 + * the Free Software Foundation, version 2 of the License.
   13.11 + *
   13.12 + * This program is distributed in the hope that it will be useful,
   13.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13.15 + * GNU General Public License for more details. apidesign.org
   13.16 + * designates this particular file as subject to the
   13.17 + * "Classpath" exception as provided by apidesign.org
   13.18 + * in the License file that accompanied this code.
   13.19 + *
   13.20 + * You should have received a copy of the GNU General Public License
   13.21 + * along with this program. Look for COPYING file in the top folder.
   13.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
   13.23 + */
   13.24 +package net.java.html.json;
   13.25 +
   13.26 +/**
   13.27 + *
   13.28 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   13.29 + */
   13.30 +public enum Sex {
   13.31 +    MALE, FEMALE;
   13.32 +}
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/pom.xml	Fri Apr 19 10:01:02 2013 +0200
    14.3 @@ -0,0 +1,248 @@
    14.4 +<?xml version="1.0" encoding="UTF-8"?>
    14.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    14.6 +  <modelVersion>4.0.0</modelVersion>
    14.7 +  <groupId>org.apidesign</groupId>
    14.8 +  <artifactId>html</artifactId>
    14.9 +  <version>0.1-SNAPSHOT</version>
   14.10 +  <packaging>pom</packaging>
   14.11 +  <name>HTML APIs via Java</name>
   14.12 +  <parent>
   14.13 +    <groupId>net.java</groupId>
   14.14 +    <artifactId>jvnet-parent</artifactId>
   14.15 +    <version>3</version>
   14.16 +  </parent>  
   14.17 +  <properties>
   14.18 +      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   14.19 +      <netbeans.version>RELEASE73</netbeans.version>
   14.20 +      <license>COPYING</license>
   14.21 +  </properties>
   14.22 +  <modules>
   14.23 +    <module>json</module>
   14.24 +  </modules>
   14.25 +  <licenses>
   14.26 +      <license>
   14.27 +          <name>GPL-2.0wCPexc</name>
   14.28 +          <url>http://opensource.org/licenses/GPL-2.0</url>
   14.29 +          <distribution>repo</distribution>
   14.30 +      </license>
   14.31 +  </licenses>
   14.32 +  <organization>
   14.33 +      <name>API Design</name>
   14.34 +      <url>http://apidesign.org</url>
   14.35 +  </organization>
   14.36 +  <scm>
   14.37 +      <connection>scm:hg:https://hg.java.net/hg/html~html4j</connection>
   14.38 +      <developerConnection>scm:hg:https://hg.java.net/hg/html~html4j</developerConnection>
   14.39 +      <url>https://hg.java.net/hg/html~html4j</url>
   14.40 +      <tag>default</tag>
   14.41 +  </scm>
   14.42 +  <repositories>
   14.43 +      <repository>
   14.44 +          <id>netbeans</id>
   14.45 +          <name>NetBeans</name>
   14.46 +          <url>http://bits.netbeans.org/maven2/</url>
   14.47 +      </repository>
   14.48 +  </repositories>
   14.49 +  <pluginRepositories>
   14.50 +      <pluginRepository>
   14.51 +          <id>mc-release</id>
   14.52 +          <name>Local Maven repository of releases</name>
   14.53 +          <url>http://mc-repo.googlecode.com/svn/maven2/releases</url>
   14.54 +          <snapshots>
   14.55 +              <enabled>false</enabled>
   14.56 +          </snapshots>
   14.57 +          <releases>
   14.58 +              <enabled>true</enabled>
   14.59 +          </releases>
   14.60 +      </pluginRepository>
   14.61 +  </pluginRepositories>
   14.62 +  <build>
   14.63 +      <plugins>
   14.64 +         <plugin>
   14.65 +              <inherited>false</inherited>
   14.66 +              <groupId>com.mycila.maven-license-plugin</groupId>
   14.67 +              <artifactId>maven-license-plugin</artifactId>
   14.68 +              <version>1.9.0</version>
   14.69 +              <executions>
   14.70 +                  <execution>
   14.71 +                      <id>blah</id>
   14.72 +                      <goals>
   14.73 +                          <goal>check</goal>
   14.74 +                      </goals>
   14.75 +                  </execution>
   14.76 +              </executions>
   14.77 +              <configuration>
   14.78 +                  <aggregate>true</aggregate>
   14.79 +                  <basedir>${basedir}</basedir>
   14.80 +                  <header>COPYING</header>
   14.81 +                  <strictCheck>true</strictCheck>
   14.82 +                  <excludes>
   14.83 +                       <exclude>*</exclude>
   14.84 +                  </excludes>
   14.85 +              </configuration>
   14.86 +          </plugin>
   14.87 +           <plugin>
   14.88 +            <artifactId>maven-release-plugin</artifactId>
   14.89 +            <version>2.4</version>
   14.90 +            <configuration>
   14.91 +              <mavenExecutorId>forked-path</mavenExecutorId>
   14.92 +              <useReleaseProfile>false</useReleaseProfile>
   14.93 +              <arguments>-Pjvnet-release -Pgpg</arguments>
   14.94 +              <tag>release-${releaseVersion}</tag>
   14.95 +            </configuration>
   14.96 +          </plugin>
   14.97 +      </plugins>
   14.98 +      <pluginManagement>
   14.99 +          <plugins>
  14.100 +              <plugin>
  14.101 +                <groupId>org.apache.maven.plugins</groupId>
  14.102 +                <artifactId>maven-surefire-plugin</artifactId>
  14.103 +                <version>2.13</version>
  14.104 +              </plugin>
  14.105 +              <plugin>
  14.106 +                <groupId>org.apache.maven.plugins</groupId>
  14.107 +                <artifactId>maven-javadoc-plugin</artifactId>
  14.108 +                <version>2.9</version>
  14.109 +                <configuration>
  14.110 +                    <skip>true</skip>
  14.111 +                </configuration>
  14.112 +              </plugin>
  14.113 +            <plugin>
  14.114 +                <groupId>org.apache.maven.plugins</groupId>
  14.115 +                <artifactId>maven-compiler-plugin</artifactId>
  14.116 +                <version>2.3.2</version>
  14.117 +                <configuration>
  14.118 +                    <source>1.7</source>
  14.119 +                    <target>1.7</target>
  14.120 +                </configuration>
  14.121 +            </plugin>
  14.122 +          </plugins>
  14.123 +      </pluginManagement>
  14.124 +  </build>
  14.125 +  <dependencyManagement>
  14.126 +      <dependencies>
  14.127 +        <dependency>
  14.128 +          <groupId>org.testng</groupId>
  14.129 +          <artifactId>testng</artifactId>
  14.130 +          <version>6.7</version>
  14.131 +          <scope>test</scope>
  14.132 +          <exclusions>
  14.133 +            <exclusion>
  14.134 +              <artifactId>junit</artifactId>
  14.135 +              <groupId>junit</groupId>
  14.136 +            </exclusion>
  14.137 +          </exclusions>
  14.138 +        </dependency>
  14.139 +        <dependency>
  14.140 +          <groupId>org.netbeans.api</groupId>
  14.141 +          <artifactId>org-netbeans-modules-classfile</artifactId>
  14.142 +          <version>${netbeans.version}</version>
  14.143 +          <type>jar</type>
  14.144 +        </dependency>
  14.145 +        <dependency>
  14.146 +          <groupId>org.netbeans.api</groupId>
  14.147 +          <artifactId>org-openide-util-lookup</artifactId>
  14.148 +          <version>${netbeans.version}</version>
  14.149 +          <scope>compile</scope>
  14.150 +          <type>jar</type>
  14.151 +        </dependency>
  14.152 +        <dependency>
  14.153 +            <groupId>org.netbeans.api</groupId>
  14.154 +            <artifactId>org-netbeans-api-annotations-common</artifactId>
  14.155 +            <version>${netbeans.version}</version>
  14.156 +        </dependency>
  14.157 +        <dependency>
  14.158 +            <groupId>org.netbeans.api</groupId>
  14.159 +            <artifactId>org-netbeans-modules-java-source</artifactId>
  14.160 +            <version>${netbeans.version}</version>
  14.161 +        </dependency>
  14.162 +        <dependency>
  14.163 +            <groupId>org.netbeans.api</groupId>
  14.164 +            <artifactId>org-netbeans-libs-javacapi</artifactId>
  14.165 +            <version>${netbeans.version}</version>
  14.166 +        </dependency>
  14.167 +        <dependency>
  14.168 +            <groupId>org.netbeans.api</groupId>
  14.169 +            <artifactId>org-netbeans-spi-java-hints</artifactId>
  14.170 +            <version>${netbeans.version}</version>
  14.171 +        </dependency>
  14.172 +        <dependency>
  14.173 +            <groupId>org.netbeans.api</groupId>
  14.174 +            <artifactId>org-netbeans-modules-parsing-api</artifactId>
  14.175 +            <version>${netbeans.version}</version>
  14.176 +        </dependency>
  14.177 +        <dependency>
  14.178 +            <groupId>org.netbeans.api</groupId>
  14.179 +            <artifactId>org-netbeans-spi-editor-hints</artifactId>
  14.180 +            <version>${netbeans.version}</version>
  14.181 +        </dependency>
  14.182 +        <dependency>
  14.183 +            <groupId>org.netbeans.api</groupId>
  14.184 +            <artifactId>org-openide-util</artifactId>
  14.185 +            <version>${netbeans.version}</version>
  14.186 +        </dependency>
  14.187 +        <dependency>
  14.188 +            <groupId>org.netbeans.api</groupId>
  14.189 +            <artifactId>org-netbeans-modules-java-lexer</artifactId>
  14.190 +            <version>${netbeans.version}</version>
  14.191 +        </dependency>
  14.192 +        <dependency>
  14.193 +            <groupId>org.netbeans.api</groupId>
  14.194 +            <artifactId>org-netbeans-modules-lexer</artifactId>
  14.195 +            <version>${netbeans.version}</version>
  14.196 +        </dependency>
  14.197 +        <dependency>
  14.198 +            <groupId>org.netbeans.api</groupId>
  14.199 +            <artifactId>org-netbeans-modules-java-hints-test</artifactId>
  14.200 +            <version>${netbeans.version}</version>
  14.201 +        </dependency>
  14.202 +        <dependency>
  14.203 +            <groupId>org.netbeans.api</groupId>
  14.204 +            <artifactId>org-netbeans-libs-junit4</artifactId>
  14.205 +            <version>${netbeans.version}</version>
  14.206 +        </dependency>
  14.207 +        <dependency>
  14.208 +            <groupId>org.netbeans.modules</groupId>
  14.209 +            <artifactId>org-netbeans-lib-nbjavac</artifactId>
  14.210 +            <version>${netbeans.version}</version>
  14.211 +        </dependency>
  14.212 +        <dependency> 
  14.213 +            <groupId>org.netbeans.modules</groupId>
  14.214 +            <artifactId>org-netbeans-modules-web-browser-api</artifactId>
  14.215 +            <version>${netbeans.version}</version>
  14.216 +            <exclusions>
  14.217 +                <exclusion>
  14.218 +                    <artifactId>org-netbeans-core</artifactId>
  14.219 +                    <groupId>org.netbeans.modules</groupId>
  14.220 +                </exclusion>
  14.221 +                <exclusion>
  14.222 +                    <artifactId>org-netbeans-core-multiview</artifactId>
  14.223 +                    <groupId>org.netbeans.api</groupId>
  14.224 +                </exclusion>
  14.225 +                <exclusion>
  14.226 +                    <artifactId>org-netbeans-libs-lucene</artifactId>
  14.227 +                    <groupId>org.netbeans.api</groupId>
  14.228 +                </exclusion>
  14.229 +                <exclusion>
  14.230 +                    <artifactId>org-netbeans-modules-diff</artifactId>
  14.231 +                    <groupId>org.netbeans.api</groupId>
  14.232 +                </exclusion>
  14.233 +                <exclusion>
  14.234 +                    <artifactId>org-netbeans-modules-editor-fold</artifactId>
  14.235 +                    <groupId>org.netbeans.api</groupId>
  14.236 +                </exclusion>
  14.237 +                <exclusion>
  14.238 +                    <artifactId>org-netbeans-modules-editor-guards</artifactId>
  14.239 +                    <groupId>org.netbeans.api</groupId>
  14.240 +                </exclusion>
  14.241 +            </exclusions>
  14.242 +        </dependency>
  14.243 +        <dependency>
  14.244 +            <artifactId>org-netbeans-modules-projectapi</artifactId>
  14.245 +            <groupId>org.netbeans.api</groupId>
  14.246 +            <type>jar</type>
  14.247 +            <version>${netbeans.version}</version>
  14.248 +        </dependency>
  14.249 +      </dependencies>
  14.250 +  </dependencyManagement>
  14.251 +</project>