json/src/main/java/net/java/html/json/Model.java
author Jaroslav Tulach <jtulach@netbeans.org>
Mon, 14 Dec 2015 06:10:54 +0100
changeset 1034 f5d6e09568b6
parent 1024 558934b8b835
permissions -rw-r--r--
Link to frowRaw method of Models class
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package net.java.html.json;
    44 
    45 import java.lang.annotation.ElementType;
    46 import java.lang.annotation.Retention;
    47 import java.lang.annotation.RetentionPolicy;
    48 import java.lang.annotation.Target;
    49 import java.net.URL;
    50 import java.util.List;
    51 
    52 /** Defines a model class that represents a single 
    53  * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a>-like object
    54  * named {@link #className()}. The generated class contains
    55  * getters and setters for properties defined via {@link #properties()} and
    56  * getters for other, derived properties defined by annotating methods
    57  * of this class by {@link ComputedProperty}. Each property
    58  * can be of primitive type, an {@link Enum enum type} or (in order to create 
    59  * nested <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> structure)
    60  * of another {@link Model class generated by @Model} annotation. Each property
    61  * can either represent a single value or be an array of its values.
    62  * <p>
    63  * The {@link #className() generated class}'s <code>toString</code> method
    64  * converts the state of the object into 
    65  * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> format. One can
    66  * use {@link Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream)}
    67  * method to read the JSON text stored in a file or other stream back into the Java model. 
    68  * One can also use {@link OnReceive @OnReceive} annotation to read the model
    69  * asynchronously from a {@link URL}.
    70  * <p>
    71  * An example where one defines class <code>Person</code> with four
    72  * properties (<code>firstName</code>, <code>lastName</code>, array of <code>addresses</code> and
    73  * <code>fullName</code>) follows:
    74  * <pre>
    75  * {@link Model @Model}(className="Person", properties={
    76  *   {@link Property @Property}(name = "firstName", type=String.<b>class</b>),
    77  *   {@link Property @Property}(name = "lastName", type=String.<b>class</b>)
    78  *   {@link Property @Property}(name = "addresses", type=Address.<b>class</b>, array = <b>true</b>)
    79  * })
    80  * <b>static class</b> PersonModel {
    81  *   {@link ComputedProperty @ComputedProperty}
    82  *   <b>static</b> String fullName(String firstName, String lastName) {
    83  *     <b>return</b> firstName + " " + lastName;
    84  *   }
    85  * 
    86  *   {@link ComputedProperty @ComputedProperty}
    87  *   <b>static</b> String mainAddress({@link List List&lt;Address&gt;} addresses) {
    88  *     <b>for</b> (Address a : addresses) {
    89  *       <b>return</b> a.getStreet() + " " + a.getTown();
    90  *     }
    91  *     <b>return</b> "No address";
    92  *   }
    93  * 
    94  *   {@link Model @Model}(className="Address", properties={
    95  *     {@link Property @Property}(name = "street", type=String.<b>class</b>),
    96  *     {@link Property @Property}(name = "town", type=String.<b>class</b>)
    97  *   })
    98  *   <b>static class</b> AddressModel {
    99  *   }
   100  * }
   101  * </pre>
   102  * The generated model class has a default constructor, and also <em>quick
   103  * instantiation</em> constructor that accepts all non-array properties 
   104  * (in the order used in the {@link #properties()} attribute) and vararg list
   105  * for the first array property (if any). One can thus use following code
   106  * to create an instance of the Person and Address classes:
   107  * <pre>
   108  * Person p = <b>new</b> Person("Jaroslav", "Tulach",
   109  *   <b>new</b> Address("Markoušovice", "Úpice"),
   110  *   <b>new</b> Address("V Parku", "Praha")
   111  * );
   112  * // p.toString() then returns equivalent of following <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> object
   113  * {
   114  *   "firstName" : "Jaroslav",
   115  *   "lastName" : "Tulach",
   116  *   "addresses" : [
   117  *     { "street" : "Markoušovice", "town" : "Úpice" },
   118  *     { "street" : "V Parku", "town" : "Praha" },
   119  *   ]
   120  * }
   121  * </pre>
   122  * <p>
   123  * In case you are using <a target="_blank" href="http://knockoutjs.com/">Knockout technology</a>
   124  * for Java then you can associate such model object with surrounding HTML page by
   125  * calling: <code>p.applyBindings();</code> (in case you specify {@link #targetId()}. 
   126  * The page can then use regular
   127  * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> bindings to reference your
   128  * model and create dynamic connection between your model in Java and 
   129  * live DOM structure in the browser:
   130  * </p>
   131  * <pre>
   132  * Name: &lt;span data-bind="text: fullName"&gt;
   133  * &lt;div data-bind="foreach: addresses"&gt;
   134  *   Lives in &lt;span data-bind="text: town"/&gt;
   135  * &lt;/div&gt;
   136  * </pre>
   137  * 
   138  * <h3>Access Raw <a target="_blank" href="http://knockoutjs.com/">Knockout</a> Observables</h3>
   139  * 
   140  * One can obtain <em>raw</em> JavaScript object representing the 
   141  * instance of {@link Model model class} (with appropriate
   142  * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> <b>observable</b> properties)
   143  * by calling {@link Models#toRaw(java.lang.Object) Models.toRaw(p)}. For 
   144  * example here is a way to obtain the value of <code>fullName</code> property
   145  * (inefficient as it switches between Java and JavaScript back and forth, 
   146  * but functional and instructive) via a JavaScript call:
   147  * <pre>
   148  * {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = "raw", javacall = true, body =
   149  *   "return raw.fullName();" // yes, the <a target="_blank" href="http://knockoutjs.com/">Knockout</a> property is a function
   150  * )
   151  * static native String jsFullName(Object raw);
   152  * // and later
   153  * Person p = ...;
   154  * String fullName = jsFullName({@link Models#toRaw(java.lang.Object) Models.toRaw(p)});
   155  * </pre>
   156  * The above shows how to read a value from <a target="_blank" href="http://knockoutjs.com/">Knockout</a>
   157  * observable. There is a way to change the value too:
   158  * One can pass a parameter to the property-function and then
   159  * it acts like a setter (of course not in the case of read only <code>fullName</code> property,
   160  * but for <code>firstName</code> or <code>lastName</code> the setter is
   161  * available). Everything mentioned in this paragraph applies only when 
   162  * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> technology is active
   163  * other technologies may behave differently.
   164  * 
   165  * <h4>Copy to Plain JSON</h4>
   166  * There is a way to pass a value of a Java {@link Model model class} instance 
   167  * by copy and convert 
   168  * the {@link Model the whole object} into plain 
   169  * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a>. Just
   170  * print it as a string and parse it in JavaScript:
   171  * <pre>
   172  * {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = { "txt" }, body =
   173  *   "return JSON.parse(txt);"
   174  * )
   175  * private static native Object parseJSON(String txt);
   176  * 
   177  * public static Object toPlainJSON(Object model) {
   178  *   return parseJSON(model.toString());
   179  * }
   180  * </pre>
   181  * The newly returned instance is a one time copy of the original model and is no longer
   182  * connected to it. The copy based behavior is independent on any 
   183  * particular technology and should work
   184  * in <a target="_blank" href="http://knockoutjs.com/">Knockout</a> as well as other
   185  * technology implementations.
   186  * 
   187  * <h4>References</h4>
   188  * 
   189  * Visit an <a target="_blank" href="http://dew.apidesign.org/dew/#7510833">on-line demo</a>
   190  * to see a histogram driven by the {@link Model} annotation or try 
   191  * a little <a target="_blank" href="http://dew.apidesign.org/dew/#7263102">math test</a>.
   192  *
   193  * @author Jaroslav Tulach
   194  */
   195 @Retention(RetentionPolicy.SOURCE)
   196 @Target(ElementType.TYPE)
   197 public @interface Model {
   198     /** Name of the model class.
   199      * @return valid Java identifier to use as a name of the model class
   200      */
   201     String className();
   202     /** List of properties in the model.
   203      * @return array of property definitions
   204      */
   205     Property[] properties();
   206     
   207     /** The id of an element to bind this model too. If this
   208      * property is specified an <code>applyBindings</code> method
   209      * in the model class is going to be generated which later calls
   210      * {@link Models#applyBindings(java.lang.Object, java.lang.String)}
   211      * with appropriate <code>targetId</code>. If the <code>targetId</code>
   212      * is specified as empty string, <code>null</code> value is passed
   213      * to {@link Models#applyBindings(java.lang.Object, java.lang.String)} method.
   214      * If the <code>targetId</code> is not specified at all, no public
   215      * <code>applyBindings</code> method is generated at all (a change compared
   216      * to previous versions of this API).
   217      * 
   218      * @return an empty string (means apply globally), or ID of a (usually DOM)
   219      *    element to apply this model after calling its generated
   220      *    <code>applyBindings()</code> method to
   221      * @since 1.1
   222      */
   223     String targetId() default "";
   224 
   225     /** Controls whether builder-like setters shall be generated. Once this
   226      * attribute is set, all {@link #properties()} will get a builder like
   227      * setter (takes value of the property and returns <code>this</code>
   228      * so invocations can be chained). When this attribute is specified,
   229      * the non-default constructor isn't generated at all.
   230      * <p>
   231      * Specifying <code>builder="assign"</code>
   232      * and having {@link #properties() properties} <code>name</code> and
   233      * <code>age</code> will generate method: <pre>
   234      * <b>public</b> MyModel assignName(String name) { ... }
   235      * <b>public</b> MyModel assignAge(int age) { ... }
   236      * </pre>
   237      * These methods can then be chained as <pre>
   238      * MyModel m = <b>new</b> MyModel().assignName("name").assignAge(3);
   239      * </pre>
   240      * The <code>builder</code> attribute can be set to empty string <code>""</code> -
   241      * then it is possible that some property names clash with Java keywords.
   242      * It's responsibility of the user to specify valid builder prefix,
   243      * so the generated methods are compilable.
   244      *
   245      * @return the prefix to put before {@link Property property} names when
   246      *   generating their builder methods
   247      * @since 1.3
   248      */
   249     String builder() default "";
   250     
   251     /** Controls keeping of per-instance private state. Sometimes
   252      * the class generated by the {@link Model @Model annotation} needs to
   253      * keep non-public, or non-JSON like state. One can achieve that by
   254      * specifying <code>instance=true</code> when using the annotation. Then
   255      * the generated class gets a pointer to the instance of the annotated
   256      * class (there needs to be default constructor) and all the {@link ModelOperation @ModelOperation},
   257      * {@link Function @Function}, {@link OnPropertyChange @OnPropertyChange}
   258      * and {@link OnReceive @OnReceive} methods may be non-static. The
   259      * instance of the implementation class isn't accessible directly, just
   260      * through calls to above defined (non-static) methods. Example:
   261      * <pre>
   262      * {@link Model @Model}(className="Data", instance=<b>true</b>, properties={
   263      *   {@link Property @Property}(name="message", type=String.<b>class</b>)
   264      * })
   265      * <b>final class</b> DataPrivate {
   266      *   <b>private int</b> count;
   267      * 
   268      *   {@link ModelOperation @ModelOperation} <b>void</b> increment(Data model) {
   269      *     count++;
   270      *   }
   271      * 
   272      *   {@link ModelOperation @ModelOperation} <b>void</b> hello(Data model) {
   273      *     model.setMessage("Hello " + count + " times!");
   274      *   }
   275      * }
   276      * Data data = <b>new</b> Data();
   277      * data.increment();
   278      * data.increment();
   279      * data.increment();
   280      * data.hello();
   281      * <b>assert</b> data.getMessage().equals("Hello 3 times!");
   282      * </pre>
   283      * <p>
   284      * The methods annotated by {@link ComputedProperty} need to remain static, as 
   285      * they are supposed to be <em>pure</em> functions (e.g. depend only on their parameters)
   286      * and shouldn't use any internal state.
   287      * </p>
   288      * <p><b>How do I initialize private values?</b>
   289      * The implementation class (the one annotated by {@link Model @Model} annotation) 
   290      * needs to have accessible default constructor. That constructor is used to 
   291      * create the instance. Obviously such constructor does not have 
   292      * any parameters, so no initialization is possible.
   293      * </p>
   294      * <p>
   295      * Later one can, however, call any {@link ModelOperation @ModelOperation}
   296      * method and pass in additional configuration parameters. In the above 
   297      * example it should be possible add
   298      * </p>
   299      * <pre>
   300      * {@link ModelOperation @ModelOperation} <b>void</b> init(Data model, int count) {
   301      *   <b>this</b>.count = count;
   302      * }
   303      * </pre><p>
   304      * and then one can initialize the model using the <code>init</code> as:
   305      * </p>
   306      * <pre>
   307      * Data data = <b>new</b> Data();
   308      * data.init(2);
   309      * data.increment();
   310      * data.hello();
   311      * <b>assert</b> data.getMessage().equals("Hello 3 times!");
   312      * </pre><p>
   313      * Why there has to be default constructor? Because instances of 
   314      * classes generated by {@link Model @Model annotation} may be constructed
   315      * by the system as 
   316      * {@link Models#fromRaw(net.java.html.BrwsrCtx, java.lang.Class, java.lang.Object) wrappers around existing JavaScript objects}
   317      * - then
   318      * there is nobody to provide additional parameters at construction time.
   319      * </p>
   320      * <p><b>How do I read private values?</b>
   321      * The methods annotated by {@link ModelOperation} must return <code>void</code>
   322      * (as they can run asynchronously) and as such they aren't suitable for
   323      * returning values back to the caller. In case something like that is
   324      * needed, one can use the approach of the <code>hello</code> method - e.g.
   325      * set value of some {@link Property property} that has a getter:
   326      * </p>
   327      * <pre>
   328      * data.hello();
   329      * <b>assert</b> data.getMessage().equals("Hello 3 times!");
   330      * </pre><p>
   331      * Or one can use actor-like callbacks. Define callback interface and
   332      * use it in a {@link ModelOperation @ModelOperation} method:
   333      * </p>
   334      * <pre>
   335      * <b>public interface</b> ReadCount {
   336      *   <b>public void</b> notifyCount(int count);
   337      * }
   338      * {@link ModelOperation @ModelOperation} <b>void</b> readCount(Data model, ReadCount callback) {
   339      *   callback.readCount(<b>this</b>.count);
   340      * }
   341      * Data data = <b>new</b> Data();
   342      * data.init(2);
   343      * data.increment();
   344      * data.readCount(count -&gt; System.out.println("count should be 3: " + count));
   345      * </pre><p>
   346      * The provided lambda-function callback may be invoked immediately 
   347      * or asynchronously as documentation for {@link ModelOperation} 
   348      * annotation describes.
   349      * </p>
   350      * 
   351      * @return <code>true</code> if the model class should keep pointer to
   352      *   instance of the implementation class
   353      * @since 1.3
   354      */
   355     boolean instance() default false;
   356 }