diff -r 2f6f1d20fa7a json/src/main/java/net/java/html/json/Function.java
--- a/json/src/main/java/net/java/html/json/Function.java Wed Dec 02 08:44:31 2015 +0100
+++ b/json/src/main/java/net/java/html/json/Function.java Mon Dec 07 23:26:27 2015 +0100
@@ -52,7 +52,8 @@
/** Methods in class annotated by {@link Model} can be
* annotated by this annotation to signal that they should be available
* as functions to users of the model classes. The method
- * should be non-private, static and return void.
+ * should be non-private, static (unless {@link Model#instance() instance mode} is on)
+ * and return void.
* It may take few arguments. The first argument can be the type of
* the associated model class, the other argument can be of any type,
* but has to be named data - this one represents the
diff -r 2f6f1d20fa7a json/src/main/java/net/java/html/json/Model.java
--- a/json/src/main/java/net/java/html/json/Model.java Wed Dec 02 08:44:31 2015 +0100
+++ b/json/src/main/java/net/java/html/json/Model.java Mon Dec 07 23:26:27 2015 +0100
@@ -247,4 +247,46 @@
* @since 1.3
*/
String builder() default "";
+
+ /** Controls keeping of per-instance private state. Sometimes
+ * the class generated by the {@link Model @Model annotation} needs to
+ * keep non-public, or non-JSON like state. One can achieve that by
+ * specifying instance=true when using the annotation. Then
+ * the generated class gets a pointer to the instance of the annotated
+ * class (there needs to be default constructor) and all the {@link ModelOperation @ModelOperation},
+ * {@link Function @Function}, {@link OnPropertyChange @OnPropertyChange}
+ * and {@link OnReceive @OnReceive} methods may be non-static. The
+ * instance of the implementation class isn't accessible directly, just
+ * through calls to above defined (non-static) methods. Example:
+ *
+ * The methods annotated by {@link ComputedProperty} need to remain static, as
+ * they are supposed to be pure functions (e.g. depend only on their parameters)
+ * and shouldn't use any internal state.
+ *
+ * @return true if the model class should keep pointer to
+ * instance of the implementation class
+ * @since 1.3
+ */
+ boolean instance() default false;
}
diff -r 2f6f1d20fa7a json/src/main/java/net/java/html/json/ModelOperation.java
--- a/json/src/main/java/net/java/html/json/ModelOperation.java Wed Dec 02 08:44:31 2015 +0100
+++ b/json/src/main/java/net/java/html/json/ModelOperation.java Mon Dec 07 23:26:27 2015 +0100
@@ -53,7 +53,8 @@
*
* A method in a class annotated by {@link Model @Model} annotation may be
* annotated by {@link ModelOperation @ModelOperation}. The method has
- * to be static, non-private and return void. The first parameter
+ * to be static (unless {@link Model#instance() instance mode} is on),
+ * non-private and return void. The first parameter
* of the method must be the {@link Model#className() model class} followed
* by any number of additional arguments.
*
diff -r 2f6f1d20fa7a json/src/main/java/net/java/html/json/OnPropertyChange.java
--- a/json/src/main/java/net/java/html/json/OnPropertyChange.java Wed Dec 02 08:44:31 2015 +0100
+++ b/json/src/main/java/net/java/html/json/OnPropertyChange.java Mon Dec 07 23:26:27 2015 +0100
@@ -74,7 +74,8 @@
* }
*
* The method's first argument should be the instance of the
- * associated {@link Model model class}.
+ * associated {@link Model model class}. The method shall be non-private
+ * and unless {@link Model#instance() instance mode} is on also static.
* There can be an optional second {@link String} argument which will be set
* to the name of the changed property. The second argument is only useful when
* a single method reacts to changes in multiple properties.
diff -r 2f6f1d20fa7a json/src/main/java/net/java/html/json/OnReceive.java
--- a/json/src/main/java/net/java/html/json/OnReceive.java Wed Dec 02 08:44:31 2015 +0100
+++ b/json/src/main/java/net/java/html/json/OnReceive.java Mon Dec 07 23:26:27 2015 +0100
@@ -102,6 +102,8 @@
* One can use this method to communicate with the server
* via WebSocket protocol since version 0.6.
* Read the tutorial to see how.
+ * The method shall be non-private
+ * and unless {@link Model#instance() instance mode} is on also static.
*
* Visit an on-line demo
* to see REST access via {@link OnReceive} annotation.
diff -r 2f6f1d20fa7a json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
--- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Wed Dec 02 08:44:31 2015 +0100
+++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Mon Dec 07 23:26:27 2015 +0100
@@ -223,11 +223,36 @@
try {
w.append("package " + pkg + ";\n");
w.append("import net.java.html.json.*;\n");
- final String inPckName = inPckName(e);
+ final String inPckName = inPckName(e, false);
w.append("/** Generated for {@link ").append(inPckName).append("}*/\n");
w.append("public final class ").append(className).append(" implements Cloneable {\n");
w.append(" private static Class<").append(inPckName).append("> modelFor() { return ").append(inPckName).append(".class; }\n");
w.append(" private static final Html4JavaType TYPE = new Html4JavaType();\n");
+ if (m.instance()) {
+ int cCnt = 0;
+ for (Element c : e.getEnclosedElements()) {
+ if (c.getKind() != ElementKind.CONSTRUCTOR) {
+ continue;
+ }
+ cCnt++;
+ ExecutableElement ec = (ExecutableElement) c;
+ if (ec.getParameters().size() > 0) {
+ continue;
+ }
+ if (ec.getModifiers().contains(Modifier.PRIVATE)) {
+ continue;
+ }
+ cCnt = 0;
+ break;
+ }
+ if (cCnt > 0) {
+ ok = false;
+ error("Needs non-private default constructor when instance=true", e);
+ w.append(" private final ").append(inPckName).append(" instance = null;\n");
+ } else {
+ w.append(" private final ").append(inPckName).append(" instance = new ").append(inPckName).append("();\n");
+ }
+ }
w.append(" private final org.netbeans.html.json.spi.Proto proto;\n");
w.append(body.toString());
w.append(" private ").append(className).append("(net.java.html.BrwsrCtx context) {\n");
@@ -382,7 +407,13 @@
if (param instanceof ExecutableElement) {
ExecutableElement ee = (ExecutableElement)param;
w.append(" case " + (i / 2) + ":\n");
- w.append(" ").append(((TypeElement)e).getQualifiedName()).append(".").append(name).append("(");
+ w.append(" ");
+ if (m.instance()) {
+ w.append("model.instance");
+ } else {
+ w.append(((TypeElement)e).getQualifiedName());
+ }
+ w.append(".").append(name).append("(");
w.append(wrapParams(ee, null, className, "model", "ev", "data"));
w.append(");\n");
w.append(" return;\n");
@@ -939,6 +970,7 @@
Element clazz, StringWriter body, String className,
List extends Element> enclosedElements, List