Can handle multiple deeply computed properties in one object DeepWatch
authorJaroslav Tulach <jtulach@netbeans.org>
Fri, 01 Aug 2014 13:02:35 +0200
branchDeepWatch
changeset 7752fb16596688f
parent 774 394cdd5bd52c
child 776 7e25ba02d9ce
Can handle multiple deeply computed properties in one object
json/src/main/java/org/apidesign/html/json/spi/Proto.java
json/src/main/java/org/apidesign/html/json/spi/Watcher.java
json/src/test/java/net/java/html/json/DeepChangeTest.java
     1.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Fri Aug 01 12:36:56 2014 +0200
     1.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Fri Aug 01 13:02:35 2014 +0200
     1.3 @@ -69,7 +69,7 @@
     1.4      private final Type type;
     1.5      private final net.java.html.BrwsrCtx context;
     1.6      private org.netbeans.html.json.impl.Bindings ko;
     1.7 -    private Watcher myOwn;
     1.8 +    private Watcher mine;
     1.9      private Watcher.Ref observers;
    1.10      private static Watcher locked;
    1.11  
    1.12 @@ -103,7 +103,7 @@
    1.13      
    1.14      public void accessValue(String propName) {
    1.15          if (locked != null) {
    1.16 -            if (locked.proto == this) {
    1.17 +            if (locked.forbiddenValue(this)) {
    1.18                  throw new IllegalStateException();
    1.19              }
    1.20              observers = locked.observe(observers, propName);
    1.21 @@ -121,7 +121,7 @@
    1.22       * unlocked state by calling this method.
    1.23       */
    1.24      public void releaseLock() {
    1.25 -        myOwn = locked;
    1.26 +        mine = Watcher.register(mine, locked);
    1.27          locked = null;
    1.28      }
    1.29      
    1.30 @@ -462,8 +462,7 @@
    1.31      }
    1.32  
    1.33      final Watcher watcher(String prop) {
    1.34 -        assert myOwn == null || prop.equals(myOwn.prop);
    1.35 -        return myOwn;
    1.36 +        return Watcher.find(mine, prop);
    1.37      }
    1.38  
    1.39      /** Functionality used by the code generated by annotation
     2.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Watcher.java	Fri Aug 01 12:36:56 2014 +0200
     2.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Watcher.java	Fri Aug 01 13:02:35 2014 +0200
     2.3 @@ -50,15 +50,44 @@
     2.4   */
     2.5  final class Watcher {
     2.6      static final Watcher DUMMY = new Watcher(null, null);
     2.7 -    
     2.8 -    final Proto proto;
     2.9 -    final String prop;
    2.10 +
    2.11 +    private final Proto proto;
    2.12 +    private final String prop;
    2.13 +    private Watcher next;
    2.14  
    2.15      private Watcher(Proto proto, String prop) {
    2.16          this.proto = proto;
    2.17          this.prop = prop;
    2.18      }
    2.19      
    2.20 +    static Watcher find(Watcher first, String prop) {
    2.21 +        for (;;) {
    2.22 +            if (prop.equals(first.prop)) {
    2.23 +                return first;
    2.24 +            }
    2.25 +            first = first.next;
    2.26 +        }
    2.27 +    }
    2.28 +
    2.29 +    static Watcher register(Watcher mine, Watcher locked) {
    2.30 +        Watcher current = mine;
    2.31 +        for (;;) {
    2.32 +            if (current == null) {
    2.33 +                return locked;
    2.34 +            }
    2.35 +            Watcher next = current.next;
    2.36 +            if (next == null) {
    2.37 +                current.next = locked;
    2.38 +                return mine;
    2.39 +            }
    2.40 +            if (next.prop.equals(locked.prop)) {
    2.41 +                locked.next = next.next;
    2.42 +                current.next = locked;
    2.43 +                return mine;
    2.44 +            }
    2.45 +            current = next;
    2.46 +        }
    2.47 +    }
    2.48      
    2.49      static Watcher computing(Proto proto, String prop) {
    2.50          proto.getClass();
    2.51 @@ -72,6 +101,10 @@
    2.52          }
    2.53          return new Ref(this, prop).chain(prev);
    2.54      }
    2.55 +
    2.56 +    final boolean forbiddenValue(Proto aThis) {
    2.57 +        return proto == aThis;
    2.58 +    }
    2.59      
    2.60      static final class Ref extends WeakReference<Watcher> {
    2.61          private final String prop;
     3.1 --- a/json/src/test/java/net/java/html/json/DeepChangeTest.java	Fri Aug 01 12:36:56 2014 +0200
     3.2 +++ b/json/src/test/java/net/java/html/json/DeepChangeTest.java	Fri Aug 01 13:02:35 2014 +0200
     3.3 @@ -76,16 +76,25 @@
     3.4          static String oneName(MyY one) {
     3.5              return one.getValue();
     3.6          }
     3.7 +        @ComputedProperty(deep = true) 
     3.8 +        static String sndName(MyY one) {
     3.9 +            return one.getValue().toUpperCase();
    3.10 +        }
    3.11 +        @ComputedProperty(deep = true) 
    3.12 +        static String thrdName(MyY one) {
    3.13 +            return "X" + one.getCount();
    3.14 +        }
    3.15      }
    3.16      @Model(className = "MyY", properties = {
    3.17 -        @Property(name = "value", type = String.class)
    3.18 +        @Property(name = "value", type = String.class),
    3.19 +        @Property(name = "count", type = int.class)
    3.20      })
    3.21      static class Y {
    3.22      }
    3.23      
    3.24      @Test public void isTransitiveChangeNotifiedProperly() throws Exception {
    3.25          MyX p = Models.bind(
    3.26 -            new MyX(new MyY("Ahoj"), new MyY("Hi"), new MyY("Hello")
    3.27 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
    3.28          ), c).applyBindings();
    3.29          
    3.30          Map m = (Map)Models.toRaw(p);
    3.31 @@ -102,4 +111,56 @@
    3.32          assertEquals(o.get(), "Nazdar");
    3.33          assertEquals(o.changes, 1, "One change so far");
    3.34      }
    3.35 +    
    3.36 +    @Test public void doublePropertyChangeNotified() throws Exception {
    3.37 +        MyX p = Models.bind(
    3.38 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
    3.39 +        ), c).applyBindings();
    3.40 +        
    3.41 +        Map m = (Map)Models.toRaw(p);
    3.42 +        Object v = m.get("oneName");
    3.43 +        assertNotNull(v, "Value should be in the map");
    3.44 +        Object v2 = m.get("sndName");
    3.45 +        assertNotNull(v2, "Value2 should be in the map");
    3.46 +        One o = (One)v;
    3.47 +        One o2 = (One)v2;
    3.48 +        assertEquals(o.changes, 0, "No changes so far");
    3.49 +        assertEquals(o2.changes, 0, "No changes so far");
    3.50 +        assertTrue(o.pb.isReadOnly(), "Derived property");
    3.51 +        assertEquals(o.get(), "Ahoj");
    3.52 +        assertEquals(o2.get(), "AHOJ");
    3.53 +
    3.54 +        p.getOne().setValue("Nazdar");
    3.55 +        
    3.56 +        assertEquals(o.get(), "Nazdar");
    3.57 +        assertEquals(o.changes, 1, "One change so far");
    3.58 +        assertEquals(o2.changes, 1, "One change so far");
    3.59 +        assertEquals(o2.get(), "NAZDAR");
    3.60 +    }
    3.61 +    
    3.62 +    @Test public void onlyAffectedPropertyChangeNotified() throws Exception {
    3.63 +        MyX p = Models.bind(
    3.64 +            new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999)
    3.65 +        ), c).applyBindings();
    3.66 +        
    3.67 +        Map m = (Map)Models.toRaw(p);
    3.68 +        Object v = m.get("oneName");
    3.69 +        assertNotNull(v, "Value should be in the map");
    3.70 +        Object v2 = m.get("thrdName");
    3.71 +        assertNotNull(v2, "Value2 should be in the map");
    3.72 +        One o = (One)v;
    3.73 +        One o2 = (One)v2;
    3.74 +        assertEquals(o.changes, 0, "No changes so far");
    3.75 +        assertEquals(o2.changes, 0, "No changes so far");
    3.76 +        assertTrue(o.pb.isReadOnly(), "Derived property");
    3.77 +        assertEquals(o.get(), "Ahoj");
    3.78 +        assertEquals(o2.get(), "X0");
    3.79 +
    3.80 +        p.getOne().setCount(10);
    3.81 +        
    3.82 +        assertEquals(o.get(), "Ahoj");
    3.83 +        assertEquals(o.changes, 0, "Still no change");
    3.84 +        assertEquals(o2.changes, 1, "One change so far");
    3.85 +        assertEquals(o2.get(), "X10");
    3.86 +    }
    3.87  }