1.1 --- a/json/src/main/java/net/java/html/json/Model.java Sun Nov 01 16:59:42 2015 +0100
1.2 +++ b/json/src/main/java/net/java/html/json/Model.java Thu Nov 05 23:38:18 2015 +0100
1.3 @@ -48,7 +48,6 @@
1.4 import java.lang.annotation.Target;
1.5 import java.net.URL;
1.6 import java.util.List;
1.7 -import org.netbeans.html.json.spi.Technology;
1.8
1.9 /** Defines a model class that represents a single
1.10 * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a>-like object
1.11 @@ -222,4 +221,30 @@
1.12 * @since 1.1
1.13 */
1.14 String targetId() default "";
1.15 +
1.16 + /** Controls whether builder-like setters shall be generated. Once this
1.17 + * attribute is set, all {@link #properties()} will get a builder like
1.18 + * setter (takes value of the property and returns <code>this</code>
1.19 + * so invocations can be chained). When this attribute is specified,
1.20 + * the non-default constructor isn't generated at all.
1.21 + * <p>
1.22 + * Specifying <code>builder="assign"</code>
1.23 + * and having {@link #properties() properties} <code>name</code> and
1.24 + * <code>age</code> will generate method: <pre>
1.25 + * <b>public</b> MyModel assignName(String name) { ... }
1.26 + * <b>public</b> MyModel assignAge(int age) { ... }
1.27 + * </pre>
1.28 + * These methods can then be chained as <pre>
1.29 + * MyModel m = <b>new</b> MyModel().assignName("name").assignAge(3);
1.30 + * </pre>
1.31 + * The <code>builder</code> attribute can be set to empty string <code>""</code> -
1.32 + * then it is possible that some property names clash with Java keywords.
1.33 + * It's responsibility of the user to specify valid builder prefix,
1.34 + * so the generated methods are compilable.
1.35 + *
1.36 + * @return the prefix to put before {@link Property property} names when
1.37 + * generating their builder methods
1.38 + * @since 1.3
1.39 + */
1.40 + String builder() default "";
1.41 }
2.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Sun Nov 01 16:59:42 2015 +0100
2.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Thu Nov 05 23:38:18 2015 +0100
2.3 @@ -189,6 +189,7 @@
2.4 Map<String, Collection<String[]>> propsDeps = new HashMap<String, Collection<String[]>>();
2.5 Map<String, Collection<String>> functionDeps = new HashMap<String, Collection<String>>();
2.6 Prprt[] props = createProps(e, m.properties());
2.7 + final String builderPrefix = findBuilderPrefix(e, m);
2.8
2.9 if (!generateComputedProperties(className, body, props, e.getEnclosedElements(), propsGetSet, propsDeps)) {
2.10 ok = false;
2.11 @@ -196,7 +197,7 @@
2.12 if (!generateOnChange(e, propsDeps, props, className, functionDeps)) {
2.13 ok = false;
2.14 }
2.15 - if (!generateProperties(e, body, className, props, propsGetSet, propsDeps, functionDeps)) {
2.16 + if (!generateProperties(e, builderPrefix, body, className, props, propsGetSet, propsDeps, functionDeps)) {
2.17 ok = false;
2.18 }
2.19 if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
2.20 @@ -271,7 +272,7 @@
2.21 }
2.22 }
2.23 w.append(" };\n");
2.24 - if (props.length > 0) {
2.25 + if (props.length > 0 && builderPrefix == null) {
2.26 StringBuilder constructorWithArguments = new StringBuilder();
2.27 constructorWithArguments.append(" public ").append(className).append("(");
2.28 Prprt firstArray = null;
2.29 @@ -462,8 +463,13 @@
2.30 w.append(type).append(".valueOf(TYPE.stringValue(e)));\n");
2.31 } else {
2.32 if (isPrimitive(type)) {
2.33 - w.append(" this.prop_").append(pn).append(".add(TYPE.numberValue(e).");
2.34 - w.append(type).append("Value());\n");
2.35 + if (type.equals("char")) {
2.36 + w.append(" this.prop_").append(pn).append(".add((char)TYPE.numberValue(e).");
2.37 + w.append("intValue());\n");
2.38 + } else {
2.39 + w.append(" this.prop_").append(pn).append(".add(TYPE.numberValue(e).");
2.40 + w.append(type).append("Value());\n");
2.41 + }
2.42 } else {
2.43 w.append(" this.prop_").append(pn).append(".add((");
2.44 w.append(type).append(")e);\n");
2.45 @@ -561,8 +567,29 @@
2.46 return ok;
2.47 }
2.48
2.49 + private static String findBuilderPrefix(Element e, Model m) {
2.50 + if (!m.builder().isEmpty()) {
2.51 + return m.builder();
2.52 + }
2.53 + for (AnnotationMirror am : e.getAnnotationMirrors()) {
2.54 + for (Map.Entry<? extends Object, ? extends Object> entry : am.getElementValues().entrySet()) {
2.55 + if ("builder()".equals(entry.getKey().toString())) {
2.56 + return "";
2.57 + }
2.58 + }
2.59 + }
2.60 + return null;
2.61 + }
2.62 +
2.63 + private static String builderMethod(String builderPrefix, Prprt p) {
2.64 + if (builderPrefix.isEmpty()) {
2.65 + return p.name();
2.66 + }
2.67 + return builderPrefix + Character.toUpperCase(p.name().charAt(0)) + p.name().substring(1);
2.68 + }
2.69 +
2.70 private boolean generateProperties(
2.71 - Element where,
2.72 + Element where, String builderPrefix,
2.73 Writer w, String className, Prprt[] properties,
2.74 List<GetSet> props,
2.75 Map<String,Collection<String[]>> deps,
2.76 @@ -583,6 +610,17 @@
2.77 w.write(" proto.accessProperty(\"" + p.name() + "\");\n");
2.78 w.write(" return prop_" + p.name() + ";\n");
2.79 w.write(" }\n");
2.80 + if (builderPrefix != null) {
2.81 + boolean[] isModel = {false};
2.82 + boolean[] isEnum = {false};
2.83 + boolean isPrimitive[] = {false};
2.84 + String ret = checkType(p, isModel, isEnum, isPrimitive);
2.85 + w.write(" public " + className + " " + builderMethod(builderPrefix, p) + "(" + ret + "... v) {\n");
2.86 + w.write(" proto.accessProperty(\"" + p.name() + "\");\n");
2.87 + w.append(" TYPE.replaceValue(prop_").append(p.name()).append(", " + tn + ".class, v);\n");
2.88 + w.write(" return this;\n");
2.89 + w.write(" }\n");
2.90 + }
2.91 } else {
2.92 castTo = tn;
2.93 boolean isModel[] = { false };
2.94 @@ -623,6 +661,12 @@
2.95 }
2.96 }
2.97 w.write(" }\n");
2.98 + if (builderPrefix != null) {
2.99 + w.write(" public " + className + " " + builderMethod(builderPrefix, p) + "(" + tn + " v) {\n");
2.100 + w.write(" " + gs[1] + "(v);\n");
2.101 + w.write(" return this;\n");
2.102 + w.write(" }\n");
2.103 + }
2.104 }
2.105
2.106 for (int i = 0; i < props.size(); i++) {
3.1 --- a/json/src/main/java/org/netbeans/html/json/spi/Proto.java Sun Nov 01 16:59:42 2015 +0100
3.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/Proto.java Thu Nov 05 23:38:18 2015 +0100
3.3 @@ -875,15 +875,41 @@
3.4 * @since 1.0
3.5 */
3.6 public final <T> void replaceValue(Collection<? super T> arr, Class<T> type, Object value) {
3.7 - Object[] newArr;
3.8 + List<T> tmp = new ArrayList<T>();
3.9 if (value instanceof Object[]) {
3.10 - newArr = (Object[]) value;
3.11 + for (Object e : (Object[]) value) {
3.12 + tmp.add(extractValue(type, e));
3.13 + }
3.14 + } else if (value instanceof byte[]) {
3.15 + for (Object e : (byte[]) value) {
3.16 + tmp.add(extractValue(type, e));
3.17 + }
3.18 + } else if (value instanceof short[]) {
3.19 + for (Object e : (short[]) value) {
3.20 + tmp.add(extractValue(type, e));
3.21 + }
3.22 + } else if (value instanceof int[]) {
3.23 + for (Object e : (int[]) value) {
3.24 + tmp.add(extractValue(type, e));
3.25 + }
3.26 + } else if (value instanceof char[]) {
3.27 + for (Object e : (char[]) value) {
3.28 + tmp.add(extractValue(type, e));
3.29 + }
3.30 + } else if (value instanceof long[]) {
3.31 + for (Object e : (long[]) value) {
3.32 + tmp.add(extractValue(type, e));
3.33 + }
3.34 + } else if (value instanceof float[]) {
3.35 + for (Object e : (float[]) value) {
3.36 + tmp.add(extractValue(type, e));
3.37 + }
3.38 + } else if (value instanceof double[]) {
3.39 + for (Object e : (double[]) value) {
3.40 + tmp.add(extractValue(type, e));
3.41 + }
3.42 } else {
3.43 - newArr = new Object[] { value };
3.44 - }
3.45 - List<T> tmp = new ArrayList<T>(newArr.length);
3.46 - for (Object e : newArr) {
3.47 - tmp.add(extractValue(type, e));
3.48 + tmp.add(extractValue(type, value));
3.49 }
3.50 if (arr instanceof JSONList) {
3.51 JSONList jsList = (JSONList) arr;
4.1 --- a/json/src/test/java/net/java/html/json/ModelTest.java Sun Nov 01 16:59:42 2015 +0100
4.2 +++ b/json/src/test/java/net/java/html/json/ModelTest.java Thu Nov 05 23:38:18 2015 +0100
4.3 @@ -65,7 +65,7 @@
4.4 *
4.5 * @author Jaroslav Tulach
4.6 */
4.7 -@Model(className = "Modelik", targetId = "", properties = {
4.8 +@Model(className = "Modelik", builder = "change", targetId = "", properties = {
4.9 @Property(name = "value", type = int.class),
4.10 @Property(name = "count", type = int.class),
4.11 @Property(name = "unrelated", type = long.class),
4.12 @@ -97,8 +97,18 @@
4.13 }
4.14
4.15 @Test public void equalsAndHashCode() {
4.16 - Modelik m1 = new Modelik(10, 20, 30, "changed", "firstName");
4.17 - Modelik m2 = new Modelik(10, 20, 30, "changed", "firstName");
4.18 + Modelik m1 = new Modelik();
4.19 + m1.setValue(10);
4.20 + m1.setCount(20);
4.21 + m1.setUnrelated(30);
4.22 + m1.setChangedProperty("changed");
4.23 + m1.getNames().add("firstName");
4.24 + Modelik m2 = new Modelik().
4.25 + changeValue(10).
4.26 + changeCount(20).
4.27 + changeUnrelated(30).
4.28 + changeChangedProperty("changed").
4.29 + changeNames("firstName");
4.30
4.31 assertTrue(m1.equals(m2), "They are the same");
4.32 assertEquals(m1.hashCode(), m2.hashCode(), "Hashcode is the same");
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/BuilderTest.java Thu Nov 05 23:38:18 2015 +0100
5.3 @@ -0,0 +1,117 @@
5.4 +/**
5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5.6 + *
5.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
5.8 + *
5.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
5.10 + * Other names may be trademarks of their respective owners.
5.11 + *
5.12 + * The contents of this file are subject to the terms of either the GNU
5.13 + * General Public License Version 2 only ("GPL") or the Common
5.14 + * Development and Distribution License("CDDL") (collectively, the
5.15 + * "License"). You may not use this file except in compliance with the
5.16 + * License. You can obtain a copy of the License at
5.17 + * http://www.netbeans.org/cddl-gplv2.html
5.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
5.19 + * specific language governing permissions and limitations under the
5.20 + * License. When distributing the software, include this License Header
5.21 + * Notice in each file and include the License file at
5.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
5.23 + * particular file as subject to the "Classpath" exception as provided
5.24 + * by Oracle in the GPL Version 2 section of the License file that
5.25 + * accompanied this code. If applicable, add the following below the
5.26 + * License Header, with the fields enclosed by brackets [] replaced by
5.27 + * your own identifying information:
5.28 + * "Portions Copyrighted [year] [name of copyright owner]"
5.29 + *
5.30 + * Contributor(s):
5.31 + *
5.32 + * The Original Software is NetBeans. The Initial Developer of the Original
5.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
5.34 + *
5.35 + * If you wish your version of this file to be governed by only the CDDL
5.36 + * or only the GPL Version 2, indicate your decision by adding
5.37 + * "[Contributor] elects to include this software in this distribution
5.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
5.39 + * single choice of license, a recipient has the option to distribute
5.40 + * your version of this file under either the CDDL, the GPL Version 2 or
5.41 + * to extend the choice of license to its licensees as provided above.
5.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
5.43 + * Version 2 license, then the option applies only if the new code is
5.44 + * made subject to such option by the copyright holder.
5.45 + */
5.46 +package org.netbeans.html.json.impl;
5.47 +
5.48 +import java.lang.reflect.Constructor;
5.49 +import net.java.html.json.Model;
5.50 +import net.java.html.json.Property;
5.51 +import static org.testng.Assert.assertEquals;
5.52 +import org.testng.annotations.Test;
5.53 +
5.54 +@Model(className="Builder", builder = "", properties = {
5.55 + @Property(name="bytes", type = byte.class, array = true),
5.56 + @Property(name="chars", type = char.class, array = true),
5.57 + @Property(name="shorts", type = short.class, array = true),
5.58 + @Property(name="ints", type = int.class, array = true),
5.59 + @Property(name="longs", type = long.class, array = true),
5.60 + @Property(name="floats", type = float.class, array = true),
5.61 + @Property(name="doubles", type = double.class, array = true),
5.62 + @Property(name="strings", type = String.class, array = true),
5.63 +})
5.64 +public class BuilderTest {
5.65 + @Test
5.66 + public void onlyDefaultClassLoader() {
5.67 + Constructor<?>[] arr = Builder.class.getConstructors();
5.68 + assertEquals(arr.length, 1, "One constructor");
5.69 + assertEquals(arr[0].getParameterTypes().length, 0, "No parameters");
5.70 + }
5.71 +
5.72 + @Test
5.73 + public void assignBytes() {
5.74 + Builder b = new Builder().bytes((byte)10, (byte)20, (byte)30);
5.75 + assertEquals(b.getBytes().size(), 3);
5.76 + assertEquals(b.getBytes().get(0).byteValue(), (byte)10);
5.77 + }
5.78 + @Test
5.79 + public void assignChars() {
5.80 + Builder b = new Builder().chars((char)10, (char)20, (char)30);
5.81 + assertEquals(b.getChars().size(), 3);
5.82 + assertEquals(b.getChars().get(0).charValue(), 10);
5.83 + }
5.84 + @Test
5.85 + public void assignShort() {
5.86 + Builder b = new Builder().shorts((short)10, (short)20, (short)30);
5.87 + assertEquals(b.getShorts().size(), 3);
5.88 + assertEquals(b.getShorts().get(0).intValue(), 10);
5.89 + }
5.90 + @Test
5.91 + public void assignInts() {
5.92 + Builder b = new Builder().ints(10, 20, 30);
5.93 + assertEquals(b.getInts().size(), 3);
5.94 + assertEquals(b.getInts().get(0).intValue(), 10);
5.95 + }
5.96 + @Test
5.97 + public void assignLongs() {
5.98 + Builder b = new Builder().longs(10, 20, 30);
5.99 + assertEquals(b.getLongs().size(), 3);
5.100 + assertEquals(b.getLongs().get(1).intValue(), 20);
5.101 + }
5.102 + @Test
5.103 + public void assignDouble() {
5.104 + Builder b = new Builder().doubles(10, 20, 30);
5.105 + assertEquals(b.getDoubles().size(), 3);
5.106 + assertEquals(b.getDoubles().get(0), 10.0);
5.107 + }
5.108 + @Test
5.109 + public void assignFloats() {
5.110 + Builder b = new Builder().floats(10, 20, 30);
5.111 + assertEquals(b.getFloats().size(), 3);
5.112 + assertEquals(b.getFloats().get(0), 10.0f);
5.113 + }
5.114 + @Test
5.115 + public void assignStrings() {
5.116 + Builder b = new Builder().strings("A", "AB", "ABC");
5.117 + assertEquals(b.getStrings().size(), 3);
5.118 + assertEquals(b.getStrings().get(1), "AB");
5.119 + }
5.120 +}
6.1 --- a/src/main/javadoc/overview.html Sun Nov 01 16:59:42 2015 +0100
6.2 +++ b/src/main/javadoc/overview.html Thu Nov 05 23:38:18 2015 +0100
6.3 @@ -96,6 +96,9 @@
6.4
6.5 <h3>Improvements in version 1.3</h3>
6.6
6.7 + {@link net.java.html.json.Model Model classes} can generate
6.8 + builder-like construction methods if builder
6.9 + {@link net.java.html.json.Model#builder() prefix} is specified.
6.10 The <em>JavaFX</em> presenter can be executed in headless mode -
6.11 just specify <code>-Dfxpresenter.headless=true</code> when launching
6.12 its virtual machine and no window will be shown. This is particularly