| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package net.java.html.json; |
| |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.annotation.Target; |
| import java.net.URL; |
| import java.util.List; |
| |
| /** Defines a model class that represents a single |
| * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a>-like object |
| * named {@link #className()}. The generated class contains |
| * getters and setters for properties defined via {@link #properties()} and |
| * getters for other, derived properties defined by annotating methods |
| * of this class by {@link ComputedProperty}. Each property |
| * can be of primitive type, an {@link Enum enum type} or (in order to create |
| * nested <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> structure) |
| * of another {@link Model class generated by @Model} annotation. Each property |
| * can either represent a single value or be an array of its values. |
| * <p> |
| * The {@link #className() generated class}'s <code>toString</code> method |
| * converts the state of the object into |
| * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> format. One can |
| * use {@link Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream)} |
| * method to read the JSON text stored in a file or other stream back into the Java model. |
| * One can also use {@link OnReceive @OnReceive} annotation to read the model |
| * asynchronously from a {@link URL}. |
| * <p> |
| * An example where one defines class <code>Person</code> with four |
| * properties (<code>firstName</code>, <code>lastName</code>, array of <code>addresses</code> and |
| * <code>fullName</code>) follows: |
| * <pre> |
| * {@link Model @Model}(className="Person", properties={ |
| * {@link Property @Property}(name = "firstName", type=String.<b>class</b>), |
| * {@link Property @Property}(name = "lastName", type=String.<b>class</b>) |
| * {@link Property @Property}(name = "addresses", type=Address.<b>class</b>, array = <b>true</b>) |
| * }) |
| * <b>static class</b> PersonModel { |
| * {@link ComputedProperty @ComputedProperty} |
| * <b>static</b> String fullName(String firstName, String lastName) { |
| * <b>return</b> firstName + " " + lastName; |
| * } |
| * |
| * {@link ComputedProperty @ComputedProperty} |
| * <b>static</b> String mainAddress({@link List List<Address>} addresses) { |
| * <b>for</b> (Address a : addresses) { |
| * <b>return</b> a.getStreet() + " " + a.getTown(); |
| * } |
| * <b>return</b> "No address"; |
| * } |
| * |
| * {@link Model @Model}(className="Address", properties={ |
| * {@link Property @Property}(name = "street", type=String.<b>class</b>), |
| * {@link Property @Property}(name = "town", type=String.<b>class</b>) |
| * }) |
| * <b>static class</b> AddressModel { |
| * } |
| * } |
| * </pre> |
| * The generated model class has a default constructor, and also <em>quick |
| * instantiation</em> constructor that accepts all non-array properties |
| * (in the order used in the {@link #properties()} attribute) and vararg list |
| * for the first array property (if any). One can thus use following code |
| * to create an instance of the Person and Address classes: |
| * <pre> |
| * Person p = <b>new</b> Person("Jaroslav", "Tulach", |
| * <b>new</b> Address("Markoušovice", "Úpice"), |
| * <b>new</b> Address("V Parku", "Praha") |
| * ); |
| * // p.toString() then returns equivalent of following <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> object |
| * { |
| * "firstName" : "Jaroslav", |
| * "lastName" : "Tulach", |
| * "addresses" : [ |
| * { "street" : "Markoušovice", "town" : "Úpice" }, |
| * { "street" : "V Parku", "town" : "Praha" }, |
| * ] |
| * } |
| * </pre> |
| * <p> |
| * In case you are using <a target="_blank" href="http://knockoutjs.com/">Knockout technology</a> |
| * for Java then you can associate such model object with surrounding HTML page by |
| * calling: <code>p.applyBindings();</code> (in case you specify {@link #targetId()}. |
| * The page can then use regular |
| * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> bindings to reference your |
| * model and create dynamic connection between your model in Java and |
| * live DOM structure in the browser: |
| * </p> |
| * <pre> |
| * Name: <span data-bind="text: fullName"> |
| * <div data-bind="foreach: addresses"> |
| * Lives in <span data-bind="text: town"/> |
| * </div> |
| * </pre> |
| * |
| * <p> |
| * <b>Access Raw <a target="_blank" href="http://knockoutjs.com/">Knockout</a> Observables</b> |
| * </p> |
| * |
| * <p> |
| * One can obtain <em>raw</em> JavaScript object representing the |
| * instance of {@link Model model class} (with appropriate |
| * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> <b>observable</b> properties) |
| * by calling {@link Models#toRaw(java.lang.Object) Models.toRaw(p)}. For |
| * example here is a way to obtain the value of <code>fullName</code> property |
| * (inefficient as it switches between Java and JavaScript back and forth, |
| * but functional and instructive) via a JavaScript call: |
| * </p> |
| * <pre> |
| * {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = "raw", javacall = true, body = |
| * "return raw.fullName();" // yes, the <a target="_blank" href="http://knockoutjs.com/">Knockout</a> property is a function |
| * ) |
| * static native String jsFullName(Object raw); |
| * // and later |
| * Person p = ...; |
| * String fullName = jsFullName({@link Models#toRaw(java.lang.Object) Models.toRaw(p)}); |
| * </pre> |
| * <p> |
| * The above shows how to read a value from <a target="_blank" href="http://knockoutjs.com/">Knockout</a> |
| * observable. There is a way to change the value too: |
| * One can pass a parameter to the property-function and then |
| * it acts like a setter (of course not in the case of read only <code>fullName</code> property, |
| * but for <code>firstName</code> or <code>lastName</code> the setter is |
| * available). Everything mentioned in this paragraph applies only when |
| * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> technology is active |
| * other technologies may behave differently. |
| * </p> |
| * |
| * <p> |
| * <b>Copy to Plain JSON</b> |
| * </p> |
| * |
| * <p> |
| * There is a way to pass a value of a Java {@link Model model class} instance |
| * by copy and convert |
| * the {@link Model the whole object} into plain |
| * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a>. Just |
| * print it as a string and parse it in JavaScript: |
| * </p> |
| * <pre> |
| * {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = { "txt" }, body = |
| * "return JSON.parse(txt);" |
| * ) |
| * private static native Object parseJSON(String txt); |
| * |
| * public static Object toPlainJSON(Object model) { |
| * return parseJSON(model.toString()); |
| * } |
| * </pre> |
| * <p> |
| * The newly returned instance is a one time copy of the original model and is no longer |
| * connected to it. The copy based behavior is independent on any |
| * particular technology and should work |
| * in <a target="_blank" href="http://knockoutjs.com/">Knockout</a> as well as other |
| * technology implementations. |
| * </p> |
| * |
| * <p> |
| * <b>References</b> |
| * </p> |
| * |
| * Visit an <a target="_blank" href="http://dew.apidesign.org/dew/#7510833">on-line demo</a> |
| * to see a histogram driven by the {@link Model} annotation or try |
| * a little <a target="_blank" href="http://dew.apidesign.org/dew/#7263102">math test</a>. |
| * |
| * @author Jaroslav Tulach |
| */ |
| @Retention(RetentionPolicy.SOURCE) |
| @Target(ElementType.TYPE) |
| public @interface Model { |
| /** Name of the model class. |
| * @return valid Java identifier to use as a name of the model class |
| */ |
| String className(); |
| /** List of properties in the model. |
| * @return array of property definitions |
| */ |
| Property[] properties(); |
| |
| /** The id of an element to bind this model too. If this |
| * property is specified an <code>applyBindings</code> method |
| * in the model class is going to be generated which later calls |
| * {@link Models#applyBindings(java.lang.Object, java.lang.String)} |
| * with appropriate <code>targetId</code>. If the <code>targetId</code> |
| * is specified as empty string, <code>null</code> value is passed |
| * to {@link Models#applyBindings(java.lang.Object, java.lang.String)} method. |
| * If the <code>targetId</code> is not specified at all, no public |
| * <code>applyBindings</code> method is generated at all (a change compared |
| * to previous versions of this API). |
| * |
| * @return an empty string (means apply globally), or ID of a (usually DOM) |
| * element to apply this model after calling its generated |
| * <code>applyBindings()</code> method to |
| * @since 1.1 |
| */ |
| String targetId() default ""; |
| |
| /** Controls whether builder-like setters shall be generated. Once this |
| * attribute is set, all {@link #properties()} will get a builder like |
| * setter (takes value of the property and returns <code>this</code> |
| * so invocations can be chained). When this attribute is specified, |
| * the non-default constructor isn't generated at all. |
| * <p> |
| * Specifying <code>builder="assign"</code> |
| * and having {@link #properties() properties} <code>name</code> and |
| * <code>age</code> will generate method: <pre> |
| * <b>public</b> MyModel assignName(String name) { ... } |
| * <b>public</b> MyModel assignAge(int age) { ... } |
| * </pre> |
| * These methods can then be chained as <pre> |
| * MyModel m = <b>new</b> MyModel().assignName("name").assignAge(3); |
| * </pre> |
| * The <code>builder</code> attribute can be set to empty string <code>""</code> - |
| * then it is possible that some property names clash with Java keywords. |
| * It's responsibility of the user to specify valid builder prefix, |
| * so the generated methods are compilable. |
| * |
| * @return the prefix to put before {@link Property property} names when |
| * generating their builder methods |
| * @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 <code>instance=true</code> 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: |
| * <pre> |
| * {@link Model @Model}(className="Data", instance=<b>true</b>, properties={ |
| * {@link Property @Property}(name="message", type=String.<b>class</b>) |
| * }) |
| * <b>final class</b> DataPrivate { |
| * <b>private int</b> count; |
| * |
| * {@link ModelOperation @ModelOperation} <b>void</b> increment(Data model) { |
| * count++; |
| * } |
| * |
| * {@link ModelOperation @ModelOperation} <b>void</b> hello(Data model) { |
| * model.setMessage("Hello " + count + " times!"); |
| * } |
| * } |
| * Data data = <b>new</b> Data(); |
| * data.increment(); |
| * data.increment(); |
| * data.increment(); |
| * data.hello(); |
| * <b>assert</b> data.getMessage().equals("Hello 3 times!"); |
| * </pre> |
| * <p> |
| * The methods annotated by {@link ComputedProperty} need to remain static, as |
| * they are supposed to be <em>pure</em> functions (e.g. depend only on their parameters) |
| * and shouldn't use any internal state. |
| * </p> |
| * <p><b>How do I initialize private values?</b> |
| * The implementation class (the one annotated by {@link Model @Model} annotation) |
| * needs to have accessible default constructor. That constructor is used to |
| * create the instance. Obviously such constructor does not have |
| * any parameters, so no initialization is possible. |
| * </p> |
| * <p> |
| * Later one can, however, call any {@link ModelOperation @ModelOperation} |
| * method and pass in additional configuration parameters. In the above |
| * example it should be possible add |
| * </p> |
| * <pre> |
| * {@link ModelOperation @ModelOperation} <b>void</b> init(Data model, int count) { |
| * <b>this</b>.count = count; |
| * } |
| * </pre><p> |
| * and then one can initialize the model using the <code>init</code> as: |
| * </p> |
| * <pre> |
| * Data data = <b>new</b> Data(); |
| * data.init(2); |
| * data.increment(); |
| * data.hello(); |
| * <b>assert</b> data.getMessage().equals("Hello 3 times!"); |
| * </pre><p> |
| * Why there has to be default constructor? Because instances of |
| * classes generated by {@link Model @Model annotation} may be constructed |
| * by the system as |
| * {@link Models#fromRaw(net.java.html.BrwsrCtx, java.lang.Class, java.lang.Object) wrappers around existing JavaScript objects} |
| * - then |
| * there is nobody to provide additional parameters at construction time. |
| * </p> |
| * <p><b>How do I read private values?</b> |
| * The methods annotated by {@link ModelOperation} must return <code>void</code> |
| * (as they can run asynchronously) and as such they aren't suitable for |
| * returning values back to the caller. In case something like that is |
| * needed, one can use the approach of the <code>hello</code> method - e.g. |
| * set value of some {@link Property property} that has a getter: |
| * </p> |
| * <pre> |
| * data.hello(); |
| * <b>assert</b> data.getMessage().equals("Hello 3 times!"); |
| * </pre><p> |
| * Or one can use actor-like callbacks. Define callback interface and |
| * use it in a {@link ModelOperation @ModelOperation} method: |
| * </p> |
| * <pre> |
| * <b>public interface</b> ReadCount { |
| * <b>public void</b> notifyCount(int count); |
| * } |
| * {@link ModelOperation @ModelOperation} <b>void</b> readCount(Data model, ReadCount callback) { |
| * callback.readCount(<b>this</b>.count); |
| * } |
| * Data data = <b>new</b> Data(); |
| * data.init(2); |
| * data.increment(); |
| * data.readCount(count -> System.out.println("count should be 3: " + count)); |
| * </pre><p> |
| * The provided lambda-function callback may be invoked immediately |
| * or asynchronously as documentation for {@link ModelOperation} |
| * annotation describes. |
| * </p> |
| * |
| * @return <code>true</code> if the model class should keep pointer to |
| * instance of the implementation class |
| * @since 1.3 |
| */ |
| boolean instance() default false; |
| } |