# HG changeset patch # User Jaroslav Tulach # Date 1391078192 -3600 # Node ID 4b66c5141f840347f7f20343ab2812729ad1e880 # Parent 501413c8638cd88720c9ff8e4c73c57dd01d67fa Property changes are delivered when in-a union property changes diff -r 501413c8638c -r 4b66c5141f84 json/src/main/java/org/apidesign/html/json/spi/Proto.java --- a/json/src/main/java/org/apidesign/html/json/spi/Proto.java Wed Jan 29 15:49:48 2014 +0100 +++ b/json/src/main/java/org/apidesign/html/json/spi/Proto.java Thu Jan 30 11:36:32 2014 +0100 @@ -70,6 +70,7 @@ private final net.java.html.BrwsrCtx context; private boolean locked; private org.netbeans.html.json.impl.Bindings ko; + private Proto onMutated; Proto(Object obj, Type type, BrwsrCtx context) { this.obj = obj; @@ -115,11 +116,36 @@ *@param propName name of the changed property */ public void valueHasMutated(String propName) { + if (onMutated != null) { + onMutated.valueHasMutated(propName); + } if (ko != null) { ko.valueHasMutated(propName); + final String[] names = type.propertyNames; + for (int i = 0; i < names.length; i++) { + if (names[i].equals(propName)) { + type.onChange(obj, i); + } + } } } + /** Allows another prototype to be notified when a value is mutated using + * {@link #valueHasMutated(java.lang.String)} method of this object. + * This method can be called only once. It is primarily useful for + * support of {@link Model}s on enums. + * + * @param m object which {@link #valueHasMutated(java.lang.String)} + * method should be called when there is a change in a property + * of this proto object. + * @since 0.8 + */ + public void onValueHasMutated(Proto m) { + assert m != null; + assert onMutated == null; + onMutated = m; + } + /** Initializes the associated model in the current {@link #getContext() context}. * In case of knockout.js technology, applies given bindings * of the current model to the body element of the page. @@ -662,5 +688,5 @@ return type.cast(val); } - } + } // end of Type } diff -r 501413c8638c -r 4b66c5141f84 json/src/main/java/org/netbeans/html/json/impl/JSONList.java --- a/json/src/main/java/org/netbeans/html/json/impl/JSONList.java Wed Jan 29 15:49:48 2014 +0100 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSONList.java Thu Jan 30 11:36:32 2014 +0100 @@ -184,6 +184,7 @@ PropertyBindingAccessor.notifyProtoChange(proto, index); } } + proto.valueHasMutated(name); } @Override diff -r 501413c8638c -r 4b66c5141f84 json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Wed Jan 29 15:49:48 2014 +0100 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Thu Jan 30 11:36:32 2014 +0100 @@ -266,15 +266,15 @@ w.append(" private static Class<" + inPckName(e) + "> modelFor() { return null; }\n"); w.append(" private ").append(className).append("(net.java.html.BrwsrCtx context) {\n"); w.append(" this.proto = TYPE.createProto(this, context);\n"); - for (Prprt p : props) { + for (int i = 0; i < props.length; i++) { + Prprt p = props[i]; if (p.array()) { final String tn = typeName(e, p); String[] gs = toGetSet(p.name(), tn, p.array()); w.write(" this.prop_" + p.name() + " = proto.createList(\"" + p.name() + "\""); if (functionDeps.containsKey(p.name())) { - int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name()); - w.write(", " + index); + w.write(", " + i); } else { w.write(", -1"); } @@ -404,9 +404,9 @@ w.append(" @Override public void onChange(" + className + " model, int type) {\n"); w.append(" switch (type) {\n"); { - String[] arr = functionDeps.keySet().toArray(new String[0]); - for (int i = 0; i < arr.length; i++) { - Collection onChange = functionDeps.get(arr[i]); + for (int i = 0; i < props.length; i++) { + String pn = props[i].name(); + Collection onChange = functionDeps.get(pn); if (onChange != null) { w.append(" case " + i + ":\n"); for (String call : onChange) { @@ -416,8 +416,7 @@ } } } - w.append(" }\n"); - w.append(" throw new UnsupportedOperationException();\n"); + w.append(" }\n"); w.append(" }\n"); w.append(onReceiveType); w.append(" @Override public " + className + " read(net.java.html.BrwsrCtx c, Object json) { return new " + className + "(c, json); }\n"); @@ -638,15 +637,15 @@ w.append(" }\n"); w.append(" private ").append(className).append("(net.java.html.BrwsrCtx context, Object union, Object json) {\n"); w.append(" this.proto = TYPE.createProto(this, context);\n"); - for (Prprt p : props) { + for (int i = 0; i < props.length; i++) { + Prprt p = props[i]; if (p.array()) { final String tn = typeName(e, p); String[] gs = toGetSet(p.name(), tn, p.array()); w.write(" this.prop_" + p.name() + " = proto.createList(\"" + p.name() + "\""); if (functionDeps.containsKey(p.name())) { - int index = Arrays.asList(functionDeps.keySet().toArray()).indexOf(p.name()); - w.write(", " + index); + w.write(", " + i); } else { w.write(", -1"); } @@ -731,6 +730,7 @@ // end of enum w.append(") {\n"); w.append(" this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class), union);\n"); + w.append(" ((" + em.className() + ")union).proto.onValueHasMutated(this.proto);\n"); for (Prprt p : props) { if (p.array()) { continue; @@ -827,9 +827,9 @@ w.append(" @Override public void onChange(" + className + " model, int type) {\n"); w.append(" switch (type) {\n"); { - String[] arr = functionDeps.keySet().toArray(new String[0]); - for (int i = 0; i < arr.length; i++) { - Collection onChange = functionDeps.get(arr[i]); + for (int i = 0; i < allProps.size(); i++) { + String pn = allProps.get(i).name(); + Collection onChange = functionDeps.get(pn); if (onChange != null) { w.append(" case " + i + ":\n"); for (String call : onChange) { @@ -839,8 +839,7 @@ } } } - w.append(" }\n"); - w.append(" throw new UnsupportedOperationException();\n"); + w.append(" }\n"); w.append(" }\n"); w.append(onReceiveType); w.append(" @Override public " + className + " read(net.java.html.BrwsrCtx c, Object json) {\n"); @@ -1011,6 +1010,7 @@ if (props.length > 0) { w.write(" public " + className + "(" + em.className() + " union) {\n"); w.append(" this(net.java.html.BrwsrCtx.findDefault(").append(className).append(".class), union, null);\n"); + w.append(" ((" + em.className() + ")union).proto.onValueHasMutated(this.proto);\n"); w.write(" }\n"); } generateEnumConstantModel(w, ec, sharedProps); @@ -2010,6 +2010,7 @@ w.write(" }\n"); w.write(" private " + className + " clone(net.java.html.BrwsrCtx ctx) {\n"); w.write(" Object newUnion = null;\n"); + w.write(" org.apidesign.html.json.spi.Proto newProto = null;\n"); for (Element ec : e.getEnclosedElements()) { if (ec.getKind() != ElementKind.ENUM_CONSTANT) { continue; @@ -2018,9 +2019,13 @@ if (em == null) { continue; } - w.write(" if (union instanceof " + em.className() + ") newUnion = ((" + em.className() + ")union).clone(ctx);\n"); + w.write(" if (union instanceof " + em.className() + ") {\n"); + w.write(" newUnion = ((" + em.className() + ")union).clone(ctx);\n"); + w.write(" newProto = ((" + em.className() + ")newUnion).proto;\n"); + w.write(" }\n"); } w.write(" " + className + " ret = new " + className + "(ctx, newUnion);\n"); + w.write(" newProto.onValueHasMutated(ret.proto);\n"); for (Prprt p : props) { if (!p.array()) { boolean isModel[] = { false }; diff -r 501413c8638c -r 4b66c5141f84 json/src/test/java/net/java/html/json/ModelTest.java --- a/json/src/test/java/net/java/html/json/ModelTest.java Wed Jan 29 15:49:48 2014 +0100 +++ b/json/src/test/java/net/java/html/json/ModelTest.java Thu Jan 30 11:36:32 2014 +0100 @@ -175,6 +175,7 @@ // not interested in change of this property my.mutated.remove("changedProperty"); + my.mutated.remove("changedProperty"); assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated); assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated); diff -r 501413c8638c -r 4b66c5141f84 json/src/test/java/org/netbeans/html/json/impl/PlainEnumTest.java --- a/json/src/test/java/org/netbeans/html/json/impl/PlainEnumTest.java Wed Jan 29 15:49:48 2014 +0100 +++ b/json/src/test/java/org/netbeans/html/json/impl/PlainEnumTest.java Thu Jan 30 11:36:32 2014 +0100 @@ -66,7 +66,8 @@ assertEquals(on.getAbc(), Abc.A); assertNotNull(on.getA()); - assertEquals(on.getA().getUa(), 9); + assertEquals(on.getA().getUa().size(), 1); + assertEquals(on.getA().getUa().get(0), Integer.valueOf(9)); assertNull(on.getB()); } @@ -101,7 +102,7 @@ }) enum Abc { @Model(className = "A", properties = { - @Property(name = "ua", type = int.class) + @Property(name = "ua", type = int.class, array = true) }) A, @Model(className = "B", properties = { @@ -163,4 +164,22 @@ assertEquals(u.getX(), 12, "Change increases X"); assertEquals(ub.getChanges(), 1, "One change"); } + + @Test public void changeInUnionArray() { + MapModelTest.MapTechnology mt = new MapModelTest.MapTechnology(); + BrwsrCtx ctx = Contexts.newBuilder().register(Technology.class, mt, 1).build(); + + Union on = new Union(new Union.A(10, 20, 30), 0, 1.1); + Union u = Models.bind(on, ctx); + + Map map = (Map) Models.toRaw(u); + MapModelTest.One ua; + assertNotNull(ua = (MapModelTest.One) map.get("ua"), "Array prop found: " + map); + + assertEquals(u.getX(), 0, "Zero"); + assertEquals(ua.getChanges(), 0, "No changes yet"); + u.getA().getUa().add(40); + assertEquals(u.getX(), 1, "Incremented once"); + assertEquals(ua.getChanges(), 1, "One change"); + } }