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 }