Property changes are delivered when in-a union property changes union
authorJaroslav Tulach <jaroslav.tulach@netbeans.org>
Thu, 30 Jan 2014 11:36:32 +0100
branchunion
changeset 5064b66c5141f84
parent 505 501413c8638c
Property changes are delivered when in-a union property changes
json/src/main/java/org/apidesign/html/json/spi/Proto.java
json/src/main/java/org/netbeans/html/json/impl/JSONList.java
json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
json/src/test/java/net/java/html/json/ModelTest.java
json/src/test/java/org/netbeans/html/json/impl/PlainEnumTest.java
     1.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Wed Jan 29 15:49:48 2014 +0100
     1.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Thu Jan 30 11:36:32 2014 +0100
     1.3 @@ -70,6 +70,7 @@
     1.4      private final net.java.html.BrwsrCtx context;
     1.5      private boolean locked;
     1.6      private org.netbeans.html.json.impl.Bindings ko;
     1.7 +    private Proto onMutated;
     1.8  
     1.9      Proto(Object obj, Type type, BrwsrCtx context) {
    1.10          this.obj = obj;
    1.11 @@ -115,11 +116,36 @@
    1.12       *@param propName name of the changed property
    1.13       */
    1.14      public void valueHasMutated(String propName) {
    1.15 +        if (onMutated != null) {
    1.16 +            onMutated.valueHasMutated(propName);
    1.17 +        }
    1.18          if (ko != null) {
    1.19              ko.valueHasMutated(propName);
    1.20 +            final String[] names = type.propertyNames;
    1.21 +            for (int i = 0; i < names.length; i++) {
    1.22 +                if (names[i].equals(propName)) {
    1.23 +                    type.onChange(obj, i);
    1.24 +                }
    1.25 +            }
    1.26          }
    1.27      }
    1.28      
    1.29 +    /** Allows another prototype to be notified when a value is mutated using
    1.30 +     * {@link #valueHasMutated(java.lang.String)} method of this object.
    1.31 +     * This method can be called only once. It is primarily useful for
    1.32 +     * support of {@link Model}s on <code>enum</code>s.
    1.33 +     * 
    1.34 +     * @param m object which {@link #valueHasMutated(java.lang.String)} 
    1.35 +     *    method should be called when there is a change in a property
    1.36 +     *    of this proto object.
    1.37 +     * @since 0.8
    1.38 +     */
    1.39 +    public void onValueHasMutated(Proto m) {
    1.40 +        assert m != null;
    1.41 +        assert onMutated == null;
    1.42 +        onMutated = m;
    1.43 +    }
    1.44 +    
    1.45      /** Initializes the associated model in the current {@link #getContext() context}.
    1.46       * In case of <em>knockout.js</em> technology, applies given bindings 
    1.47       * of the current model to the <em>body</em> element of the page.
    1.48 @@ -662,5 +688,5 @@
    1.49              return type.cast(val);
    1.50          }
    1.51  
    1.52 -    }
    1.53 +    } // end of Type
    1.54  }
     2.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSONList.java	Wed Jan 29 15:49:48 2014 +0100
     2.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSONList.java	Thu Jan 30 11:36:32 2014 +0100
     2.3 @@ -184,6 +184,7 @@
     2.4                  PropertyBindingAccessor.notifyProtoChange(proto, index);
     2.5              }
     2.6          }
     2.7 +        proto.valueHasMutated(name);
     2.8      }
     2.9  
    2.10      @Override
     3.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Wed Jan 29 15:49:48 2014 +0100
     3.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Thu Jan 30 11:36:32 2014 +0100
     3.3 @@ -266,15 +266,15 @@
     3.4          w.append("  private static Class<" + inPckName(e) + "> modelFor() { return null; }\n");
     3.5          w.append("  private ").append(className).append("(net.java.html.BrwsrCtx context) {\n");
     3.6          w.append("    this.proto = TYPE.createProto(this, context);\n");
     3.7 -        for (Prprt p : props) {
     3.8 +        for (int i = 0; i < props.length; i++) {
     3.9 +            Prprt p = props[i];
    3.10              if (p.array()) {
    3.11                  final String tn = typeName(e, p);
    3.12                  String[] gs = toGetSet(p.name(), tn, p.array());
    3.13                  w.write("    this.prop_" + p.name() + " = proto.createList(\""
    3.14                      + p.name() + "\"");
    3.15                  if (functionDeps.containsKey(p.name())) {
    3.16 -                    int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name());
    3.17 -                    w.write(", " + index);
    3.18 +                    w.write(", " + i);
    3.19                  } else {
    3.20                      w.write(", -1");
    3.21                  }
    3.22 @@ -404,9 +404,9 @@
    3.23          w.append("    @Override public void onChange(" + className + " model, int type) {\n");
    3.24          w.append("      switch (type) {\n");
    3.25          {
    3.26 -            String[] arr = functionDeps.keySet().toArray(new String[0]);
    3.27 -            for (int i = 0; i < arr.length; i++) {
    3.28 -                Collection<String> onChange = functionDeps.get(arr[i]);
    3.29 +            for (int i = 0; i < props.length; i++) {
    3.30 +                String pn = props[i].name();
    3.31 +                Collection<String> onChange = functionDeps.get(pn);
    3.32                  if (onChange != null) {
    3.33                      w.append("      case " + i + ":\n");
    3.34                      for (String call : onChange) {
    3.35 @@ -416,8 +416,7 @@
    3.36                  }
    3.37              }
    3.38          }
    3.39 -        w.append("    }\n");
    3.40 -        w.append("      throw new UnsupportedOperationException();\n");
    3.41 +        w.append("      }\n");
    3.42          w.append("    }\n");
    3.43          w.append(onReceiveType);
    3.44          w.append("    @Override public " + className + " read(net.java.html.BrwsrCtx c, Object json) { return new " + className + "(c, json); }\n");
    3.45 @@ -638,15 +637,15 @@
    3.46                  w.append("  }\n");
    3.47                  w.append("  private ").append(className).append("(net.java.html.BrwsrCtx context, Object union, Object json) {\n");
    3.48                  w.append("    this.proto = TYPE.createProto(this, context);\n");
    3.49 -                for (Prprt p : props) {
    3.50 +                for (int i = 0; i < props.length; i++) {
    3.51 +                    Prprt p = props[i];
    3.52                      if (p.array()) {
    3.53                          final String tn = typeName(e, p);
    3.54                          String[] gs = toGetSet(p.name(), tn, p.array());
    3.55                          w.write("    this.prop_" + p.name() + " = proto.createList(\""
    3.56                              + p.name() + "\"");
    3.57                          if (functionDeps.containsKey(p.name())) {
    3.58 -                            int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name());
    3.59 -                            w.write(", " + index);
    3.60 +                            w.write(", " + i);
    3.61                          } else {
    3.62                              w.write(", -1");
    3.63                          }
    3.64 @@ -731,6 +730,7 @@
    3.65                      // end of enum
    3.66                      w.append(") {\n");
    3.67                      w.append("    this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class), union);\n");
    3.68 +                    w.append("    ((" + em.className() + ")union).proto.onValueHasMutated(this.proto);\n");
    3.69                      for (Prprt p : props) {
    3.70                          if (p.array()) {
    3.71                              continue;
    3.72 @@ -827,9 +827,9 @@
    3.73                  w.append("    @Override public void onChange(" + className + " model, int type) {\n");
    3.74                  w.append("      switch (type) {\n");
    3.75                  {
    3.76 -                    String[] arr = functionDeps.keySet().toArray(new String[0]);
    3.77 -                    for (int i = 0; i < arr.length; i++) {
    3.78 -                        Collection<String> onChange = functionDeps.get(arr[i]);
    3.79 +                    for (int i = 0; i < allProps.size(); i++) {
    3.80 +                        String pn = allProps.get(i).name();
    3.81 +                        Collection<String> onChange = functionDeps.get(pn);
    3.82                          if (onChange != null) {
    3.83                              w.append("      case " + i + ":\n");
    3.84                              for (String call : onChange) {
    3.85 @@ -839,8 +839,7 @@
    3.86                          }
    3.87                      }
    3.88                  }
    3.89 -                w.append("    }\n");
    3.90 -                w.append("      throw new UnsupportedOperationException();\n");
    3.91 +                w.append("      }\n");
    3.92                  w.append("    }\n");
    3.93                  w.append(onReceiveType);
    3.94                  w.append("    @Override public " + className + " read(net.java.html.BrwsrCtx c, Object json) {\n");
    3.95 @@ -1011,6 +1010,7 @@
    3.96                      if (props.length > 0) {
    3.97                          w.write("    public " + className + "(" + em.className() + " union) {\n");
    3.98                          w.append("      this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class), union, null);\n");
    3.99 +                        w.append("      ((" + em.className() + ")union).proto.onValueHasMutated(this.proto);\n");
   3.100                          w.write("    }\n");
   3.101                      }
   3.102                      generateEnumConstantModel(w, ec, sharedProps);
   3.103 @@ -2010,6 +2010,7 @@
   3.104          w.write("  }\n");
   3.105          w.write("  private " + className + " clone(net.java.html.BrwsrCtx ctx) {\n");
   3.106          w.write("    Object newUnion = null;\n");
   3.107 +        w.write("    org.apidesign.html.json.spi.Proto newProto = null;\n");
   3.108          for (Element ec : e.getEnclosedElements()) {
   3.109              if (ec.getKind() != ElementKind.ENUM_CONSTANT) {
   3.110                  continue;
   3.111 @@ -2018,9 +2019,13 @@
   3.112              if (em == null) {
   3.113                  continue;
   3.114              }
   3.115 -            w.write("    if (union instanceof " + em.className() + ") newUnion = ((" + em.className() + ")union).clone(ctx);\n");
   3.116 +            w.write("    if (union instanceof " + em.className() + ") {\n");
   3.117 +            w.write("      newUnion = ((" + em.className() + ")union).clone(ctx);\n");
   3.118 +            w.write("      newProto = ((" + em.className() + ")newUnion).proto;\n");
   3.119 +            w.write("    }\n");
   3.120          }
   3.121          w.write("    " + className + " ret = new " + className + "(ctx, newUnion);\n");
   3.122 +        w.write("    newProto.onValueHasMutated(ret.proto);\n");
   3.123          for (Prprt p : props) {
   3.124              if (!p.array()) {
   3.125                  boolean isModel[] = { false };
     4.1 --- a/json/src/test/java/net/java/html/json/ModelTest.java	Wed Jan 29 15:49:48 2014 +0100
     4.2 +++ b/json/src/test/java/net/java/html/json/ModelTest.java	Thu Jan 30 11:36:32 2014 +0100
     4.3 @@ -175,6 +175,7 @@
     4.4          
     4.5          // not interested in change of this property
     4.6          my.mutated.remove("changedProperty");
     4.7 +        my.mutated.remove("changedProperty");
     4.8          
     4.9          assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
    4.10          assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated);
     5.1 --- a/json/src/test/java/org/netbeans/html/json/impl/PlainEnumTest.java	Wed Jan 29 15:49:48 2014 +0100
     5.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/PlainEnumTest.java	Thu Jan 30 11:36:32 2014 +0100
     5.3 @@ -66,7 +66,8 @@
     5.4          
     5.5          assertEquals(on.getAbc(), Abc.A);
     5.6          assertNotNull(on.getA());
     5.7 -        assertEquals(on.getA().getUa(), 9);
     5.8 +        assertEquals(on.getA().getUa().size(), 1);
     5.9 +        assertEquals(on.getA().getUa().get(0), Integer.valueOf(9));
    5.10          assertNull(on.getB());
    5.11      }
    5.12  
    5.13 @@ -101,7 +102,7 @@
    5.14      })
    5.15      enum Abc {
    5.16          @Model(className = "A", properties = { 
    5.17 -            @Property(name = "ua", type = int.class)
    5.18 +            @Property(name = "ua", type = int.class, array = true)
    5.19          })
    5.20          A, 
    5.21          @Model(className = "B", properties = { 
    5.22 @@ -163,4 +164,22 @@
    5.23          assertEquals(u.getX(), 12, "Change increases X");
    5.24          assertEquals(ub.getChanges(), 1, "One change");
    5.25      }
    5.26 +    
    5.27 +    @Test public void changeInUnionArray() {
    5.28 +        MapModelTest.MapTechnology mt = new MapModelTest.MapTechnology();
    5.29 +        BrwsrCtx ctx = Contexts.newBuilder().register(Technology.class, mt, 1).build();
    5.30 +
    5.31 +        Union on = new Union(new Union.A(10, 20, 30), 0, 1.1);
    5.32 +        Union u = Models.bind(on, ctx);
    5.33 +
    5.34 +        Map<?, ?> map = (Map<?, ?>) Models.toRaw(u);
    5.35 +        MapModelTest.One ua;
    5.36 +        assertNotNull(ua = (MapModelTest.One) map.get("ua"), "Array prop found: " + map);
    5.37 +        
    5.38 +        assertEquals(u.getX(), 0, "Zero");
    5.39 +        assertEquals(ua.getChanges(), 0, "No changes yet");
    5.40 +        u.getA().getUa().add(40);
    5.41 +        assertEquals(u.getX(), 1, "Incremented once");
    5.42 +        assertEquals(ua.getChanges(), 1, "One change");
    5.43 +    }
    5.44  }