Documenting how to initialize and read private values from the implementation class
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
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]"
29 * The Original Software is NetBeans. The Initial Developer of the Original
30 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
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.
43 package net.java.html.json;
45 import java.lang.annotation.ElementType;
46 import java.lang.annotation.Retention;
47 import java.lang.annotation.RetentionPolicy;
48 import java.lang.annotation.Target;
50 import java.util.List;
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.
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}.
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:
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>)
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;
86 * {@link ComputedProperty @ComputedProperty}
87 * <b>static</b> String mainAddress({@link List List<Address>} addresses) {
88 * <b>for</b> (Address a : addresses) {
89 * <b>return</b> a.getStreet() + " " + a.getTown();
91 * <b>return</b> "No address";
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>)
98 * <b>static class</b> AddressModel {
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:
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")
112 * // p.toString() then returns equivalent of following <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a> object
114 * "firstName" : "Jaroslav",
115 * "lastName" : "Tulach",
117 * { "street" : "Markoušovice", "town" : "Úpice" },
118 * { "street" : "V Parku", "town" : "Praha" },
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:
132 * Name: <span data-bind="text: fullName">
133 * <div data-bind="foreach: addresses">
134 * Lives in <span data-bind="text: town"/>
138 * <h3>Access Raw <a target="_blank" href="http://knockoutjs.com/">Knockout</a> Observables</h3>
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:
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
151 * static native String jsFullName(Object raw);
154 * String fullName = jsFullName({@link Models#toRaw(java.lang.Object) Models.toRaw(p)});
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.
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:
172 * {@link net.java.html.js.JavaScriptBody @JavaScriptBody}(args = { "txt" }, body =
173 * "return JSON.parse(txt);"
175 * private static native Object parseJSON(String txt);
177 * public static Object toPlainJSON(Object model) {
178 * return parseJSON(model.toString());
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.
187 * <h4>References</h4>
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>.
193 * @author Jaroslav Tulach
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
202 /** List of properties in the model.
203 * @return array of property definitions
205 Property[] properties();
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).
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
223 String targetId() default "";
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.
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) { ... }
237 * These methods can then be chained as <pre>
238 * MyModel m = <b>new</b> MyModel().assignName("name").assignAge(3);
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.
245 * @return the prefix to put before {@link Property property} names when
246 * generating their builder methods
249 String builder() default "";
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:
262 * {@link Model @Model}(className="Data", instance=<b>true</b>, properties={
263 * {@link Property @Property}(name="message", type=String.<b>class</b>)
265 * <b>final class</b> DataPrivate {
266 * <b>private int</b> count;
268 * {@link ModelOperation @ModelOperation} <b>void</b> increment(Data model) {
272 * {@link ModelOperation @ModelOperation} <b>void</b> hello(Data model) {
273 * model.setMessage("Hello " + count + " times!");
276 * Data data = <b>new</b> Data();
281 * <b>assert</b> data.getMessage().equals("Hello 3 times!");
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.
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.
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
300 * {@link ModelOperation @ModelOperation} <b>void</b> init(Data model, int count) {
301 * <b>this</b>.count = count;
304 * and then one can initialize the model using the <code>init</code> as:
307 * Data data = <b>new</b> Data();
311 * <b>assert</b> data.getMessage().equals("Hello 3 times!");
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 wrappers around existing JavaScript objects - then
316 * there is nobody to provide additional parameters at construction time.
318 * <p><b>How do I read private values?</b>
319 * The methods annotated by {@link ModelOperation} must return <code>void</code>
320 * (as they can run asynchronously) and as such they aren't suitable for
321 * returning values back to the caller. In case something like that is
322 * needed, one can use the approach of the <code>hello</code> method - e.g.
323 * set value of some {@link Property property} that has a getter:
327 * <b>assert</b> data.getMessage().equals("Hello 3 times!");
329 * Or one can use actor-like callbacks. Define callback interface and
330 * use it in a {@link ModelOperation @ModelOperation} method:
333 * <b>public interface</b> ReadCount {
334 * <b>public void</b> notifyCount(int count);
336 * {@link ModelOperation @ModelOperation} <b>void</b> readCount(Data model, ReadCount callback) {
337 * callback.readCount(<b>this</b>.count);
339 * Data data = <b>new</b> Data();
342 * data.readCount(count -> System.out.println("count should be 3: " + count));
344 * The provided lambda-function callback may be invoked immediately
345 * or asynchronously as documentation for {@link ModelOperation}
346 * annotation describes.
349 * @return <code>true</code> if the model class should keep pointer to
350 * instance of the implementation class
353 boolean instance() default false;