You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jt...@apache.org on 2017/09/03 12:48:40 UTC
[07/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial
donation of HTML/Java NetBeans API. Equivalent to the content of
html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip
with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/MapModelTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/MapModelTest.java b/json/src/test/java/net/java/html/json/MapModelTest.java
new file mode 100644
index 0000000..3a9143d
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/MapModelTest.java
@@ -0,0 +1,521 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import net.java.html.BrwsrCtx;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.ListIterator;
+import java.util.Map;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class MapModelTest {
+ private MapTechnology t;
+ private BrwsrCtx c;
+
+ @BeforeMethod public void initTechnology() {
+ t = new MapTechnology();
+ c = Contexts.newBuilder().register(Technology.class, t, 1).
+ register(Transfer.class, t, 1).build();
+ }
+
+ @Test public void isThereNoApplyBinding() throws Exception {
+ try {
+ Person.class.getMethod("applyBindings");
+ } catch (NoSuchMethodException ex) {
+ // OK
+ return;
+ }
+ fail("There should be no applyBindings() method");
+ }
+
+ @Test public void isThereABinding() throws Exception {
+ Person p = Models.bind(new Person(), c);
+ Models.applyBindings(p);
+ assertNull(t.appliedId, "Applied globally");
+ p.setFirstName("Jarda");
+
+ Map m = (Map)Models.toRaw(p);
+ Object v = m.get("firstName");
+ assertNotNull(v, "Value should be in the map");
+ assertEquals(v.getClass(), One.class, "It is instance of One");
+ One o = (One)v;
+ assertEquals(o.changes, 1, "One change so far");
+ assertFalse(o.pb.isReadOnly(), "Mutable property");
+
+ assertEquals(o.get(), "Jarda", "Value should be in the map");
+
+ o.set("Karle");
+
+ assertEquals(p.getFirstName(), "Karle", "Model value updated");
+ assertEquals(o.changes, 2, "Snd change");
+ }
+
+ @Test public void applyLocally() throws Exception {
+ Person p = Models.bind(new Person(), c);
+ Models.applyBindings(p, "local");
+ assertEquals(t.appliedId, "local", "Applied locally");
+ }
+
+ @Test public void dontNotifySameProperty() throws Exception {
+ Person p = Models.bind(new Person(), c);
+ p.setFirstName("Jirka");
+
+ Map m = (Map)Models.toRaw(p);
+ Object v = m.get("firstName");
+ assertNotNull(v, "Value should be in the map");
+ assertEquals(v.getClass(), One.class, "It is instance of One");
+ One o = (One)v;
+ assertEquals(o.changes, 0, "No change so far the only one change happened before we connected");
+
+ p.setFirstName(new String("Jirka"));
+ assertEquals(o.changes, 0, "No change so far, the value is the same");
+
+ p.setFirstName("Jarda");
+ assertFalse(o.pb.isReadOnly(), "Mutable property");
+
+ assertEquals(o.get(), "Jarda", "Value should be in the map");
+
+ o.set("Karle");
+
+ assertEquals(p.getFirstName(), "Karle", "Model value updated");
+ assertEquals(o.changes, 2, "Snd change");
+ }
+
+ @Test public void canSetEnumAsString() throws Exception {
+ Person p = Models.bind(new Person(), c);
+ p.setFirstName("Jirka");
+ p.setSex(Sex.MALE);
+
+ Map m = (Map)Models.toRaw(p);
+ Object v = m.get("sex");
+ assertNotNull(v, "Value should be in the map");
+ assertEquals(v.getClass(), One.class, "It is instance of One");
+ One o = (One)v;
+
+ o.set("FEMALE");
+
+ assertEquals(p.getSex(), Sex.FEMALE, "Changed to female");
+ }
+
+ @Test public void derivedProperty() throws Exception {
+ Person p = Models.bind(new Person(), c);
+
+ Map m = (Map)Models.toRaw(p);
+ Object v = m.get("fullName");
+ assertNotNull(v, "Value should be in the map");
+ assertEquals(v.getClass(), One.class, "It is instance of One");
+ One o = (One)v;
+ assertTrue(o.pb.isReadOnly(), "Mutable property");
+ }
+
+ @Test public void changeSex() {
+ Person p = Models.bind(new Person(), c);
+ p.setFirstName("Trans");
+ p.setSex(Sex.MALE);
+
+ Map m = (Map)Models.toRaw(p);
+ Object o = m.get("changeSex");
+ assertNotNull(o, "Function registered in the model");
+ assertEquals(o.getClass(), One.class);
+
+ One one = (One)o;
+ assertNotNull(one.fb, "Function binding specified");
+
+ one.fb.call(null, null);
+
+ assertEquals(p.getSex(), Sex.FEMALE, "Changed");
+ }
+
+ @Test public void setSex() {
+ Person p = Models.bind(new Person(), c);
+ p.setFirstName("Trans");
+
+ Map m = (Map)Models.toRaw(p);
+ Object o = m.get("changeSex");
+ assertNotNull(o, "Function registered in the model");
+ assertEquals(o.getClass(), One.class);
+
+ One one = (One)o;
+ assertNotNull(one.fb, "Function binding specified");
+
+ one.fb.call("FEMALE", new Object());
+
+ assertEquals(p.getSex(), Sex.FEMALE, "Changed");
+ }
+
+ @Test public void changeComputedProperty() {
+ Modelik p = Models.bind(new Modelik(), c);
+ p.setValue(5);
+
+ Map m = (Map)Models.toRaw(p);
+ Object o = m.get("powerValue");
+ assertNotNull(o, "Value is there");
+ assertEquals(o.getClass(), One.class);
+
+ One one = (One)o;
+ assertNotNull(one.pb, "Prop binding specified");
+
+ assertEquals(one.pb.getValue(), 25, "Power of 5");
+
+ one.pb.setValue(16);
+ assertEquals(p.getValue(), 4, "Square root of 16");
+ }
+
+ @Test public void removeViaIterator() {
+ People p = Models.bind(new People(), c);
+ p.getNicknames().add("One");
+ p.getNicknames().add("Two");
+ p.getNicknames().add("Three");
+
+ Map m = (Map)Models.toRaw(p);
+ Object o = m.get("nicknames");
+ assertNotNull(o, "List registered in the model");
+ assertEquals(o.getClass(), One.class);
+ One one = (One)o;
+
+
+ assertEquals(one.changes, 0, "No change");
+
+ Iterator<String> it = p.getNicknames().iterator();
+ assertEquals(it.next(), "One");
+ assertEquals(it.next(), "Two");
+ it.remove();
+ assertEquals(it.next(), "Three");
+ assertFalse(it.hasNext());
+
+
+ assertEquals(one.changes, 1, "One change");
+ }
+
+ @Test public void removeViaListIterator() {
+ People p = Models.bind(new People(), c);
+ p.getNicknames().add("One");
+ p.getNicknames().add("Two");
+ p.getNicknames().add("Three");
+
+ Map m = (Map)Models.toRaw(p);
+ Object o = m.get("nicknames");
+ assertNotNull(o, "List registered in the model");
+ assertEquals(o.getClass(), One.class);
+ One one = (One)o;
+
+
+ assertEquals(one.changes, 0, "No change");
+
+ ListIterator<String> it = p.getNicknames().listIterator(1);
+ assertEquals(it.next(), "Two");
+ it.remove();
+ assertEquals(it.next(), "Three");
+ assertFalse(it.hasNext());
+
+
+ assertEquals(one.changes, 1, "One change");
+
+ it.set("3");
+ assertEquals(p.getNicknames().get(1), "3");
+
+ assertEquals(one.changes, 2, "Snd change");
+ }
+
+ @Test public void subListChange() {
+ People p = Models.bind(new People(), c);
+ p.getNicknames().add("One");
+ p.getNicknames().add("Two");
+ p.getNicknames().add("Three");
+
+ Map m = (Map)Models.toRaw(p);
+ Object o = m.get("nicknames");
+ assertNotNull(o, "List registered in the model");
+ assertEquals(o.getClass(), One.class);
+ One one = (One)o;
+
+
+ assertEquals(one.changes, 0, "No change");
+
+ p.getNicknames().subList(1, 2).clear();
+
+ assertEquals(p.getNicknames().size(), 2, "Two elements");
+
+ ListIterator<String> it = p.getNicknames().listIterator(0);
+ assertEquals(it.next(), "One");
+ assertEquals(it.next(), "Three");
+ assertFalse(it.hasNext());
+
+
+ assertEquals(one.changes, 1, "One change");
+ }
+
+ @Test public void sort() {
+ People p = Models.bind(new People(), c);
+ p.getNicknames().add("One");
+ p.getNicknames().add("Two");
+ p.getNicknames().add("Three");
+ p.getNicknames().add("Four");
+
+ Map m = (Map)Models.toRaw(p);
+ Object o = m.get("nicknames");
+ assertNotNull(o, "List registered in the model");
+ assertEquals(o.getClass(), One.class);
+ One one = (One)o;
+
+
+ assertEquals(one.changes, 0, "No change");
+
+ Collections.sort(p.getNicknames());
+
+ Iterator<String> it = p.getNicknames().iterator();
+ assertEquals(it.next(), "Four");
+ assertEquals(it.next(), "One");
+ assertEquals(it.next(), "Three");
+ assertEquals(it.next(), "Two");
+ assertFalse(it.hasNext());
+
+
+ assertNotEquals(one.changes, 0, "At least one change");
+
+ if (isJDK8()) {
+ assertEquals(one.changes, 1, "Exactly one echange");
+ }
+ }
+
+ @Test public void functionWithParameters() {
+ People p = Models.bind(new People(), c);
+ p.getNicknames().add("One");
+ p.getNicknames().add("Two");
+ p.getNicknames().add("Three");
+
+ Map m = (Map)Models.toRaw(p);
+ Object o = m.get("inInnerClass");
+ assertNotNull(o, "functiton is available");
+ assertEquals(o.getClass(), One.class);
+ One one = (One)o;
+
+ Map<String,Object> obj = new HashMap<String, Object>();
+ obj.put("nick", "newNick");
+ obj.put("x", 42);
+ obj.put("y", 7.7f);
+ final Person data = new Person("a", "b", Sex.MALE);
+
+ one.fb.call(data, obj);
+
+ assertEquals(p.getInfo().size(), 1, "a+b is there: " + p.getInfo());
+ assertEquals(p.getInfo().get(0), data, "Expecting data: " + p.getInfo());
+
+ assertEquals(p.getNicknames().size(), 4, "One more nickname: " + p.getNicknames());
+ assertEquals(p.getNicknames().get(3), "newNick");
+
+ assertEquals(p.getAge().size(), 2, "Two new values: " + p.getAge());
+ assertEquals(p.getAge().get(0).intValue(), 42);
+ assertEquals(p.getAge().get(1).intValue(), 7);
+ }
+
+ @Test
+ public void addAge42ThreeTimes() {
+ People p = Models.bind(new People(), c);
+ Map m = (Map)Models.toRaw(p);
+ assertNotNull(m);
+
+ class Inc implements Runnable {
+ int cnt;
+
+ @Override
+ public void run() {
+ cnt++;
+ }
+ }
+ Inc incThreeTimes = new Inc();
+ p.onInfoChange(incThreeTimes);
+
+ p.addAge42();
+ p.addAge42();
+ p.addAge42();
+ final int[] cnt = { 0, 0 };
+ p.readAddAgeCount(cnt, new Runnable() {
+ @Override
+ public void run() {
+ cnt[1] = 1;
+ }
+ });
+ assertEquals(cnt[1], 1, "Callback called");
+ assertEquals(cnt[0], 3, "Internal state kept");
+ assertEquals(incThreeTimes.cnt, 3, "Property change delivered three times");
+ }
+
+ private static boolean isJDK8() {
+ try {
+ Class.forName("java.lang.FunctionalInterface");
+ return true;
+ } catch (ClassNotFoundException ex) {
+ return false;
+ }
+ }
+
+ static final class One {
+ int changes;
+ final PropertyBinding pb;
+ final FunctionBinding fb;
+
+ One(Object m, PropertyBinding pb) throws NoSuchMethodException {
+ this.pb = pb;
+ this.fb = null;
+ }
+ One(Object m, FunctionBinding fb) throws NoSuchMethodException {
+ this.pb = null;
+ this.fb = fb;
+ }
+
+ Object get() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ return pb.getValue();
+ }
+
+ void set(Object v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ pb.setValue(v);
+ }
+ }
+
+ static final class MapTechnology
+ implements Technology.ApplyId<Map<String,One>>, Transfer {
+ private Map<String, One> appliedData;
+ private String appliedId;
+
+ @Override
+ public Map<String, One> wrapModel(Object model) {
+ return new HashMap<String, One>();
+ }
+
+ @Override
+ public void valueHasMutated(Map<String, One> data, String propertyName) {
+ One p = data.get(propertyName);
+ if (p != null) {
+ p.changes++;
+ }
+ }
+
+ @Override
+ public void bind(PropertyBinding b, Object model, Map<String, One> data) {
+ try {
+ One o = new One(model, b);
+ data.put(b.getPropertyName(), o);
+ } catch (NoSuchMethodException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ @Override
+ public void expose(FunctionBinding fb, Object model, Map<String, One> data) {
+ try {
+ data.put(fb.getFunctionName(), new One(model, fb));
+ } catch (NoSuchMethodException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ @Override
+ public void applyBindings(Map<String, One> data) {
+ throw new UnsupportedOperationException("Never called!");
+ }
+
+ @Override
+ public Object wrapArray(Object[] arr) {
+ return arr;
+ }
+
+ @Override
+ public void extract(Object obj, String[] props, Object[] values) {
+ Map<?,?> map = obj instanceof Map ? (Map<?,?>)obj : null;
+ for (int i = 0; i < Math.min(props.length, values.length); i++) {
+ if (map == null) {
+ values[i] = null;
+ } else {
+ values[i] = map.get(props[i]);
+ if (values[i] instanceof One) {
+ values[i] = ((One)values[i]).pb.getValue();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void loadJSON(JSONCall call) {
+ call.notifyError(new UnsupportedOperationException());
+ }
+
+ @Override
+ public <M> M toModel(Class<M> modelClass, Object data) {
+ return modelClass.cast(data);
+ }
+
+ @Override
+ public Object toJSON(InputStream is) throws IOException {
+ throw new IOException();
+ }
+
+ @Override
+ public void runSafe(Runnable r) {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public void applyBindings(String id, Map<String, One> data) {
+ this.appliedId = id;
+ this.appliedData = data;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/ModelProcessorTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/ModelProcessorTest.java b/json/src/test/java/net/java/html/json/ModelProcessorTest.java
new file mode 100644
index 0000000..9c0d336
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/ModelProcessorTest.java
@@ -0,0 +1,823 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.io.IOException;
+import java.util.Locale;
+import javax.tools.Diagnostic;
+import javax.tools.JavaFileObject;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/** Verify errors emitted by the processor.
+ *
+ * @author Jaroslav Tulach
+ */
+public class ModelProcessorTest {
+ @Test public void verifyWrongType() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=Runnable.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ boolean ok = false;
+ StringBuilder msgs = new StringBuilder();
+ for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+ String msg = e.getMessage(Locale.ENGLISH);
+ if (msg.contains("Runnable")) {
+ ok = true;
+ }
+ msgs.append("\n").append(msg);
+ }
+ if (!ok) {
+ fail("Should contain warning about Runnable:" + msgs);
+ }
+ }
+
+ @Test public void verifyWrongTypeInInnerClass() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "class X {\n"
+ + " @Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=Runnable.class)\n"
+ + " })\n"
+ + " static class Inner {\n"
+ + " }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ boolean ok = false;
+ StringBuilder msgs = new StringBuilder();
+ for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+ String msg = e.getMessage(Locale.ENGLISH);
+ if (msg.contains("Runnable")) {
+ ok = true;
+ }
+ msgs.append("\n").append(msg);
+ }
+ if (!ok) {
+ fail("Should contain warning about Runnable:" + msgs);
+ }
+ }
+
+ @Test public void warnOnNonStatic() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=int.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @ComputedProperty int y(int prop) {\n"
+ + " return prop;\n"
+ + " }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ boolean ok = false;
+ StringBuilder msgs = new StringBuilder();
+ for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+ String msg = e.getMessage(Locale.ENGLISH);
+ if (msg.contains("y has to be static")) {
+ ok = true;
+ }
+ msgs.append("\n").append(msg);
+ }
+ if (!ok) {
+ fail("Should contain warning about non-static method:" + msgs);
+ }
+ }
+
+ @Test public void warnOnDuplicated() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop1\", type=int.class),\n"
+ + " @Property(name=\"prop2\", type=int.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @ComputedProperty static int y(int prop1) {\n"
+ + " return prop1;\n"
+ + " }\n"
+ + " @ComputedProperty static int y(int prop1, int prop2) {\n"
+ + " return prop2;\n"
+ + " }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ boolean ok = false;
+ StringBuilder msgs = new StringBuilder();
+ for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+ String msg = e.getMessage(Locale.ENGLISH);
+ if (msg.contains("Cannot have the property y defined twice")) {
+ ok = true;
+ }
+ msgs.append("\n").append(msg);
+ }
+ if (!ok) {
+ fail("Should contain warning duplicated property:" + msgs);
+ }
+ }
+
+ @Test public void warnOnDuplicatedWithNormalProp() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop1\", type=int.class),\n"
+ + " @Property(name=\"prop2\", type=int.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @ComputedProperty static int prop2(int prop1) {\n"
+ + " return prop1;\n"
+ + " }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ boolean ok = false;
+ StringBuilder msgs = new StringBuilder();
+ for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+ String msg = e.getMessage(Locale.ENGLISH);
+ if (msg.contains("Cannot have the property prop2 defined twice")) {
+ ok = true;
+ }
+ msgs.append("\n").append(msg);
+ }
+ if (!ok) {
+ fail("Should contain warning duplicated property:" + msgs);
+ }
+ }
+
+ @Test public void tooManyProperties() throws IOException {
+ manyProperties(255, false, 0);
+ }
+
+ @Test public void tooManyArrayPropertiesIsOK() throws IOException {
+ manyProperties(0, true, 300);
+ }
+
+ @Test public void justEnoughProperties() throws IOException {
+ manyProperties(254, true, 0);
+ }
+
+ @Test public void justEnoughPropertiesWithArrayOne() throws IOException {
+ manyProperties(253, true, 300);
+ }
+
+ @Test public void justEnoughPropertiesButOneArrayOne() throws IOException {
+ manyProperties(254, false, 300);
+ }
+
+ private void manyProperties(
+ int cnt, boolean constructorWithParams, int arrayCnt
+ ) throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ StringBuilder code = new StringBuilder();
+ code.append("package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ );
+ for (int i = 1; i <= cnt; i++) {
+ code.append(" @Property(name=\"prop").append(i).append("\", ");
+ code.append("type=int.class),\n");
+ }
+ for (int i = 1; i <= arrayCnt; i++) {
+ code.append(" @Property(name=\"array").append(i).append("\", ");
+ code.append("array=true, ");
+ code.append("type=int.class),\n");
+ }
+ code.append(""
+ + "})\n"
+ + "class X {\n"
+ + " static {\n"
+ + " new XModel();\n"
+ + " new XModel("
+ );
+ if (constructorWithParams) {
+ code.append("0");
+ for (int i = 1; i < cnt; i++) {
+ code.append(",\n").append(i);
+ }
+ }
+ code.append(");\n"
+ + " }\n"
+ + "}\n"
+ );
+
+ Compile c = Compile.create(html, code.toString());
+ assertTrue(c.getErrors().isEmpty(), "Compiles OK: " + c.getErrors());
+ }
+
+ @Test public void writeableComputedPropertyMissingWrite() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=int.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " static @ComputedProperty(write=\"setY\") int y(int prop) {\n"
+ + " return prop;\n"
+ + " }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ boolean ok = false;
+ StringBuilder msgs = new StringBuilder();
+ for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+ String msg = e.getMessage(Locale.ENGLISH);
+ if (msg.contains("Cannot find setY")) {
+ ok = true;
+ }
+ msgs.append("\n").append(msg);
+ }
+ if (!ok) {
+ fail("Should contain warning about non-static method:" + msgs);
+ }
+ }
+
+ @Test public void writeableComputedPropertyWrongWriteType() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=int.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " static @ComputedProperty(write=\"setY\") int y(int prop) {\n"
+ + " return prop;\n"
+ + " }\n"
+ + " static void setY(XModel model, String prop) {\n"
+ + " }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ boolean ok = false;
+ StringBuilder msgs = new StringBuilder();
+ for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+ String msg = e.getMessage(Locale.ENGLISH);
+ if (msg.contains("Write method first argument needs to be XModel and second int or Object")) {
+ ok = true;
+ }
+ msgs.append("\n").append(msg);
+ }
+ if (!ok) {
+ fail("Should contain warning about non-static method:" + msgs);
+ }
+ }
+
+ @Test public void writeableComputedPropertyReturnsVoid() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=int.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " static @ComputedProperty(write=\"setY\") int y(int prop) {\n"
+ + " return prop;\n"
+ + " }\n"
+ + " static Number setY(XModel model, int prop) {\n"
+ + " }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ boolean ok = false;
+ StringBuilder msgs = new StringBuilder();
+ for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+ String msg = e.getMessage(Locale.ENGLISH);
+ if (msg.contains("Write method has to return void")) {
+ ok = true;
+ }
+ msgs.append("\n").append(msg);
+ }
+ if (!ok) {
+ fail("Should contain warning about non-static method:" + msgs);
+ }
+ }
+
+ @Test public void computedCantReturnVoid() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=int.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @ComputedProperty static void y(int prop) {\n"
+ + " }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ boolean ok = false;
+ StringBuilder msgs = new StringBuilder();
+ for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+ String msg = e.getMessage(Locale.ENGLISH);
+ if (msg.contains("y cannot return void")) {
+ ok = true;
+ }
+ msgs.append("\n").append(msg);
+ }
+ if (!ok) {
+ fail("Should contain warning about non-static method:" + msgs);
+ }
+ }
+
+ @Test public void computedCantReturnRunnable() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=int.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @ComputedProperty static Runnable y(int prop) {\n"
+ + " return null;\n"
+ + " }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ boolean ok = false;
+ StringBuilder msgs = new StringBuilder();
+ for (Diagnostic<? extends JavaFileObject> e : c.getErrors()) {
+ String msg = e.getMessage(Locale.ENGLISH);
+ if (msg.contains("y cannot return java.lang.Runnable")) {
+ ok = true;
+ }
+ msgs.append("\n").append(msg);
+ }
+ if (!ok) {
+ fail("Should contain warning about non-static method:" + msgs);
+ }
+ }
+
+ @Test public void canWeCompileWithJDK1_5SourceLevel() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=long.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @ComputedProperty static double derived(long prop) { return prop; }"
+ + "}\n";
+
+ Compile c = Compile.create(html, code, "1.5");
+ assertTrue(c.getErrors().isEmpty(), "No errors: " + c.getErrors());
+ }
+
+ @Test public void instanceNeedsDefaultConstructor() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", instance=true, properties={\n"
+ + " @Property(name=\"prop\", type=long.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " X(int x) {}\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ c.assertError("Needs non-private default constructor when instance=true");
+ }
+
+ @Test public void instanceNeedsNonPrivateConstructor() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", instance=true, properties={\n"
+ + " @Property(name=\"prop\", type=long.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " private X() {}\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ c.assertError("Needs non-private default constructor when instance=true");
+ }
+
+ @Test public void instanceNoConstructorIsOK() throws IOException {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.ComputedProperty;\n"
+ + "@Model(className=\"XModel\", instance=true, properties={\n"
+ + " @Property(name=\"prop\", type=long.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ c.assertNoErrors();
+ }
+
+ @Test public void putNeedsDataArgument() throws Exception {
+ needsAnArg("PUT");
+ }
+
+ @Test public void postNeedsDataArgument() throws Exception {
+ needsAnArg("POST");
+ }
+
+ private void needsAnArg(String method) throws Exception {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.OnReceive;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=long.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @Model(className=\"PQ\", properties={})\n"
+ + " class PImpl {\n"
+ + " }\n"
+ + " @OnReceive(method=\"" + method + "\", url=\"whereever\")\n"
+ + " static void obtained(XModel m, PQ p) { }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+ String msg = diagnostic.getMessage(Locale.ENGLISH);
+ if (msg.contains("specify a data()")) {
+ return;
+ }
+ }
+ fail("Needs an error message about missing data():\n" + c.getErrors());
+
+ }
+
+
+ @Test public void jsonNeedsToUseGet () throws Exception {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.OnReceive;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=long.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @Model(className=\"PQ\", properties={})\n"
+ + " class PImpl {\n"
+ + " }\n"
+ + " @OnReceive(method=\"POST\", jsonp=\"callback\", url=\"whereever\")\n"
+ + " static void obtained(XModel m, PQ p) { }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+ String msg = diagnostic.getMessage(Locale.ENGLISH);
+ if (msg.contains("JSONP works only with GET")) {
+ return;
+ }
+ }
+ fail("Needs an error message about wrong method:\n" + c.getErrors());
+
+ }
+
+ @Test public void noHeadersForWebSockets() throws Exception {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.OnReceive;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=long.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @Model(className=\"PQ\", properties={})\n"
+ + " class PImpl {\n"
+ + " }\n"
+ + " @OnReceive(method=\"WebSocket\", data = PQ.class, headers=\"SomeHeader: {some}\", url=\"whereever\")\n"
+ + " static void obtained(XModel m, PQ p) { }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+ String msg = diagnostic.getMessage(Locale.ENGLISH);
+ if (msg.contains("WebSocket spec does not support headers")) {
+ return;
+ }
+ }
+ fail("Needs an error message about headers:\n" + c.getErrors());
+
+ }
+
+ @Test public void webSocketsWithoutDataIsError() throws Exception {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.OnReceive;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=long.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @Model(className=\"PQ\", properties={})\n"
+ + " class PImpl {\n"
+ + " }\n"
+ + " @OnReceive(method=\"WebSocket\", url=\"whereever\")\n"
+ + " static void obtained(XModel m, PQ p) { }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+ String msg = diagnostic.getMessage(Locale.ENGLISH);
+ if (msg.contains("eeds to specify a data()")) {
+ return;
+ }
+ }
+ fail("Needs data attribute :\n" + c.getErrors());
+ }
+
+ @Test public void noNewLinesInHeaderLines() throws Exception {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.OnReceive;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=long.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @Model(className=\"PQ\", properties={})\n"
+ + " class PImpl {\n"
+ + " }\n"
+ + " @OnReceive(headers=\"SomeHeader\\n: {some}\", url=\"whereever\")\n"
+ + " static void obtained(XModel m, PQ p) { }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+ String msg = diagnostic.getMessage(Locale.ENGLISH);
+ if (msg.contains("Header line cannot contain line separator")) {
+ return;
+ }
+ }
+ fail("Needs an error message about headers:\n" + c.getErrors());
+
+ }
+
+ @Test public void noReturnInHeaderLines() throws Exception {
+ String html = "<html><body>"
+ + "</body></html>";
+ String code = "package x.y.z;\n"
+ + "import net.java.html.json.Model;\n"
+ + "import net.java.html.json.Property;\n"
+ + "import net.java.html.json.OnReceive;\n"
+ + "@Model(className=\"XModel\", properties={\n"
+ + " @Property(name=\"prop\", type=long.class)\n"
+ + "})\n"
+ + "class X {\n"
+ + " @Model(className=\"PQ\", properties={})\n"
+ + " class PImpl {\n"
+ + " }\n"
+ + " @OnReceive(headers=\"Some\\rHeader: {some}\", url=\"whereever\")\n"
+ + " static void obtained(XModel m, PQ p) { }\n"
+ + "}\n";
+
+ Compile c = Compile.create(html, code);
+ assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
+ for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
+ String msg = diagnostic.getMessage(Locale.ENGLISH);
+ if (msg.contains("Header line cannot contain line separator")) {
+ return;
+ }
+ }
+ fail("Needs an error message about headers:\n" + c.getErrors());
+
+ }
+
+ @Test public void onErrorHasToExist() throws IOException {
+ Compile res = Compile.create("", "package x;\n"
+ + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+ + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+ + "})\n"
+ + "class UseOnReceive {\n"
+ + " @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"doesNotExist\")\n"
+ + " static void onMessage(MyModel model, String value) {\n"
+ + " }\n"
+ + "}\n"
+ );
+ res.assertErrors();
+ res.assertError("not find doesNotExist");
+ }
+
+ @Test public void usingListIsOK() throws IOException {
+ Compile res = Compile.create("", "package x;\n"
+ + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+ + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+ + "})\n"
+ + "class UseOnReceive {\n"
+ + " @net.java.html.json.OnReceive(url=\"http://nowhere.com\")\n"
+ + " static void onMessage(MyModel model, java.util.List<MyData> value) {\n"
+ + " }\n"
+ + "\n"
+ + " @net.java.html.json.Model(className=\"MyData\", properties={\n"
+ + " })\n"
+ + " static class MyDataModel {\n"
+ + " }\n"
+ + "}\n"
+ );
+ res.assertNoErrors();
+ }
+
+ @Test public void functionAndPropertyCollide() throws IOException {
+ Compile res = Compile.create("", "package x;\n"
+ + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+ + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+ + "})\n"
+ + "class Collision {\n"
+ + " @net.java.html.json.Function\n"
+ + " static void x(MyModel model, String value) {\n"
+ + " }\n"
+ + "}\n"
+ );
+ res.assertErrors();
+ res.assertError("cannot have the name");
+ }
+
+ @Test public void twoPropertiesCollide() throws IOException {
+ Compile res = Compile.create("", "package x;\n"
+ + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+ + " @net.java.html.json.Property(name=\"x\", type=String.class),\n"
+ + " @net.java.html.json.Property(name=\"x\", type=int.class)\n"
+ + "})\n"
+ + "class Collision {\n"
+ + "}\n"
+ );
+ res.assertErrors();
+ res.assertError("Cannot have the property");
+ }
+
+ @Test public void propertyAndComputedOneCollide() throws IOException {
+ Compile res = Compile.create("", "package x;\n"
+ + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+ + " @net.java.html.json.Property(name=\"x\", type=String.class),\n"
+ + "})\n"
+ + "class Collision {\n"
+ + " @net.java.html.json.ComputedProperty static int x(String x) {\n"
+ + " return x.length();\n"
+ + " }\n"
+ + "}\n"
+ );
+ res.assertErrors();
+ res.assertError("Cannot have the property");
+ }
+
+ @Test public void onWebSocketJustTwoArgs() throws IOException {
+ Compile res = Compile.create("", "package x;\n"
+ + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+ + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+ + "})\n"
+ + "class UseOnReceive {\n"
+ + " @net.java.html.json.OnReceive(url=\"http://nowhere.com\", method=\"WebSocket\", data=String.class)\n"
+ + " static void onMessage(MyModel model, String value, int arg) {\n"
+ + " }\n"
+ + "}\n"
+ );
+ res.assertErrors();
+ res.assertError("only have two arg");
+ }
+
+ @Test public void onErrorWouldHaveToBeStatic() throws IOException {
+ Compile res = Compile.create("", "package x;\n"
+ + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+ + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+ + "})\n"
+ + "class UseOnReceive {\n"
+ + " @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"notStatic\")\n"
+ + " static void onMessage(MyModel model, String value) {\n"
+ + " }\n"
+ + " void notStatic(Exception e) {}\n"
+ + "}\n"
+ );
+ res.assertErrors();
+ res.assertError("have to be static");
+ }
+
+ @Test public void onErrorMustAcceptExceptionArgument() throws IOException {
+ Compile res = Compile.create("", "package x;\n"
+ + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
+ + " @net.java.html.json.Property(name=\"x\", type=String.class)\n"
+ + "})\n"
+ + "class UseOnReceive {\n"
+ + " @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"subclass\")\n"
+ + " static void onMessage(MyModel model, String value) {\n"
+ + " }\n"
+ + " static void subclass(java.io.IOException e) {}\n"
+ + "}\n"
+ );
+ res.assertErrors();
+ res.assertError("Error method first argument needs to be MyModel and second Exception");
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/ModelTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/ModelTest.java b/json/src/test/java/net/java/html/json/ModelTest.java
new file mode 100644
index 0000000..bb4a7be
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/ModelTest.java
@@ -0,0 +1,506 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Technology;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "Modelik", builder = "change", targetId = "", properties = {
+ @Property(name = "value", type = int.class),
+ @Property(name = "count", type = int.class),
+ @Property(name = "unrelated", type = long.class),
+ @Property(name = "names", type = String.class, array = true),
+ @Property(name = "values", type = int.class, array = true),
+ @Property(name = "people", type = Person.class, array = true),
+ @Property(name = "changedProperty", type=String.class)
+})
+public class ModelTest {
+ private MockTechnology my;
+ private Modelik model;
+ private static Modelik leakedModel;
+
+ @BeforeMethod
+ public void createModel() {
+ my = new MockTechnology();
+ final BrwsrCtx c = Contexts.newBuilder().register(Technology.class, my, 1).build();
+ model = Models.bind(new Modelik(), c);
+ }
+
+ @Test public void classGeneratedWithSetterGetter() {
+ model.setValue(10);
+ assertEquals(10, model.getValue(), "Value changed");
+ }
+
+ @Test public void computedMethod() {
+ model.setValue(4);
+ assertEquals(16, model.getPowerValue());
+ }
+
+ @Test public void equalsAndHashCode() {
+ Modelik m1 = new Modelik();
+ m1.setValue(10);
+ m1.setCount(20);
+ m1.setUnrelated(30);
+ m1.setChangedProperty("changed");
+ m1.getNames().add("firstName");
+ Modelik m2 = new Modelik().
+ changeValue(10).
+ changeCount(20).
+ changeUnrelated(30).
+ changeChangedProperty("changed").
+ changeNames("firstName");
+
+ assertTrue(m1.equals(m2), "They are the same");
+ assertEquals(m1.hashCode(), m2.hashCode(), "Hashcode is the same");
+
+ m1.setCount(33);
+
+ assertFalse(m1.equals(m2), "No longer the same");
+ assertFalse(m1.hashCode() == m2.hashCode(), "No longe is hashcode is the same");
+ }
+
+ @Test public void arrayIsMutable() {
+ assertEquals(model.getNames().size(), 0, "Is empty");
+ model.getNames().add("Jarda");
+ assertEquals(model.getNames().size(), 1, "One element");
+ }
+
+ @Test public void arrayChangesNotNotifiedUntilInitied() {
+ model.getNames().add("Hello");
+ assertTrue(my.mutated.isEmpty(), "No change now " + my.mutated);
+ model.getNames().remove("Hello");
+ assertTrue(my.mutated.isEmpty(), "No change still " + my.mutated);
+ assertTrue(model.getNames().isEmpty(), "No empty");
+ }
+
+ @Test public void arrayChangesNotified() {
+ Models.applyBindings(model);
+ model.getNames().add("Hello");
+
+ assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
+ assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
+
+ my.mutated.clear();
+
+ Iterator<String> it = model.getNames().iterator();
+ assertEquals(it.next(), "Hello");
+ it.remove();
+
+ assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
+ assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
+
+ my.mutated.clear();
+
+ ListIterator<String> lit = model.getNames().listIterator();
+ lit.add("Jarda");
+
+ assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
+ assertTrue(my.mutated.contains("names"), "Change in names property: " + my.mutated);
+ }
+
+ @Test public void autoboxedArray() {
+ model.getValues().add(10);
+
+ assertEquals(model.getValues().get(0), Integer.valueOf(10), "Really ten");
+ }
+
+ @Test public void derivedArrayProp() {
+ model.applyBindings();
+ model.setCount(10);
+
+ List<String> arr = model.getRepeat();
+ assertEquals(arr.size(), 10, "Ten items: " + arr);
+
+ my.mutated.clear();
+
+ model.setCount(5);
+
+ arr = model.getRepeat();
+ assertEquals(arr.size(), 5, "Five items: " + arr);
+
+ assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
+ assertTrue(my.mutated.contains("repeat"), "Array is in there: " + my.mutated);
+ assertTrue(my.mutated.contains("count"), "Count is in there: " + my.mutated);
+ }
+
+ @Test public void derivedArrayPropChange() {
+ model.applyBindings();
+ model.setCount(5);
+
+ List<String> arr = model.getRepeat();
+ assertEquals(arr.size(), 5, "Five items: " + arr);
+
+ model.setRepeat(10);
+ assertEquals(model.getCount(), 10, "Changing repeat changes count");
+ }
+
+ @Test public void derivedPropertiesAreNotified() {
+ model.applyBindings();
+
+ model.setValue(33);
+
+ // not interested in change of this property
+ my.mutated.remove("changedProperty");
+
+ assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
+ assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated);
+ assertTrue(my.mutated.contains("value"), "Simple value is in there: " + my.mutated);
+
+ my.mutated.clear();
+
+ model.setUnrelated(44);
+
+
+ // not interested in change of this property
+ my.mutated.remove("changedProperty");
+ assertEquals(my.mutated.size(), 1, "One property changed: " + my.mutated);
+ assertTrue(my.mutated.contains("unrelated"), "Its name is unrelated");
+ }
+
+ @Test public void computedPropertyCannotWriteToModel() {
+ leakedModel = model;
+ try {
+ String res = model.getNotAllowedWrite();
+ fail("We should not be allowed to write to the model: " + res);
+ } catch (IllegalStateException ex) {
+ // OK, we can't read
+ }
+ }
+
+ @Test public void computedPropertyCannotReadToModel() {
+ leakedModel = model;
+ try {
+ String res = model.getNotAllowedRead();
+ fail("We should not be allowed to read from the model: " + res);
+ } catch (IllegalStateException ex) {
+ // OK, we can't read
+ }
+ }
+
+ @OnReceive(url = "{protocol}://{host}?query={query}", data = Person.class, onError = "errorState")
+ static void loadPeople(Modelik thiz, People p) {
+ Modelik m = null;
+ m.applyBindings();
+ m.loadPeople("http", "apidesign.org", "query", new Person());
+ }
+
+ static void errorState(Modelik thiz, Exception ex) {
+
+ }
+
+ @OnReceive(url="{url}", headers={
+ "Easy: {easy}",
+ "H-a+r!d?e.r: {harder}",
+ "H-a+r!d?e's\"t: {harder}",
+ "Repeat-ed: {rep}",
+ "Repeat+ed: {rep}",
+ "Same-URL: {url}"
+ })
+ static void fetchPeopleWithHeaders(Modelik model, People p) {
+ model.fetchPeopleWithHeaders("url", "easy", "harder", "rep");
+ }
+
+ @OnReceive(url = "{protocol}://{host}?callback={back}&query={query}", jsonp = "back")
+ static void loadPeopleViaJSONP(Modelik thiz, People p) {
+ Modelik m = null;
+ m.applyBindings();
+ m.loadPeopleViaJSONP("http", "apidesign.org", "query");
+ }
+
+ @OnReceive(url = "{rep}://{rep}")
+ static void repeatedTest(Modelik thiz, People p) {
+ thiz.repeatedTest("justOneParameterRep");
+ }
+
+ @Function
+ static void doSomething() {
+ }
+
+ @ComputedProperty(write = "setPowerValue")
+ static int powerValue(int value) {
+ return value * value;
+ }
+
+ static void setPowerValue(Modelik m, int value) {
+ m.setValue((int)Math.sqrt(value));
+ }
+
+ @OnPropertyChange({ "powerValue", "unrelated" })
+ static void aPropertyChanged(Modelik m, String name) {
+ m.setChangedProperty(name);
+ }
+
+ @OnPropertyChange({ "values" })
+ static void anArrayPropertyChanged(String name, Modelik m) {
+ m.setChangedProperty(name);
+ }
+
+ @Test public void changeAnything() {
+ model.setCount(44);
+ assertNull(model.getChangedProperty(), "No observed value change");
+ }
+ @Test public void changeValue() {
+ model.setValue(33);
+ assertEquals(model.getChangedProperty(), "powerValue", "power property changed");
+ }
+ @Test public void changePowerValue() {
+ model.setValue(3);
+ assertEquals(model.getPowerValue(), 9, "Square");
+ model.setPowerValue(16);
+ assertEquals(model.getValue(), 4, "Square root");
+ assertEquals(model.getPowerValue(), 16, "Square changed");
+ }
+ @Test public void changeUnrelated() {
+ model.setUnrelated(333);
+ assertEquals(model.getChangedProperty(), "unrelated", "unrelated changed");
+ }
+
+ @Test public void changeInArray() {
+ model.getValues().add(10);
+ assertNull(model.getChangedProperty(), "No change before applyBindings");
+ model.applyBindings();
+ model.getValues().add(10);
+ assertEquals(model.getChangedProperty(), "values", "Something added into the array");
+ }
+
+ @ComputedProperty
+ static String notAllowedRead() {
+ return "Not allowed callback: " + leakedModel.getUnrelated();
+ }
+
+ @ComputedProperty
+ static String notAllowedWrite() {
+ leakedModel.setUnrelated(11);
+ return "Not allowed callback!";
+ }
+
+ @ComputedProperty(write="parseRepeat")
+ static List<String> repeat(int count) {
+ return Collections.nCopies(count, "Hello");
+ }
+ static void parseRepeat(Modelik m, Object v) {
+ m.setCount((Integer)v);
+ }
+
+ public @Test void hasPersonPropertyAndComputedFullName() {
+ List<Person> arr = model.getPeople();
+ assertEquals(arr.size(), 0, "By default empty");
+ Person p = null;
+ if (p != null) {
+ String fullNameGenerated = p.getFullName();
+ assertNotNull(fullNameGenerated);
+ }
+ }
+
+ public @Test void computedListIsOfTypeString() {
+ Person p = new Person("1st", "2nd", Sex.MALE);
+ String first = p.getBothNames().get(0);
+ String last = p.getBothNames().get(1);
+ assertEquals(first, "1st");
+ assertEquals(last, "2nd");
+ }
+
+ @Model(className = "Inner", instance = true, properties = {
+ @Property(name = "x", type = int.class),
+ @Property(name = "y", type = int.class)
+ })
+ static final class InnerCntrl {
+ private BrwsrCtx ctx;
+
+ @ModelOperation
+ void init(Inner model, BrwsrCtx ctx) {
+ this.ctx = ctx;
+ }
+
+ @Function
+ void setYToTen(Inner model) {
+ assertCtx();
+ model.setY(10);
+ }
+
+ @ModelOperation
+ void modelYToTen(Inner model) {
+ assertCtx();
+ model.setY(10);
+ }
+
+ @OnPropertyChange("y")
+ void increment(Inner model) {
+ model.setX(model.getX() + 1);
+ assertCtx();
+ }
+
+ private void assertCtx() {
+ BrwsrCtx realCtx = BrwsrCtx.findDefault(InnerCntrl.class);
+ assertSame(realCtx, ctx, "Proper Ctx is provided");
+ }
+ }
+
+// directly using setters doesn't set the context currently
+// @Test
+// public void incrementXOnChangeOfY() {
+// doIncreementXOnChangeOfY(0);
+// }
+
+ @Test
+ public void incrementXOnChangeOfYViaFunction() {
+ doIncreementXOnChangeOfY(1);
+ }
+
+ @Test
+ public void incrementXOnChangeOfYViaModel() {
+ doIncreementXOnChangeOfY(2);
+ }
+
+ private void doIncreementXOnChangeOfY(int modificationType) {
+ class Exec implements Executor {
+ int cnt;
+ @Override
+ public void execute(Runnable command) {
+ cnt++;
+ command.run();
+ }
+
+ final void assertCount(int expected, String msg) {
+ assertEquals(cnt, expected, msg);
+ cnt = 0;
+ }
+ }
+ MapModelTest.MapTechnology tech = new MapModelTest.MapTechnology();
+ Exec exec = new Exec();
+ final BrwsrCtx c = Contexts.newBuilder().
+ register(Technology.class, tech, 1).
+ register(Executor.class, exec, 5).
+ build();
+
+ Inner model = Models.bind(new Inner(), c);
+ exec.assertCount(0, "Executor not used for anything yet");
+ model.init(c);
+ exec.assertCount(1, "Executor used for initialization");
+
+ Models.applyBindings(model);
+
+ assertEquals(model.getX(), 0, "Zero");
+ assertEquals(model.getY(), 0, "Zero too");
+ int execUse;
+ if (modificationType != 1) {
+ model.modelYToTen();
+ execUse = 3;
+ } else {
+ Object raw = Models.toRaw(model);
+ assertTrue(raw instanceof Map);
+ Map<?,?> map = (Map<?,?>) raw;
+ MapModelTest.One one = (MapModelTest.One) map.get("setYToTen");
+ assertNotNull(one);
+ one.fb.call(model, null);
+ execUse = 3;
+ }
+ assertEquals(model.getX(), 1, "One");
+ assertEquals(model.getY(), 10, "Ten");
+ exec.assertCount(execUse, "Executor used");
+ }
+
+ private static class MockTechnology implements Technology<Object> {
+ private final List<String> mutated = new ArrayList<String>();
+
+ @Override
+ public Object wrapModel(Object model) {
+ return this;
+ }
+
+ @Override
+ public void valueHasMutated(Object data, String propertyName) {
+ mutated.add(propertyName);
+ }
+
+ @Override
+ public void bind(PropertyBinding b, Object model, Object data) {
+ }
+
+ @Override
+ public void expose(FunctionBinding fb, Object model, Object d) {
+ }
+
+ @Override
+ public void applyBindings(Object data) {
+ }
+
+ @Override
+ public Object wrapArray(Object[] arr) {
+ return arr;
+ }
+
+ @Override
+ public <M> M toModel(Class<M> modelClass, Object data) {
+ return modelClass.cast(data);
+ }
+
+ @Override
+ public void runSafe(Runnable r) {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/ModelsTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/ModelsTest.java b/json/src/test/java/net/java/html/json/ModelsTest.java
new file mode 100644
index 0000000..7e7c5b0
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/ModelsTest.java
@@ -0,0 +1,72 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class ModelsTest {
+
+ public ModelsTest() {
+ }
+
+ @Test public void peopleAreModel() {
+ assertTrue(Models.isModel(People.class), "People are generated class");
+ }
+
+ @Test public void personIsModel() {
+ assertTrue(Models.isModel(Person.class), "Person is generated class");
+ }
+
+ @Test public void implClassIsNotModel() {
+ assertFalse(Models.isModel(PersonImpl.class), "Impl is not model");
+ }
+
+ @Test public void randomClassIsNotModel() {
+ assertFalse(Models.isModel(StringBuilder.class), "JDK classes are not model");
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/OperationTest.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/OperationTest.java b/json/src/test/java/net/java/html/json/OperationTest.java
new file mode 100644
index 0000000..db30837
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/OperationTest.java
@@ -0,0 +1,125 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.io.IOException;
+import java.util.Arrays;
+import net.java.html.BrwsrCtx;
+import org.netbeans.html.context.spi.Contexts;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "OpModel", properties = {
+ @Property(name = "names", type = String.class, array = true)
+})
+public class OperationTest {
+ @ModelOperation static void add(OpModel m, String name, BrwsrCtx exp) {
+ assertSame(BrwsrCtx.findDefault(OpModel.class), exp, "Context is passed in");
+ m.getNames().add(name);
+ }
+
+ @ModelOperation static void add(OpModel m, int times, String name) throws IOException {
+ while (times-- > 0) {
+ m.getNames().add(name.toUpperCase());
+ }
+ }
+
+ @ModelOperation static void copy(OpModel m, OpModel orig) {
+ m.getNames().clear();
+ m.getNames().addAll(orig.getNames());
+ }
+
+ @Test public void addOneToTheModel() {
+ BrwsrCtx ctx = Contexts.newBuilder().build();
+ OpModel m = Models.bind(new OpModel("One"), ctx);
+ m.add("Second", ctx);
+ assertEquals(m.getNames().size(), 2, "Both are there: " + m.getNames());
+ }
+
+ @Test public void addTwoUpperCasesToTheModel() {
+ BrwsrCtx ctx = Contexts.newBuilder().build();
+ OpModel m = Models.bind(new OpModel("One"), ctx);
+ m.add(2, "Second");
+ assertEquals(m.getNames().size(), 3, "Both are there: " + m.getNames());
+ assertEquals(m.getNames().get(1), "SECOND", "Converted to upper case");
+ assertEquals(m.getNames().get(2), "SECOND", "Also converted to upper case");
+ }
+
+ @Test public void noAnnonymousInnerClass() {
+ int cnt = 0;
+ for (Class<?> c : OpModel.class.getDeclaredClasses()) {
+ cnt++;
+ int dolar = c.getName().lastIndexOf('$');
+ assertNotEquals(dolar, -1, "There is dolar in : " + c.getName());
+ String res = c.getName().substring(dolar + 1);
+ try {
+ int number = Integer.parseInt(res);
+ if (number == 1) {
+ // one is OK, #2 was a problem
+ continue;
+ }
+ fail("There seems to annonymous innerclass! " + c.getName() + "\nImplements: "
+ + Arrays.toString(c.getInterfaces()) + " extends: " + c.getSuperclass()
+ );
+ } catch (NumberFormatException ex) {
+ // OK, go on
+ }
+ }
+ if (cnt == 0) {
+ fail("There should be at least one inner class: " + cnt);
+ }
+ }
+
+ @Test public void copyOperation() {
+ OpModel orig = new OpModel("Ahoj", "Jardo");
+ OpModel n = new OpModel();
+ n.copy(orig);
+ assertEquals(n.getNames().size(), 2, "Two elems");
+ assertEquals(n.getNames().get(0), "Ahoj", "1st");
+ assertEquals(n.getNames().get(1), "Jardo", "2nd");
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/json/src/test/java/net/java/html/json/PersonImpl.java
----------------------------------------------------------------------
diff --git a/json/src/test/java/net/java/html/json/PersonImpl.java b/json/src/test/java/net/java/html/json/PersonImpl.java
new file mode 100644
index 0000000..581bb6a
--- /dev/null
+++ b/json/src/test/java/net/java/html/json/PersonImpl.java
@@ -0,0 +1,135 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package net.java.html.json;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@Model(className = "Person", properties = {
+ @Property(name = "firstName", type = String.class),
+ @Property(name = "lastName", type = String.class),
+ @Property(name = "sex", type = Sex.class)
+})
+final class PersonImpl {
+ @ComputedProperty
+ public static String fullName(String firstName, String lastName) {
+ return firstName + " " + lastName;
+ }
+
+ @ComputedProperty
+ public static List<String> bothNames(String firstName, String lastName) {
+ return Arrays.asList(firstName, lastName);
+ }
+
+ @ComputedProperty
+ public static String sexType(Sex sex) {
+ return sex == null ? "unknown" : sex.toString();
+ }
+
+ @ComputedProperty static Sex attractedBy(Sex sex) {
+ if (sex == null) {
+ return null;
+ }
+ return sex == Sex.MALE ? Sex.FEMALE : Sex.MALE;
+ }
+
+ @Function
+ static void changeSex(Person p, String data) {
+ if (data != null) {
+ p.setSex(Sex.valueOf(data));
+ return;
+ }
+ if (p.getSex() == Sex.MALE) {
+ p.setSex(Sex.FEMALE);
+ } else {
+ p.setSex(Sex.MALE);
+ }
+ }
+
+ @Model(className = "People", instance = true, targetId="myPeople", properties = {
+ @Property(array = true, name = "info", type = Person.class),
+ @Property(array = true, name = "nicknames", type = String.class),
+ @Property(array = true, name = "age", type = int.class),
+ @Property(array = true, name = "sex", type = Sex.class)
+ })
+ public static class PeopleImpl {
+ private int addAgeCount;
+ private Runnable onInfoChange;
+
+ @ModelOperation void onInfoChange(People self, Runnable r) {
+ onInfoChange = r;
+ }
+
+ @ModelOperation void addAge42(People p) {
+ p.getAge().add(42);
+ addAgeCount++;
+ }
+
+ @OnReceive(url = "url", method = "WebSocket", data = String.class)
+ void innerClass(People p, String d) {
+ }
+
+ @Function void inInnerClass(People p, Person data, int x, double y, String nick) throws IOException {
+ p.getInfo().add(data);
+ p.getAge().add(x);
+ p.getAge().add((int)y);
+ p.getNicknames().add(nick);
+ }
+
+ @ModelOperation void readAddAgeCount(People p, int[] holder, Runnable whenDone) {
+ holder[0] = addAgeCount;
+ whenDone.run();
+ }
+
+ @OnPropertyChange("age") void infoChange(People p) {
+ if (onInfoChange != null) {
+ onInfoChange.run();
+ }
+ }
+ }
+}