Moving the ability to control whether the value change is transitive or not out from the public API. Will make it default. DeepWatch
authorJaroslav Tulach <jtulach@netbeans.org>
Sat, 02 Aug 2014 07:28:37 +0200
branchDeepWatch
changeset 787a0e8f185c0d4
parent 786 6a3b43d13a5c
child 788 f8ac4d547ad3
Moving the ability to control whether the value change is transitive or not out from the public API. Will make it default.
json/src/main/java/net/java/html/json/ComputedProperty.java
json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
json/src/main/java/org/netbeans/html/json/impl/Transitive.java
json/src/test/java/net/java/html/json/DeepChangeTest.java
json/src/test/java/net/java/html/json/MapModelTest.java
json/src/test/java/net/java/html/json/ToDoTest.java
json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java
json/src/test/java/org/netbeans/html/json/impl/ToDoTest.java
     1.1 --- a/json/src/main/java/net/java/html/json/ComputedProperty.java	Sat Aug 02 07:13:47 2014 +0200
     1.2 +++ b/json/src/main/java/net/java/html/json/ComputedProperty.java	Sat Aug 02 07:28:37 2014 +0200
     1.3 @@ -71,5 +71,4 @@
     1.4  @Retention(RetentionPolicy.SOURCE)
     1.5  @Target(ElementType.METHOD)
     1.6  public @interface ComputedProperty {
     1.7 -    boolean deep() default false;
     1.8  }
     2.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Sat Aug 02 07:13:47 2014 +0200
     2.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Sat Aug 02 07:28:37 2014 +0200
     2.3 @@ -607,6 +607,7 @@
     2.4                  continue;
     2.5              }
     2.6              final ComputedProperty cp = e.getAnnotation(ComputedProperty.class);
     2.7 +            final Transitive tp = e.getAnnotation(Transitive.class);
     2.8              if (cp == null) {
     2.9                  continue;
    2.10              }
    2.11 @@ -677,7 +678,7 @@
    2.12                  depends.add(new String[] { sn, gs[0] });
    2.13              }
    2.14              w.write("    try {\n");
    2.15 -            if (cp.deep()) {
    2.16 +            if (tp != null && tp.deep()) {
    2.17                  w.write("      proto.acquireLock(\"" + sn + "\");\n");
    2.18              } else {
    2.19                  w.write("      proto.acquireLock();\n");
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/Transitive.java	Sat Aug 02 07:28:37 2014 +0200
     3.3 @@ -0,0 +1,60 @@
     3.4 +/**
     3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 + *
     3.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     3.8 + *
     3.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    3.10 + * Other names may be trademarks of their respective owners.
    3.11 + *
    3.12 + * The contents of this file are subject to the terms of either the GNU
    3.13 + * General Public License Version 2 only ("GPL") or the Common
    3.14 + * Development and Distribution License("CDDL") (collectively, the
    3.15 + * "License"). You may not use this file except in compliance with the
    3.16 + * License. You can obtain a copy of the License at
    3.17 + * http://www.netbeans.org/cddl-gplv2.html
    3.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    3.19 + * specific language governing permissions and limitations under the
    3.20 + * License.  When distributing the software, include this License Header
    3.21 + * Notice in each file and include the License file at
    3.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    3.23 + * particular file as subject to the "Classpath" exception as provided
    3.24 + * by Oracle in the GPL Version 2 section of the License file that
    3.25 + * accompanied this code. If applicable, add the following below the
    3.26 + * License Header, with the fields enclosed by brackets [] replaced by
    3.27 + * your own identifying information:
    3.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    3.29 + *
    3.30 + * Contributor(s):
    3.31 + *
    3.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    3.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    3.34 + *
    3.35 + * If you wish your version of this file to be governed by only the CDDL
    3.36 + * or only the GPL Version 2, indicate your decision by adding
    3.37 + * "[Contributor] elects to include this software in this distribution
    3.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    3.39 + * single choice of license, a recipient has the option to distribute
    3.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    3.41 + * to extend the choice of license to its licensees as provided above.
    3.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    3.43 + * Version 2 license, then the option applies only if the new code is
    3.44 + * made subject to such option by the copyright holder.
    3.45 + */
    3.46 +package org.netbeans.html.json.impl;
    3.47 +
    3.48 +import java.lang.annotation.ElementType;
    3.49 +import java.lang.annotation.Retention;
    3.50 +import java.lang.annotation.RetentionPolicy;
    3.51 +import java.lang.annotation.Target;
    3.52 +
    3.53 +/** A way to control {@link ComputedProperty} behavior - whether it tracks
    3.54 + * deeply or not. Not public yet, maybe it won't be needed in the API at all.
    3.55 + * Used in tests only.
    3.56 + * 
    3.57 + * @author Jaroslav Tulach
    3.58 + */
    3.59 +@Retention(RetentionPolicy.SOURCE)
    3.60 +@Target(ElementType.METHOD)
    3.61 +@interface Transitive {
    3.62 +    boolean deep() default false;
    3.63 +}
     4.1 --- a/json/src/test/java/net/java/html/json/DeepChangeTest.java	Sat Aug 02 07:13:47 2014 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,382 +0,0 @@
     4.4 -/**
     4.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     4.6 - *
     4.7 - * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     4.8 - *
     4.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    4.10 - * Other names may be trademarks of their respective owners.
    4.11 - *
    4.12 - * The contents of this file are subject to the terms of either the GNU
    4.13 - * General Public License Version 2 only ("GPL") or the Common
    4.14 - * Development and Distribution License("CDDL") (collectively, the
    4.15 - * "License"). You may not use this file except in compliance with the
    4.16 - * License. You can obtain a copy of the License at
    4.17 - * http://www.netbeans.org/cddl-gplv2.html
    4.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    4.19 - * specific language governing permissions and limitations under the
    4.20 - * License.  When distributing the software, include this License Header
    4.21 - * Notice in each file and include the License file at
    4.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    4.23 - * particular file as subject to the "Classpath" exception as provided
    4.24 - * by Oracle in the GPL Version 2 section of the License file that
    4.25 - * accompanied this code. If applicable, add the following below the
    4.26 - * License Header, with the fields enclosed by brackets [] replaced by
    4.27 - * your own identifying information:
    4.28 - * "Portions Copyrighted [year] [name of copyright owner]"
    4.29 - *
    4.30 - * Contributor(s):
    4.31 - *
    4.32 - * The Original Software is NetBeans. The Initial Developer of the Original
    4.33 - * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    4.34 - *
    4.35 - * If you wish your version of this file to be governed by only the CDDL
    4.36 - * or only the GPL Version 2, indicate your decision by adding
    4.37 - * "[Contributor] elects to include this software in this distribution
    4.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
    4.39 - * single choice of license, a recipient has the option to distribute
    4.40 - * your version of this file under either the CDDL, the GPL Version 2 or
    4.41 - * to extend the choice of license to its licensees as provided above.
    4.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
    4.43 - * Version 2 license, then the option applies only if the new code is
    4.44 - * made subject to such option by the copyright holder.
    4.45 - */
    4.46 -package net.java.html.json;
    4.47 -
    4.48 -import java.util.List;
    4.49 -import java.util.Map;
    4.50 -import net.java.html.BrwsrCtx;
    4.51 -import net.java.html.json.MapModelTest.MapTechnology;
    4.52 -import net.java.html.json.MapModelTest.One;
    4.53 -import org.apidesign.html.context.spi.Contexts;
    4.54 -import org.apidesign.html.json.spi.Technology;
    4.55 -import org.apidesign.html.json.spi.Transfer;
    4.56 -import static org.testng.Assert.*;
    4.57 -import org.testng.annotations.BeforeMethod;
    4.58 -import org.testng.annotations.Test;
    4.59 -
    4.60 -/**
    4.61 - *
    4.62 - * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.63 - */
    4.64 -public class DeepChangeTest {
    4.65 -    private MapTechnology t;
    4.66 -    private BrwsrCtx c;
    4.67 -
    4.68 -    @BeforeMethod public void initTechnology() {
    4.69 -        t = new MapTechnology();
    4.70 -        c = Contexts.newBuilder().register(Technology.class, t, 1).
    4.71 -            register(Transfer.class, t, 1).build();
    4.72 -    }
    4.73 -    
    4.74 -    @Model(className = "MyX", properties = {
    4.75 -        @Property(name = "one", type = MyY.class),
    4.76 -        @Property(name = "all", type = MyY.class, array = true)
    4.77 -    })
    4.78 -    static class X {
    4.79 -        @ComputedProperty(deep = true) 
    4.80 -        static String oneName(MyY one) {
    4.81 -            return one.getValue();
    4.82 -        }
    4.83 -        @ComputedProperty(deep = true) 
    4.84 -        static String sndName(MyY one) {
    4.85 -            return one.getValue().toUpperCase();
    4.86 -        }
    4.87 -        @ComputedProperty(deep = false) 
    4.88 -        static String noName(MyY one) {
    4.89 -            return one.getValue().toUpperCase();
    4.90 -        }
    4.91 -        @ComputedProperty(deep = true) 
    4.92 -        static String thrdName(MyY one) {
    4.93 -            return "X" + one.getCount();
    4.94 -        }
    4.95 -        
    4.96 -        @ComputedProperty(deep = true)
    4.97 -        static String allNames(List<MyY> all) {
    4.98 -            StringBuilder sb = new StringBuilder();
    4.99 -            for (MyY y : all) {
   4.100 -                sb.append(y.getValue());
   4.101 -            }
   4.102 -            return sb.toString();
   4.103 -        }
   4.104 -
   4.105 -        @ComputedProperty(deep = true)
   4.106 -        static String firstFromNames(List<MyY> all) {
   4.107 -            for (MyY y : all) {
   4.108 -                if (y != null && y.getValue() != null) {
   4.109 -                    return y.getValue();
   4.110 -                }
   4.111 -            }
   4.112 -            return null;
   4.113 -        }
   4.114 -    }
   4.115 -    @Model(className = "MyY", properties = {
   4.116 -        @Property(name = "value", type = String.class),
   4.117 -        @Property(name = "count", type = int.class)
   4.118 -    })
   4.119 -    static class Y {
   4.120 -    }
   4.121 -    @Model(className = "MyOverall", properties = {
   4.122 -        @Property(name = "x", type = MyX.class)
   4.123 -    })
   4.124 -    static class Overall {
   4.125 -        @ComputedProperty(deep = true) 
   4.126 -        static String valueAccross(MyX x) {
   4.127 -            return x.getFirstFromNames();
   4.128 -        }
   4.129 -    }
   4.130 -    
   4.131 -    @Test public void isTransitiveChangeNotifiedProperly() throws Exception {
   4.132 -        MyX p = Models.bind(
   4.133 -            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   4.134 -        ), c).applyBindings();
   4.135 -        
   4.136 -        Map m = (Map)Models.toRaw(p);
   4.137 -        Object v = m.get("oneName");
   4.138 -        assertNotNull(v, "Value should be in the map");
   4.139 -        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.140 -        One o = (One)v;
   4.141 -        assertEquals(o.changes, 0, "No changes so far");
   4.142 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.143 -        assertEquals(o.get(), "Ahoj");
   4.144 -
   4.145 -        p.getOne().setValue("Nazdar");
   4.146 -        
   4.147 -        assertEquals(o.get(), "Nazdar");
   4.148 -        assertEquals(o.changes, 1, "One change so far");
   4.149 -    }
   4.150 -    
   4.151 -    @Test public void isTransitiveChangeInArrayNotifiedProperly() throws Exception {
   4.152 -        MyX p = Models.bind(
   4.153 -            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   4.154 -        ), c).applyBindings();
   4.155 -        
   4.156 -        Map m = (Map)Models.toRaw(p);
   4.157 -        Object v = m.get("allNames");
   4.158 -        assertNotNull(v, "Value should be in the map");
   4.159 -        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.160 -        One o = (One)v;
   4.161 -        assertEquals(o.changes, 0, "No changes so far");
   4.162 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.163 -        assertEquals(o.get(), "HiHello");
   4.164 -
   4.165 -        p.getAll().get(0).setValue("Nazdar");
   4.166 -        
   4.167 -        assertEquals(o.get(), "NazdarHello");
   4.168 -        assertEquals(o.changes, 1, "One change so far");
   4.169 -    }
   4.170 -
   4.171 -    @Test public void addingIntoArray() throws Exception {
   4.172 -        MyX p = Models.bind(
   4.173 -            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   4.174 -        ), c).applyBindings();
   4.175 -        
   4.176 -        Map m = (Map)Models.toRaw(p);
   4.177 -        Object v = m.get("allNames");
   4.178 -        assertNotNull(v, "Value should be in the map");
   4.179 -        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.180 -        One o = (One)v;
   4.181 -        assertEquals(o.changes, 0, "No changes so far");
   4.182 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.183 -        assertEquals(o.get(), "HiHello");
   4.184 -
   4.185 -        MyY y = new MyY("Cus", 1);
   4.186 -        p.getAll().add(y);
   4.187 -        
   4.188 -        assertEquals(o.changes, 1, "One change so far");
   4.189 -        assertEquals(o.get(), "HiHelloCus");
   4.190 -        
   4.191 -        y.setValue("Nazdar");
   4.192 -        
   4.193 -        assertEquals(o.changes, 2, "2nd change so far");
   4.194 -        assertEquals(o.get(), "HiHelloNazdar");
   4.195 -        
   4.196 -        y.setValue("Zdravim");
   4.197 -
   4.198 -        assertEquals(o.changes, 3, "3rd change so far");
   4.199 -        assertEquals(o.get(), "HiHelloZdravim");
   4.200 -    }
   4.201 -    
   4.202 -    @Test public void firstChangeInArrayNotifiedProperly() throws Exception {
   4.203 -        MyX p = Models.bind(
   4.204 -            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   4.205 -        ), c).applyBindings();
   4.206 -        
   4.207 -        Map m = (Map)Models.toRaw(p);
   4.208 -        Object v = m.get("firstFromNames");
   4.209 -        assertNotNull(v, "Value should be in the map");
   4.210 -        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.211 -        One o = (One)v;
   4.212 -        assertEquals(o.changes, 0, "No changes so far");
   4.213 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.214 -        assertEquals(o.get(), "Hi");
   4.215 -
   4.216 -        p.getAll().get(0).setValue("Nazdar");
   4.217 -        
   4.218 -        assertEquals(o.get(), "Nazdar");
   4.219 -        assertEquals(o.changes, 1, "One change so far");
   4.220 -    }
   4.221 -    
   4.222 -    @Test public void firstChangeInArrayToNull() throws Exception {
   4.223 -        MyX p = Models.bind(
   4.224 -            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   4.225 -        ), c).applyBindings();
   4.226 -        
   4.227 -        Map m = (Map)Models.toRaw(p);
   4.228 -        Object v = m.get("firstFromNames");
   4.229 -        assertNotNull(v, "Value should be in the map");
   4.230 -        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.231 -        One o = (One)v;
   4.232 -        assertEquals(o.changes, 0, "No changes so far");
   4.233 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.234 -        assertEquals(o.get(), "Hi");
   4.235 -
   4.236 -        p.getAll().get(0).setValue(null);
   4.237 -        
   4.238 -        assertEquals(o.get(), "Hello");
   4.239 -        assertEquals(o.changes, 1, "One change so far");
   4.240 -        
   4.241 -        p.getAll().get(0).setValue("Nazdar");
   4.242 -
   4.243 -        assertEquals(o.get(), "Nazdar");
   4.244 -        assertEquals(o.changes, 2, "2nd change so far");
   4.245 -    }
   4.246 -    
   4.247 -    @Test public void firstChangeInArrayNotifiedTransitively() throws Exception {
   4.248 -        MyOverall p = Models.bind(
   4.249 -            new MyOverall(new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999))
   4.250 -        ), c).applyBindings();
   4.251 -        
   4.252 -        Map m = (Map)Models.toRaw(p);
   4.253 -        Object v = m.get("valueAccross");
   4.254 -        assertNotNull(v, "Value should be in the map");
   4.255 -        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.256 -        One o = (One)v;
   4.257 -        assertEquals(o.changes, 0, "No changes so far");
   4.258 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.259 -        assertEquals(o.get(), "Hi");
   4.260 -
   4.261 -        p.getX().getAll().get(0).setValue("Nazdar");
   4.262 -        
   4.263 -        assertEquals(o.get(), "Nazdar");
   4.264 -        assertEquals(o.changes, 1, "One change so far");
   4.265 -    }
   4.266 -    
   4.267 -    @Test public void secondChangeInArrayIgnored() throws Exception {
   4.268 -        MyX p = Models.bind(
   4.269 -            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   4.270 -        ), c).applyBindings();
   4.271 -        
   4.272 -        Map m = (Map)Models.toRaw(p);
   4.273 -        Object v = m.get("firstFromNames");
   4.274 -        assertNotNull(v, "Value should be in the map");
   4.275 -        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.276 -        One o = (One)v;
   4.277 -        assertEquals(o.changes, 0, "No changes so far");
   4.278 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.279 -        assertEquals(o.get(), "Hi");
   4.280 -
   4.281 -        p.getAll().get(1).setValue("Nazdar");
   4.282 -        
   4.283 -        assertEquals(o.get(), "Hi");
   4.284 -        assertEquals(o.changes, 0, "No change so far");
   4.285 -    }
   4.286 -    
   4.287 -    @Test public void changeInArraySizeNeedsToBeRecomputed() throws Exception {
   4.288 -        MyX p = Models.bind(
   4.289 -            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   4.290 -        ), c).applyBindings();
   4.291 -        
   4.292 -        Map m = (Map)Models.toRaw(p);
   4.293 -        Object v = m.get("firstFromNames");
   4.294 -        assertNotNull(v, "Value should be in the map");
   4.295 -        assertEquals(v.getClass(), One.class, "It is instance of One");
   4.296 -        One o = (One)v;
   4.297 -        assertEquals(o.changes, 0, "No changes so far");
   4.298 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.299 -        assertEquals(o.get(), "Hi");
   4.300 -
   4.301 -        p.getAll().remove(1);
   4.302 -        
   4.303 -        assertEquals(o.get(), "Hi");
   4.304 -        assertEquals(o.changes, 1, "This required a change");
   4.305 -    }
   4.306 -    
   4.307 -    @Test public void doublePropertyChangeNotified() throws Exception {
   4.308 -        MyX p = Models.bind(
   4.309 -            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   4.310 -        ), c).applyBindings();
   4.311 -        
   4.312 -        Map m = (Map)Models.toRaw(p);
   4.313 -        Object v = m.get("oneName");
   4.314 -        assertNotNull(v, "Value should be in the map");
   4.315 -        Object v2 = m.get("sndName");
   4.316 -        assertNotNull(v2, "Value2 should be in the map");
   4.317 -        One o = (One)v;
   4.318 -        One o2 = (One)v2;
   4.319 -        assertEquals(o.changes, 0, "No changes so far");
   4.320 -        assertEquals(o2.changes, 0, "No changes so far");
   4.321 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.322 -        assertEquals(o.get(), "Ahoj");
   4.323 -        assertEquals(o2.get(), "AHOJ");
   4.324 -
   4.325 -        p.getOne().setValue("Nazdar");
   4.326 -        
   4.327 -        assertEquals(o.get(), "Nazdar");
   4.328 -        assertEquals(o.changes, 1, "One change so far");
   4.329 -        assertEquals(o2.changes, 1, "One change so far");
   4.330 -        assertEquals(o2.get(), "NAZDAR");
   4.331 -    }
   4.332 -    
   4.333 -    @Test public void onlyAffectedPropertyChangeNotified() throws Exception {
   4.334 -        MyX p = Models.bind(
   4.335 -            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   4.336 -        ), c).applyBindings();
   4.337 -        
   4.338 -        Map m = (Map)Models.toRaw(p);
   4.339 -        Object v = m.get("oneName");
   4.340 -        assertNotNull(v, "Value should be in the map");
   4.341 -        Object v2 = m.get("thrdName");
   4.342 -        assertNotNull(v2, "Value2 should be in the map");
   4.343 -        One o = (One)v;
   4.344 -        One o2 = (One)v2;
   4.345 -        assertEquals(o.changes, 0, "No changes so far");
   4.346 -        assertEquals(o2.changes, 0, "No changes so far");
   4.347 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.348 -        assertEquals(o.get(), "Ahoj");
   4.349 -        assertEquals(o2.get(), "X0");
   4.350 -
   4.351 -        p.getOne().setCount(10);
   4.352 -        
   4.353 -        assertEquals(o.get(), "Ahoj");
   4.354 -        assertEquals(o.changes, 0, "Still no change");
   4.355 -        assertEquals(o2.changes, 1, "One change so far");
   4.356 -        assertEquals(o2.get(), "X10");
   4.357 -    }
   4.358 -    
   4.359 -    @Test public void onlyDeepPropsAreNotified() throws Exception {
   4.360 -        MyX p = Models.bind(
   4.361 -            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   4.362 -        ), c).applyBindings();
   4.363 -        
   4.364 -        Map m = (Map)Models.toRaw(p);
   4.365 -        Object v = m.get("oneName");
   4.366 -        assertNotNull(v, "Value should be in the map");
   4.367 -        Object v2 = m.get("noName");
   4.368 -        assertNotNull(v2, "Value2 should be in the map");
   4.369 -        One o = (One)v;
   4.370 -        One o2 = (One)v2;
   4.371 -        assertEquals(o.changes, 0, "No changes so far");
   4.372 -        assertEquals(o2.changes, 0, "No changes so far");
   4.373 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   4.374 -        assertEquals(o.get(), "Ahoj");
   4.375 -        assertEquals(o2.get(), "AHOJ");
   4.376 -
   4.377 -        p.getOne().setValue("Nazdar");
   4.378 -        
   4.379 -        assertEquals(o.get(), "Nazdar");
   4.380 -        assertEquals(o.changes, 1, "One change so far");
   4.381 -        assertEquals(o2.changes, 0, "This change is not noticed");
   4.382 -        assertEquals(o2.get(), "NAZDAR", "but property value changes when computed");
   4.383 -    }
   4.384 -    
   4.385 -}
     5.1 --- a/json/src/test/java/net/java/html/json/MapModelTest.java	Sat Aug 02 07:13:47 2014 +0200
     5.2 +++ b/json/src/test/java/net/java/html/json/MapModelTest.java	Sat Aug 02 07:28:37 2014 +0200
     5.3 @@ -54,7 +54,6 @@
     5.4  import org.apidesign.html.json.spi.PropertyBinding;
     5.5  import org.apidesign.html.json.spi.Technology;
     5.6  import org.apidesign.html.json.spi.Transfer;
     5.7 -import org.netbeans.html.json.impl.JSON;
     5.8  import org.testng.annotations.BeforeMethod;
     5.9  import org.testng.annotations.Test;
    5.10  import static org.testng.Assert.*;
     6.1 --- a/json/src/test/java/net/java/html/json/ToDoTest.java	Sat Aug 02 07:13:47 2014 +0200
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,126 +0,0 @@
     6.4 -/**
     6.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     6.6 - *
     6.7 - * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     6.8 - *
     6.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    6.10 - * Other names may be trademarks of their respective owners.
    6.11 - *
    6.12 - * The contents of this file are subject to the terms of either the GNU
    6.13 - * General Public License Version 2 only ("GPL") or the Common
    6.14 - * Development and Distribution License("CDDL") (collectively, the
    6.15 - * "License"). You may not use this file except in compliance with the
    6.16 - * License. You can obtain a copy of the License at
    6.17 - * http://www.netbeans.org/cddl-gplv2.html
    6.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    6.19 - * specific language governing permissions and limitations under the
    6.20 - * License.  When distributing the software, include this License Header
    6.21 - * Notice in each file and include the License file at
    6.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    6.23 - * particular file as subject to the "Classpath" exception as provided
    6.24 - * by Oracle in the GPL Version 2 section of the License file that
    6.25 - * accompanied this code. If applicable, add the following below the
    6.26 - * License Header, with the fields enclosed by brackets [] replaced by
    6.27 - * your own identifying information:
    6.28 - * "Portions Copyrighted [year] [name of copyright owner]"
    6.29 - *
    6.30 - * Contributor(s):
    6.31 - *
    6.32 - * The Original Software is NetBeans. The Initial Developer of the Original
    6.33 - * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    6.34 - *
    6.35 - * If you wish your version of this file to be governed by only the CDDL
    6.36 - * or only the GPL Version 2, indicate your decision by adding
    6.37 - * "[Contributor] elects to include this software in this distribution
    6.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
    6.39 - * single choice of license, a recipient has the option to distribute
    6.40 - * your version of this file under either the CDDL, the GPL Version 2 or
    6.41 - * to extend the choice of license to its licensees as provided above.
    6.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
    6.43 - * Version 2 license, then the option applies only if the new code is
    6.44 - * made subject to such option by the copyright holder.
    6.45 - */
    6.46 -package net.java.html.json;
    6.47 -
    6.48 -import java.util.List;
    6.49 -import java.util.Map;
    6.50 -import net.java.html.BrwsrCtx;
    6.51 -import net.java.html.json.MapModelTest.One;
    6.52 -import org.apidesign.html.context.spi.Contexts;
    6.53 -import org.apidesign.html.json.spi.Technology;
    6.54 -import org.apidesign.html.json.spi.Transfer;
    6.55 -import static org.testng.Assert.*;
    6.56 -import org.testng.annotations.BeforeMethod;
    6.57 -import org.testng.annotations.Test;
    6.58 -
    6.59 -/**
    6.60 - *
    6.61 - * @author Jaroslav Tulach
    6.62 - */
    6.63 -@Model(className = "TodoUI", properties = {
    6.64 -    @Property(name = "todos", type = Todo.class, array = true),
    6.65 -    @Property(name = "todoText", type = String.class)
    6.66 -})
    6.67 -public class ToDoTest {
    6.68 -    @Model(className = "Todo", properties = {
    6.69 -        @Property(name = "text", type = String.class),
    6.70 -        @Property(name = "done", type = boolean.class)
    6.71 -    })
    6.72 -    static class ItemCtrl {
    6.73 -    }
    6.74 -
    6.75 -    @ComputedProperty(deep = true)
    6.76 -    static int remaining(
    6.77 -        List<Todo> todos, String todoText
    6.78 -    ) {
    6.79 -        int count = 0;
    6.80 -        for (Todo d : todos) {
    6.81 -            if (!d.isDone()) {
    6.82 -                count++;
    6.83 -            }
    6.84 -        }
    6.85 -        return count;
    6.86 -    }
    6.87 -    
    6.88 -    private MapModelTest.MapTechnology t;
    6.89 -    private BrwsrCtx c;
    6.90 -
    6.91 -    @BeforeMethod
    6.92 -    public void initTechnology() {
    6.93 -        t = new MapModelTest.MapTechnology();
    6.94 -        c = Contexts.newBuilder().register(Technology.class, t, 1).
    6.95 -                register(Transfer.class, t, 1).build();
    6.96 -    }
    6.97 -    
    6.98 -    
    6.99 -    @Test public void checkAndUncheckFirstItem() throws Exception {
   6.100 -        TodoUI ui = Models.bind(
   6.101 -                new TodoUI(
   6.102 -                    null,
   6.103 -                    new Todo("First", false),
   6.104 -                    new Todo("2nd", true),
   6.105 -                    new Todo("Third", false)
   6.106 -                ), c).applyBindings();
   6.107 -
   6.108 -        Map m = (Map) Models.toRaw(ui);
   6.109 -        Object v = m.get("remaining");
   6.110 -        assertNotNull(v, "Value should be in the map");
   6.111 -        assertEquals(v.getClass(), One.class, "It is instance of One");
   6.112 -        One o = (One) v;
   6.113 -        assertEquals(o.changes, 0, "No changes so far");
   6.114 -        assertTrue(o.pb.isReadOnly(), "Derived property");
   6.115 -        assertEquals(o.get(), 2);
   6.116 -
   6.117 -        ui.getTodos().get(0).setDone(true);
   6.118 -
   6.119 -        assertEquals(o.get(), 1);
   6.120 -        assertEquals(o.changes, 1, "One change so far");
   6.121 -
   6.122 -        ui.getTodos().get(0).setDone(false);
   6.123 -
   6.124 -        assertEquals(o.get(), 2);
   6.125 -        assertEquals(o.changes, 2, "2nd change so far");
   6.126 -        
   6.127 -    }
   6.128 -    
   6.129 -}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java	Sat Aug 02 07:28:37 2014 +0200
     7.3 @@ -0,0 +1,496 @@
     7.4 +/**
     7.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     7.6 + *
     7.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     7.8 + *
     7.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    7.10 + * Other names may be trademarks of their respective owners.
    7.11 + *
    7.12 + * The contents of this file are subject to the terms of either the GNU
    7.13 + * General Public License Version 2 only ("GPL") or the Common
    7.14 + * Development and Distribution License("CDDL") (collectively, the
    7.15 + * "License"). You may not use this file except in compliance with the
    7.16 + * License. You can obtain a copy of the License at
    7.17 + * http://www.netbeans.org/cddl-gplv2.html
    7.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    7.19 + * specific language governing permissions and limitations under the
    7.20 + * License.  When distributing the software, include this License Header
    7.21 + * Notice in each file and include the License file at
    7.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    7.23 + * particular file as subject to the "Classpath" exception as provided
    7.24 + * by Oracle in the GPL Version 2 section of the License file that
    7.25 + * accompanied this code. If applicable, add the following below the
    7.26 + * License Header, with the fields enclosed by brackets [] replaced by
    7.27 + * your own identifying information:
    7.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    7.29 + *
    7.30 + * Contributor(s):
    7.31 + *
    7.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    7.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    7.34 + *
    7.35 + * If you wish your version of this file to be governed by only the CDDL
    7.36 + * or only the GPL Version 2, indicate your decision by adding
    7.37 + * "[Contributor] elects to include this software in this distribution
    7.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    7.39 + * single choice of license, a recipient has the option to distribute
    7.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    7.41 + * to extend the choice of license to its licensees as provided above.
    7.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    7.43 + * Version 2 license, then the option applies only if the new code is
    7.44 + * made subject to such option by the copyright holder.
    7.45 + */
    7.46 +package org.netbeans.html.json.impl;
    7.47 +
    7.48 +import java.io.IOException;
    7.49 +import java.io.InputStream;
    7.50 +import java.lang.reflect.InvocationTargetException;
    7.51 +import java.util.HashMap;
    7.52 +import java.util.List;
    7.53 +import java.util.Map;
    7.54 +import net.java.html.BrwsrCtx;
    7.55 +import net.java.html.json.ComputedProperty;
    7.56 +import net.java.html.json.Model;
    7.57 +import net.java.html.json.Models;
    7.58 +import net.java.html.json.Property;
    7.59 +import org.apidesign.html.context.spi.Contexts;
    7.60 +import org.apidesign.html.json.spi.FunctionBinding;
    7.61 +import org.apidesign.html.json.spi.JSONCall;
    7.62 +import org.apidesign.html.json.spi.PropertyBinding;
    7.63 +import org.apidesign.html.json.spi.Technology;
    7.64 +import org.apidesign.html.json.spi.Transfer;
    7.65 +import static org.testng.Assert.*;
    7.66 +import org.testng.annotations.BeforeMethod;
    7.67 +import org.testng.annotations.Test;
    7.68 +
    7.69 +/**
    7.70 + *
    7.71 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    7.72 + */
    7.73 +public class DeepChangeTest {
    7.74 +    private MapTechnology t;
    7.75 +    private BrwsrCtx c;
    7.76 +
    7.77 +    @BeforeMethod public void initTechnology() {
    7.78 +        t = new MapTechnology();
    7.79 +        c = Contexts.newBuilder().register(Technology.class, t, 1).
    7.80 +            register(Transfer.class, t, 1).build();
    7.81 +    }
    7.82 +    
    7.83 +    @Model(className = "MyX", properties = {
    7.84 +        @Property(name = "one", type = MyY.class),
    7.85 +        @Property(name = "all", type = MyY.class, array = true)
    7.86 +    })
    7.87 +    static class X {
    7.88 +        @ComputedProperty @Transitive(deep = true) 
    7.89 +        static String oneName(MyY one) {
    7.90 +            return one.getValue();
    7.91 +        }
    7.92 +        @ComputedProperty @Transitive(deep = true) 
    7.93 +        static String sndName(MyY one) {
    7.94 +            return one.getValue().toUpperCase();
    7.95 +        }
    7.96 +        @ComputedProperty @Transitive(deep = false) 
    7.97 +        static String noName(MyY one) {
    7.98 +            return one.getValue().toUpperCase();
    7.99 +        }
   7.100 +        @ComputedProperty @Transitive(deep = true) 
   7.101 +        static String thrdName(MyY one) {
   7.102 +            return "X" + one.getCount();
   7.103 +        }
   7.104 +        
   7.105 +        @ComputedProperty @Transitive(deep = true)
   7.106 +        static String allNames(List<MyY> all) {
   7.107 +            StringBuilder sb = new StringBuilder();
   7.108 +            for (MyY y : all) {
   7.109 +                sb.append(y.getValue());
   7.110 +            }
   7.111 +            return sb.toString();
   7.112 +        }
   7.113 +
   7.114 +        @ComputedProperty @Transitive(deep = true)
   7.115 +        static String firstFromNames(List<MyY> all) {
   7.116 +            for (MyY y : all) {
   7.117 +                if (y != null && y.getValue() != null) {
   7.118 +                    return y.getValue();
   7.119 +                }
   7.120 +            }
   7.121 +            return null;
   7.122 +        }
   7.123 +    }
   7.124 +    @Model(className = "MyY", properties = {
   7.125 +        @Property(name = "value", type = String.class),
   7.126 +        @Property(name = "count", type = int.class)
   7.127 +    })
   7.128 +    static class Y {
   7.129 +    }
   7.130 +    @Model(className = "MyOverall", properties = {
   7.131 +        @Property(name = "x", type = MyX.class)
   7.132 +    })
   7.133 +    static class Overall {
   7.134 +        @ComputedProperty @Transitive(deep = true) 
   7.135 +        static String valueAccross(MyX x) {
   7.136 +            return x.getFirstFromNames();
   7.137 +        }
   7.138 +    }
   7.139 +    
   7.140 +    @Test public void isTransitiveChangeNotifiedProperly() throws Exception {
   7.141 +        MyX p = Models.bind(
   7.142 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   7.143 +        ), c).applyBindings();
   7.144 +        
   7.145 +        Map m = (Map)Models.toRaw(p);
   7.146 +        Object v = m.get("oneName");
   7.147 +        assertNotNull(v, "Value should be in the map");
   7.148 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   7.149 +        One o = (One)v;
   7.150 +        assertEquals(o.changes, 0, "No changes so far");
   7.151 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.152 +        assertEquals(o.get(), "Ahoj");
   7.153 +
   7.154 +        p.getOne().setValue("Nazdar");
   7.155 +        
   7.156 +        assertEquals(o.get(), "Nazdar");
   7.157 +        assertEquals(o.changes, 1, "One change so far");
   7.158 +    }
   7.159 +    
   7.160 +    @Test public void isTransitiveChangeInArrayNotifiedProperly() throws Exception {
   7.161 +        MyX p = Models.bind(
   7.162 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   7.163 +        ), c).applyBindings();
   7.164 +        
   7.165 +        Map m = (Map)Models.toRaw(p);
   7.166 +        Object v = m.get("allNames");
   7.167 +        assertNotNull(v, "Value should be in the map");
   7.168 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   7.169 +        One o = (One)v;
   7.170 +        assertEquals(o.changes, 0, "No changes so far");
   7.171 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.172 +        assertEquals(o.get(), "HiHello");
   7.173 +
   7.174 +        p.getAll().get(0).setValue("Nazdar");
   7.175 +        
   7.176 +        assertEquals(o.get(), "NazdarHello");
   7.177 +        assertEquals(o.changes, 1, "One change so far");
   7.178 +    }
   7.179 +
   7.180 +    @Test public void addingIntoArray() throws Exception {
   7.181 +        MyX p = Models.bind(
   7.182 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   7.183 +        ), c).applyBindings();
   7.184 +        
   7.185 +        Map m = (Map)Models.toRaw(p);
   7.186 +        Object v = m.get("allNames");
   7.187 +        assertNotNull(v, "Value should be in the map");
   7.188 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   7.189 +        One o = (One)v;
   7.190 +        assertEquals(o.changes, 0, "No changes so far");
   7.191 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.192 +        assertEquals(o.get(), "HiHello");
   7.193 +
   7.194 +        MyY y = new MyY("Cus", 1);
   7.195 +        p.getAll().add(y);
   7.196 +        
   7.197 +        assertEquals(o.changes, 1, "One change so far");
   7.198 +        assertEquals(o.get(), "HiHelloCus");
   7.199 +        
   7.200 +        y.setValue("Nazdar");
   7.201 +        
   7.202 +        assertEquals(o.changes, 2, "2nd change so far");
   7.203 +        assertEquals(o.get(), "HiHelloNazdar");
   7.204 +        
   7.205 +        y.setValue("Zdravim");
   7.206 +
   7.207 +        assertEquals(o.changes, 3, "3rd change so far");
   7.208 +        assertEquals(o.get(), "HiHelloZdravim");
   7.209 +    }
   7.210 +    
   7.211 +    @Test public void firstChangeInArrayNotifiedProperly() throws Exception {
   7.212 +        MyX p = Models.bind(
   7.213 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   7.214 +        ), c).applyBindings();
   7.215 +        
   7.216 +        Map m = (Map)Models.toRaw(p);
   7.217 +        Object v = m.get("firstFromNames");
   7.218 +        assertNotNull(v, "Value should be in the map");
   7.219 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   7.220 +        One o = (One)v;
   7.221 +        assertEquals(o.changes, 0, "No changes so far");
   7.222 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.223 +        assertEquals(o.get(), "Hi");
   7.224 +
   7.225 +        p.getAll().get(0).setValue("Nazdar");
   7.226 +        
   7.227 +        assertEquals(o.get(), "Nazdar");
   7.228 +        assertEquals(o.changes, 1, "One change so far");
   7.229 +    }
   7.230 +    
   7.231 +    @Test public void firstChangeInArrayToNull() throws Exception {
   7.232 +        MyX p = Models.bind(
   7.233 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   7.234 +        ), c).applyBindings();
   7.235 +        
   7.236 +        Map m = (Map)Models.toRaw(p);
   7.237 +        Object v = m.get("firstFromNames");
   7.238 +        assertNotNull(v, "Value should be in the map");
   7.239 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   7.240 +        One o = (One)v;
   7.241 +        assertEquals(o.changes, 0, "No changes so far");
   7.242 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.243 +        assertEquals(o.get(), "Hi");
   7.244 +
   7.245 +        p.getAll().get(0).setValue(null);
   7.246 +        
   7.247 +        assertEquals(o.get(), "Hello");
   7.248 +        assertEquals(o.changes, 1, "One change so far");
   7.249 +        
   7.250 +        p.getAll().get(0).setValue("Nazdar");
   7.251 +
   7.252 +        assertEquals(o.get(), "Nazdar");
   7.253 +        assertEquals(o.changes, 2, "2nd change so far");
   7.254 +    }
   7.255 +    
   7.256 +    @Test public void firstChangeInArrayNotifiedTransitively() throws Exception {
   7.257 +        MyOverall p = Models.bind(
   7.258 +            new MyOverall(new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999))
   7.259 +        ), c).applyBindings();
   7.260 +        
   7.261 +        Map m = (Map)Models.toRaw(p);
   7.262 +        Object v = m.get("valueAccross");
   7.263 +        assertNotNull(v, "Value should be in the map");
   7.264 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   7.265 +        One o = (One)v;
   7.266 +        assertEquals(o.changes, 0, "No changes so far");
   7.267 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.268 +        assertEquals(o.get(), "Hi");
   7.269 +
   7.270 +        p.getX().getAll().get(0).setValue("Nazdar");
   7.271 +        
   7.272 +        assertEquals(o.get(), "Nazdar");
   7.273 +        assertEquals(o.changes, 1, "One change so far");
   7.274 +    }
   7.275 +    
   7.276 +    @Test public void secondChangeInArrayIgnored() throws Exception {
   7.277 +        MyX p = Models.bind(
   7.278 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   7.279 +        ), c).applyBindings();
   7.280 +        
   7.281 +        Map m = (Map)Models.toRaw(p);
   7.282 +        Object v = m.get("firstFromNames");
   7.283 +        assertNotNull(v, "Value should be in the map");
   7.284 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   7.285 +        One o = (One)v;
   7.286 +        assertEquals(o.changes, 0, "No changes so far");
   7.287 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.288 +        assertEquals(o.get(), "Hi");
   7.289 +
   7.290 +        p.getAll().get(1).setValue("Nazdar");
   7.291 +        
   7.292 +        assertEquals(o.get(), "Hi");
   7.293 +        assertEquals(o.changes, 0, "No change so far");
   7.294 +    }
   7.295 +    
   7.296 +    @Test public void changeInArraySizeNeedsToBeRecomputed() throws Exception {
   7.297 +        MyX p = Models.bind(
   7.298 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   7.299 +        ), c).applyBindings();
   7.300 +        
   7.301 +        Map m = (Map)Models.toRaw(p);
   7.302 +        Object v = m.get("firstFromNames");
   7.303 +        assertNotNull(v, "Value should be in the map");
   7.304 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   7.305 +        One o = (One)v;
   7.306 +        assertEquals(o.changes, 0, "No changes so far");
   7.307 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.308 +        assertEquals(o.get(), "Hi");
   7.309 +
   7.310 +        p.getAll().remove(1);
   7.311 +        
   7.312 +        assertEquals(o.get(), "Hi");
   7.313 +        assertEquals(o.changes, 1, "This required a change");
   7.314 +    }
   7.315 +    
   7.316 +    @Test public void doublePropertyChangeNotified() throws Exception {
   7.317 +        MyX p = Models.bind(
   7.318 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   7.319 +        ), c).applyBindings();
   7.320 +        
   7.321 +        Map m = (Map)Models.toRaw(p);
   7.322 +        Object v = m.get("oneName");
   7.323 +        assertNotNull(v, "Value should be in the map");
   7.324 +        Object v2 = m.get("sndName");
   7.325 +        assertNotNull(v2, "Value2 should be in the map");
   7.326 +        One o = (One)v;
   7.327 +        One o2 = (One)v2;
   7.328 +        assertEquals(o.changes, 0, "No changes so far");
   7.329 +        assertEquals(o2.changes, 0, "No changes so far");
   7.330 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.331 +        assertEquals(o.get(), "Ahoj");
   7.332 +        assertEquals(o2.get(), "AHOJ");
   7.333 +
   7.334 +        p.getOne().setValue("Nazdar");
   7.335 +        
   7.336 +        assertEquals(o.get(), "Nazdar");
   7.337 +        assertEquals(o.changes, 1, "One change so far");
   7.338 +        assertEquals(o2.changes, 1, "One change so far");
   7.339 +        assertEquals(o2.get(), "NAZDAR");
   7.340 +    }
   7.341 +    
   7.342 +    @Test public void onlyAffectedPropertyChangeNotified() throws Exception {
   7.343 +        MyX p = Models.bind(
   7.344 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   7.345 +        ), c).applyBindings();
   7.346 +        
   7.347 +        Map m = (Map)Models.toRaw(p);
   7.348 +        Object v = m.get("oneName");
   7.349 +        assertNotNull(v, "Value should be in the map");
   7.350 +        Object v2 = m.get("thrdName");
   7.351 +        assertNotNull(v2, "Value2 should be in the map");
   7.352 +        One o = (One)v;
   7.353 +        One o2 = (One)v2;
   7.354 +        assertEquals(o.changes, 0, "No changes so far");
   7.355 +        assertEquals(o2.changes, 0, "No changes so far");
   7.356 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.357 +        assertEquals(o.get(), "Ahoj");
   7.358 +        assertEquals(o2.get(), "X0");
   7.359 +
   7.360 +        p.getOne().setCount(10);
   7.361 +        
   7.362 +        assertEquals(o.get(), "Ahoj");
   7.363 +        assertEquals(o.changes, 0, "Still no change");
   7.364 +        assertEquals(o2.changes, 1, "One change so far");
   7.365 +        assertEquals(o2.get(), "X10");
   7.366 +    }
   7.367 +    
   7.368 +    @Test public void onlyDeepPropsAreNotified() throws Exception {
   7.369 +        MyX p = Models.bind(
   7.370 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
   7.371 +        ), c).applyBindings();
   7.372 +        
   7.373 +        Map m = (Map)Models.toRaw(p);
   7.374 +        Object v = m.get("oneName");
   7.375 +        assertNotNull(v, "Value should be in the map");
   7.376 +        Object v2 = m.get("noName");
   7.377 +        assertNotNull(v2, "Value2 should be in the map");
   7.378 +        One o = (One)v;
   7.379 +        One o2 = (One)v2;
   7.380 +        assertEquals(o.changes, 0, "No changes so far");
   7.381 +        assertEquals(o2.changes, 0, "No changes so far");
   7.382 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   7.383 +        assertEquals(o.get(), "Ahoj");
   7.384 +        assertEquals(o2.get(), "AHOJ");
   7.385 +
   7.386 +        p.getOne().setValue("Nazdar");
   7.387 +        
   7.388 +        assertEquals(o.get(), "Nazdar");
   7.389 +        assertEquals(o.changes, 1, "One change so far");
   7.390 +        assertEquals(o2.changes, 0, "This change is not noticed");
   7.391 +        assertEquals(o2.get(), "NAZDAR", "but property value changes when computed");
   7.392 +    }
   7.393 +
   7.394 +    static final class One {
   7.395 +
   7.396 +        int changes;
   7.397 +        final PropertyBinding pb;
   7.398 +        final FunctionBinding fb;
   7.399 +
   7.400 +        One(Object m, PropertyBinding pb) throws NoSuchMethodException {
   7.401 +            this.pb = pb;
   7.402 +            this.fb = null;
   7.403 +        }
   7.404 +
   7.405 +        One(Object m, FunctionBinding fb) throws NoSuchMethodException {
   7.406 +            this.pb = null;
   7.407 +            this.fb = fb;
   7.408 +        }
   7.409 +
   7.410 +        Object get() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
   7.411 +            return pb.getValue();
   7.412 +        }
   7.413 +
   7.414 +        void set(Object v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
   7.415 +            pb.setValue(v);
   7.416 +        }
   7.417 +    }
   7.418 +
   7.419 +    static final class MapTechnology
   7.420 +            implements Technology<Map<String, One>>, Transfer {
   7.421 +
   7.422 +        @Override
   7.423 +        public Map<String, One> wrapModel(Object model) {
   7.424 +            return new HashMap<String, One>();
   7.425 +        }
   7.426 +
   7.427 +        @Override
   7.428 +        public void valueHasMutated(Map<String, One> data, String propertyName) {
   7.429 +            One p = data.get(propertyName);
   7.430 +            if (p != null) {
   7.431 +                p.changes++;
   7.432 +            }
   7.433 +        }
   7.434 +
   7.435 +        @Override
   7.436 +        public void bind(PropertyBinding b, Object model, Map<String, One> data) {
   7.437 +            try {
   7.438 +                One o = new One(model, b);
   7.439 +                data.put(b.getPropertyName(), o);
   7.440 +            } catch (NoSuchMethodException ex) {
   7.441 +                throw new IllegalStateException(ex);
   7.442 +            }
   7.443 +        }
   7.444 +
   7.445 +        @Override
   7.446 +        public void expose(FunctionBinding fb, Object model, Map<String, One> data) {
   7.447 +            try {
   7.448 +                data.put(fb.getFunctionName(), new One(model, fb));
   7.449 +            } catch (NoSuchMethodException ex) {
   7.450 +                throw new IllegalStateException(ex);
   7.451 +            }
   7.452 +        }
   7.453 +
   7.454 +        @Override
   7.455 +        public void applyBindings(Map<String, One> data) {
   7.456 +        }
   7.457 +
   7.458 +        @Override
   7.459 +        public Object wrapArray(Object[] arr) {
   7.460 +            return arr;
   7.461 +        }
   7.462 +
   7.463 +        @Override
   7.464 +        public void extract(Object obj, String[] props, Object[] values) {
   7.465 +            Map<?, ?> map = obj instanceof Map ? (Map<?, ?>) obj : null;
   7.466 +            for (int i = 0; i < Math.min(props.length, values.length); i++) {
   7.467 +                if (map == null) {
   7.468 +                    values[i] = null;
   7.469 +                } else {
   7.470 +                    values[i] = map.get(props[i]);
   7.471 +                    if (values[i] instanceof One) {
   7.472 +                        values[i] = ((One) values[i]).pb.getValue();
   7.473 +                    }
   7.474 +                }
   7.475 +            }
   7.476 +        }
   7.477 +
   7.478 +        @Override
   7.479 +        public void loadJSON(JSONCall call) {
   7.480 +            call.notifyError(new UnsupportedOperationException());
   7.481 +        }
   7.482 +
   7.483 +        @Override
   7.484 +        public <M> M toModel(Class<M> modelClass, Object data) {
   7.485 +            return modelClass.cast(data);
   7.486 +        }
   7.487 +
   7.488 +        @Override
   7.489 +        public Object toJSON(InputStream is) throws IOException {
   7.490 +            throw new IOException();
   7.491 +        }
   7.492 +
   7.493 +        @Override
   7.494 +        public void runSafe(Runnable r) {
   7.495 +            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
   7.496 +        }
   7.497 +    }
   7.498 +    
   7.499 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/ToDoTest.java	Sat Aug 02 07:28:37 2014 +0200
     8.3 @@ -0,0 +1,131 @@
     8.4 +/**
     8.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     8.6 + *
     8.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     8.8 + *
     8.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    8.10 + * Other names may be trademarks of their respective owners.
    8.11 + *
    8.12 + * The contents of this file are subject to the terms of either the GNU
    8.13 + * General Public License Version 2 only ("GPL") or the Common
    8.14 + * Development and Distribution License("CDDL") (collectively, the
    8.15 + * "License"). You may not use this file except in compliance with the
    8.16 + * License. You can obtain a copy of the License at
    8.17 + * http://www.netbeans.org/cddl-gplv2.html
    8.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    8.19 + * specific language governing permissions and limitations under the
    8.20 + * License.  When distributing the software, include this License Header
    8.21 + * Notice in each file and include the License file at
    8.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    8.23 + * particular file as subject to the "Classpath" exception as provided
    8.24 + * by Oracle in the GPL Version 2 section of the License file that
    8.25 + * accompanied this code. If applicable, add the following below the
    8.26 + * License Header, with the fields enclosed by brackets [] replaced by
    8.27 + * your own identifying information:
    8.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    8.29 + *
    8.30 + * Contributor(s):
    8.31 + *
    8.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    8.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    8.34 + *
    8.35 + * If you wish your version of this file to be governed by only the CDDL
    8.36 + * or only the GPL Version 2, indicate your decision by adding
    8.37 + * "[Contributor] elects to include this software in this distribution
    8.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    8.39 + * single choice of license, a recipient has the option to distribute
    8.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    8.41 + * to extend the choice of license to its licensees as provided above.
    8.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    8.43 + * Version 2 license, then the option applies only if the new code is
    8.44 + * made subject to such option by the copyright holder.
    8.45 + */
    8.46 +package org.netbeans.html.json.impl;
    8.47 +
    8.48 +import java.util.List;
    8.49 +import java.util.Map;
    8.50 +import net.java.html.BrwsrCtx;
    8.51 +import net.java.html.json.ComputedProperty;
    8.52 +import net.java.html.json.Model;
    8.53 +import net.java.html.json.Models;
    8.54 +import net.java.html.json.Property;
    8.55 +import org.apidesign.html.context.spi.Contexts;
    8.56 +import org.apidesign.html.json.spi.Technology;
    8.57 +import org.apidesign.html.json.spi.Transfer;
    8.58 +import org.netbeans.html.json.impl.DeepChangeTest.MapTechnology;
    8.59 +import org.netbeans.html.json.impl.DeepChangeTest.One;
    8.60 +import static org.testng.Assert.*;
    8.61 +import org.testng.annotations.BeforeMethod;
    8.62 +import org.testng.annotations.Test;
    8.63 +
    8.64 +/**
    8.65 + *
    8.66 + * @author Jaroslav Tulach
    8.67 + */
    8.68 +@Model(className = "TodoUI", properties = {
    8.69 +    @Property(name = "todos", type = Todo.class, array = true),
    8.70 +    @Property(name = "todoText", type = String.class)
    8.71 +})
    8.72 +public class ToDoTest {
    8.73 +    @Model(className = "Todo", properties = {
    8.74 +        @Property(name = "text", type = String.class),
    8.75 +        @Property(name = "done", type = boolean.class)
    8.76 +    })
    8.77 +    static class ItemCtrl {
    8.78 +    }
    8.79 +
    8.80 +    @ComputedProperty @Transitive(deep = true)
    8.81 +    static int remaining(
    8.82 +        List<Todo> todos, String todoText
    8.83 +    ) {
    8.84 +        int count = 0;
    8.85 +        for (Todo d : todos) {
    8.86 +            if (!d.isDone()) {
    8.87 +                count++;
    8.88 +            }
    8.89 +        }
    8.90 +        return count;
    8.91 +    }
    8.92 +    
    8.93 +    private MapTechnology t;
    8.94 +    private BrwsrCtx c;
    8.95 +
    8.96 +    @BeforeMethod
    8.97 +    public void initTechnology() {
    8.98 +        t = new MapTechnology();
    8.99 +        c = Contexts.newBuilder().register(Technology.class, t, 1).
   8.100 +                register(Transfer.class, t, 1).build();
   8.101 +    }
   8.102 +    
   8.103 +    
   8.104 +    @Test public void checkAndUncheckFirstItem() throws Exception {
   8.105 +        TodoUI ui = Models.bind(
   8.106 +                new TodoUI(
   8.107 +                    null,
   8.108 +                    new Todo("First", false),
   8.109 +                    new Todo("2nd", true),
   8.110 +                    new Todo("Third", false)
   8.111 +                ), c).applyBindings();
   8.112 +
   8.113 +        Map m = (Map) Models.toRaw(ui);
   8.114 +        Object v = m.get("remaining");
   8.115 +        assertNotNull(v, "Value should be in the map");
   8.116 +        assertEquals(v.getClass(), One.class, "It is instance of One");
   8.117 +        One o = (One) v;
   8.118 +        assertEquals(o.changes, 0, "No changes so far");
   8.119 +        assertTrue(o.pb.isReadOnly(), "Derived property");
   8.120 +        assertEquals(o.get(), 2);
   8.121 +
   8.122 +        ui.getTodos().get(0).setDone(true);
   8.123 +
   8.124 +        assertEquals(o.get(), 1);
   8.125 +        assertEquals(o.changes, 1, "One change so far");
   8.126 +
   8.127 +        ui.getTodos().get(0).setDone(false);
   8.128 +
   8.129 +        assertEquals(o.get(), 2);
   8.130 +        assertEquals(o.changes, 2, "2nd change so far");
   8.131 +        
   8.132 +    }
   8.133 +    
   8.134 +}