During the API review process (bug 246133) the reviewers decided that in order to include html4j to NetBeans Platform, we need to stop using org.apidesign namespace and switch to NetBeans one. Repackaging all SPI packages into org.netbeans.html.smthng.spi.
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 org.netbeans.html.json.impl;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.lang.reflect.InvocationTargetException;
48 import java.util.HashMap;
49 import java.util.List;
51 import net.java.html.BrwsrCtx;
52 import net.java.html.json.ComputedProperty;
53 import net.java.html.json.Model;
54 import net.java.html.json.Models;
55 import net.java.html.json.Property;
56 import org.netbeans.html.context.spi.Contexts;
57 import org.netbeans.html.json.spi.FunctionBinding;
58 import org.netbeans.html.json.spi.JSONCall;
59 import org.netbeans.html.json.spi.PropertyBinding;
60 import org.netbeans.html.json.spi.Technology;
61 import org.netbeans.html.json.spi.Transfer;
62 import static org.testng.Assert.*;
63 import org.testng.annotations.BeforeMethod;
64 import org.testng.annotations.Test;
68 * @author Jaroslav Tulach
70 public class DeepChangeTest {
71 private MapTechnology t;
74 @BeforeMethod public void initTechnology() {
75 t = new MapTechnology();
76 c = Contexts.newBuilder().register(Technology.class, t, 1).
77 register(Transfer.class, t, 1).build();
80 @Model(className = "MyX", properties = {
81 @Property(name = "one", type = MyY.class),
82 @Property(name = "all", type = MyY.class, array = true)
85 @ComputedProperty @Transitive(deep = true)
86 static String oneName(MyY one) {
87 return one.getValue();
90 static String sndName(MyY one) {
91 return one.getValue().toUpperCase();
93 @ComputedProperty @Transitive(deep = false)
94 static String noName(MyY one) {
95 return one.getValue().toUpperCase();
97 @ComputedProperty @Transitive(deep = true)
98 static String thrdName(MyY one) {
99 return "X" + one.getCount();
103 static String allNames(List<MyY> all) {
104 StringBuilder sb = new StringBuilder();
106 sb.append(y.getValue());
108 return sb.toString();
111 @ComputedProperty @Transitive(deep = true)
112 static String firstFromNames(List<MyY> all) {
114 if (y != null && y.getValue() != null) {
121 @Model(className = "MyY", properties = {
122 @Property(name = "value", type = String.class),
123 @Property(name = "count", type = int.class)
127 @Model(className = "MyOverall", properties = {
128 @Property(name = "x", type = MyX.class)
130 static class Overall {
131 @ComputedProperty @Transitive(deep = true)
132 static String valueAccross(MyX x) {
133 return x.getFirstFromNames();
137 @Test public void isTransitiveChangeNotifiedProperly() throws Exception {
139 new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
140 ), c).applyBindings();
142 Map m = (Map)Models.toRaw(p);
143 Object v = m.get("oneName");
144 assertNotNull(v, "Value should be in the map");
145 assertEquals(v.getClass(), One.class, "It is instance of One");
147 assertEquals(o.changes, 0, "No changes so far");
148 assertTrue(o.pb.isReadOnly(), "Derived property");
149 assertEquals(o.get(), "Ahoj");
151 p.getOne().setValue("Nazdar");
153 assertEquals(o.get(), "Nazdar");
154 assertEquals(o.changes, 1, "One change so far");
157 @Test public void isTransitiveChangeInArrayNotifiedProperly() throws Exception {
159 new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
160 ), c).applyBindings();
162 Map m = (Map)Models.toRaw(p);
163 Object v = m.get("allNames");
164 assertNotNull(v, "Value should be in the map");
165 assertEquals(v.getClass(), One.class, "It is instance of One");
167 assertEquals(o.changes, 0, "No changes so far");
168 assertTrue(o.pb.isReadOnly(), "Derived property");
169 assertEquals(o.get(), "HiHello");
171 p.getAll().get(0).setValue("Nazdar");
173 assertEquals(o.get(), "NazdarHello");
174 assertEquals(o.changes, 1, "One change so far");
177 @Test public void addingIntoArray() throws Exception {
179 new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
180 ), c).applyBindings();
182 Map m = (Map)Models.toRaw(p);
183 Object v = m.get("allNames");
184 assertNotNull(v, "Value should be in the map");
185 assertEquals(v.getClass(), One.class, "It is instance of One");
187 assertEquals(o.changes, 0, "No changes so far");
188 assertTrue(o.pb.isReadOnly(), "Derived property");
189 assertEquals(o.get(), "HiHello");
191 MyY y = new MyY("Cus", 1);
194 assertEquals(o.changes, 1, "One change so far");
195 assertEquals(o.get(), "HiHelloCus");
197 y.setValue("Nazdar");
199 assertEquals(o.changes, 2, "2nd change so far");
200 assertEquals(o.get(), "HiHelloNazdar");
202 y.setValue("Zdravim");
204 assertEquals(o.changes, 3, "3rd change so far");
205 assertEquals(o.get(), "HiHelloZdravim");
208 @Test public void firstChangeInArrayNotifiedProperly() throws Exception {
210 new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
211 ), c).applyBindings();
213 Map m = (Map)Models.toRaw(p);
214 Object v = m.get("firstFromNames");
215 assertNotNull(v, "Value should be in the map");
216 assertEquals(v.getClass(), One.class, "It is instance of One");
218 assertEquals(o.changes, 0, "No changes so far");
219 assertTrue(o.pb.isReadOnly(), "Derived property");
220 assertEquals(o.get(), "Hi");
222 p.getAll().get(0).setValue("Nazdar");
224 assertEquals(o.get(), "Nazdar");
225 assertEquals(o.changes, 1, "One change so far");
228 @Test public void firstChangeInArrayToNull() throws Exception {
230 new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
231 ), c).applyBindings();
233 Map m = (Map)Models.toRaw(p);
234 Object v = m.get("firstFromNames");
235 assertNotNull(v, "Value should be in the map");
236 assertEquals(v.getClass(), One.class, "It is instance of One");
238 assertEquals(o.changes, 0, "No changes so far");
239 assertTrue(o.pb.isReadOnly(), "Derived property");
240 assertEquals(o.get(), "Hi");
242 p.getAll().get(0).setValue(null);
244 assertEquals(o.get(), "Hello");
245 assertEquals(o.changes, 1, "One change so far");
247 p.getAll().get(0).setValue("Nazdar");
249 assertEquals(o.get(), "Nazdar");
250 assertEquals(o.changes, 2, "2nd change so far");
253 @Test public void firstChangeInArrayNotifiedTransitively() throws Exception {
254 MyOverall p = Models.bind(
255 new MyOverall(new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999))
256 ), c).applyBindings();
258 Map m = (Map)Models.toRaw(p);
259 Object v = m.get("valueAccross");
260 assertNotNull(v, "Value should be in the map");
261 assertEquals(v.getClass(), One.class, "It is instance of One");
263 assertEquals(o.changes, 0, "No changes so far");
264 assertTrue(o.pb.isReadOnly(), "Derived property");
265 assertEquals(o.get(), "Hi");
267 p.getX().getAll().get(0).setValue("Nazdar");
269 assertEquals(o.get(), "Nazdar");
270 assertEquals(o.changes, 1, "One change so far");
273 @Test public void secondChangeInArrayIgnored() throws Exception {
275 new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
276 ), c).applyBindings();
278 Map m = (Map)Models.toRaw(p);
279 Object v = m.get("firstFromNames");
280 assertNotNull(v, "Value should be in the map");
281 assertEquals(v.getClass(), One.class, "It is instance of One");
283 assertEquals(o.changes, 0, "No changes so far");
284 assertTrue(o.pb.isReadOnly(), "Derived property");
285 assertEquals(o.get(), "Hi");
287 p.getAll().get(1).setValue("Nazdar");
289 assertEquals(o.get(), "Hi");
290 assertEquals(o.changes, 0, "No change so far");
293 @Test public void changeInArraySizeNeedsToBeRecomputed() throws Exception {
295 new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
296 ), c).applyBindings();
298 Map m = (Map)Models.toRaw(p);
299 Object v = m.get("firstFromNames");
300 assertNotNull(v, "Value should be in the map");
301 assertEquals(v.getClass(), One.class, "It is instance of One");
303 assertEquals(o.changes, 0, "No changes so far");
304 assertTrue(o.pb.isReadOnly(), "Derived property");
305 assertEquals(o.get(), "Hi");
307 p.getAll().remove(1);
309 assertEquals(o.get(), "Hi");
310 assertEquals(o.changes, 1, "This required a change");
313 @Test public void doublePropertyChangeNotified() throws Exception {
315 new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
316 ), c).applyBindings();
318 Map m = (Map)Models.toRaw(p);
319 Object v = m.get("oneName");
320 assertNotNull(v, "Value should be in the map");
321 Object v2 = m.get("sndName");
322 assertNotNull(v2, "Value2 should be in the map");
325 assertEquals(o.changes, 0, "No changes so far");
326 assertEquals(o2.changes, 0, "No changes so far");
327 assertTrue(o.pb.isReadOnly(), "Derived property");
328 assertEquals(o.get(), "Ahoj");
329 assertEquals(o2.get(), "AHOJ");
331 p.getOne().setValue("Nazdar");
333 assertEquals(o.get(), "Nazdar");
334 assertEquals(o.changes, 1, "One change so far");
335 assertEquals(o2.changes, 1, "One change so far");
336 assertEquals(o2.get(), "NAZDAR");
339 @Test public void onlyAffectedPropertyChangeNotified() throws Exception {
341 new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
342 ), c).applyBindings();
344 Map m = (Map)Models.toRaw(p);
345 Object v = m.get("oneName");
346 assertNotNull(v, "Value should be in the map");
347 Object v2 = m.get("thrdName");
348 assertNotNull(v2, "Value2 should be in the map");
351 assertEquals(o.changes, 0, "No changes so far");
352 assertEquals(o2.changes, 0, "No changes so far");
353 assertTrue(o.pb.isReadOnly(), "Derived property");
354 assertEquals(o.get(), "Ahoj");
355 assertEquals(o2.get(), "X0");
357 p.getOne().setCount(10);
359 assertEquals(o.get(), "Ahoj");
360 assertEquals(o.changes, 0, "Still no change");
361 assertEquals(o2.changes, 1, "One change so far");
362 assertEquals(o2.get(), "X10");
365 @Test public void onlyDeepPropsAreNotified() throws Exception {
367 new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
368 ), c).applyBindings();
370 Map m = (Map)Models.toRaw(p);
371 Object v = m.get("oneName");
372 assertNotNull(v, "Value should be in the map");
373 Object v2 = m.get("noName");
374 assertNotNull(v2, "Value2 should be in the map");
377 assertEquals(o.changes, 0, "No changes so far");
378 assertEquals(o2.changes, 0, "No changes so far");
379 assertTrue(o.pb.isReadOnly(), "Derived property");
380 assertEquals(o.get(), "Ahoj");
381 assertEquals(o2.get(), "AHOJ");
383 p.getOne().setValue("Nazdar");
385 assertEquals(o.get(), "Nazdar");
386 assertEquals(o.changes, 1, "One change so far");
387 assertEquals(o2.changes, 0, "This change is not noticed");
388 assertEquals(o2.get(), "NAZDAR", "but property value changes when computed");
391 static final class One {
394 final PropertyBinding pb;
395 final FunctionBinding fb;
397 One(Object m, PropertyBinding pb) throws NoSuchMethodException {
402 One(Object m, FunctionBinding fb) throws NoSuchMethodException {
407 Object get() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
408 return pb.getValue();
411 void set(Object v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
416 static final class MapTechnology
417 implements Technology<Map<String, One>>, Transfer {
420 public Map<String, One> wrapModel(Object model) {
421 return new HashMap<String, One>();
425 public void valueHasMutated(Map<String, One> data, String propertyName) {
426 One p = data.get(propertyName);
433 public void bind(PropertyBinding b, Object model, Map<String, One> data) {
435 One o = new One(model, b);
436 data.put(b.getPropertyName(), o);
437 } catch (NoSuchMethodException ex) {
438 throw new IllegalStateException(ex);
443 public void expose(FunctionBinding fb, Object model, Map<String, One> data) {
445 data.put(fb.getFunctionName(), new One(model, fb));
446 } catch (NoSuchMethodException ex) {
447 throw new IllegalStateException(ex);
452 public void applyBindings(Map<String, One> data) {
456 public Object wrapArray(Object[] arr) {
461 public void extract(Object obj, String[] props, Object[] values) {
462 Map<?, ?> map = obj instanceof Map ? (Map<?, ?>) obj : null;
463 for (int i = 0; i < Math.min(props.length, values.length); i++) {
467 values[i] = map.get(props[i]);
468 if (values[i] instanceof One) {
469 values[i] = ((One) values[i]).pb.getValue();
476 public void loadJSON(JSONCall call) {
477 call.notifyError(new UnsupportedOperationException());
481 public <M> M toModel(Class<M> modelClass, Object data) {
482 return modelClass.cast(data);
486 public Object toJSON(InputStream is) throws IOException {
487 throw new IOException();
491 public void runSafe(Runnable r) {
492 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.