1.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Proto.java Thu Jul 31 15:35:28 2014 +0200
1.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Proto.java Fri Aug 01 12:36:56 2014 +0200
1.3 @@ -68,8 +68,10 @@
1.4 private final Object obj;
1.5 private final Type type;
1.6 private final net.java.html.BrwsrCtx context;
1.7 - private boolean locked;
1.8 private org.netbeans.html.json.impl.Bindings ko;
1.9 + private Watcher myOwn;
1.10 + private Watcher.Ref observers;
1.11 + private static Watcher locked;
1.12
1.13 Proto(Object obj, Type type, BrwsrCtx context) {
1.14 this.obj = obj;
1.15 @@ -91,22 +93,36 @@
1.16 * @throws IllegalStateException if already locked
1.17 */
1.18 public void acquireLock() throws IllegalStateException {
1.19 - if (locked) throw new IllegalStateException();
1.20 - locked = true;
1.21 + if (locked != null) throw new IllegalStateException();
1.22 + locked = Watcher.DUMMY;
1.23 + }
1.24 + public void acquireLock(String propName) throws IllegalStateException {
1.25 + if (locked != null) throw new IllegalStateException();
1.26 + locked = Watcher.computing(this, propName);
1.27 + }
1.28 +
1.29 + public void accessValue(String propName) {
1.30 + if (locked != null) {
1.31 + if (locked.proto == this) {
1.32 + throw new IllegalStateException();
1.33 + }
1.34 + observers = locked.observe(observers, propName);
1.35 + }
1.36 }
1.37
1.38 /** Verifies the model is not locked otherwise throws an exception.
1.39 * @throws IllegalStateException if the model is locked
1.40 */
1.41 public void verifyUnlocked() throws IllegalStateException {
1.42 - if (locked) throw new IllegalStateException();
1.43 + if (locked != null) throw new IllegalStateException();
1.44 }
1.45
1.46 /** When modifications are over, the model is switched into
1.47 * unlocked state by calling this method.
1.48 */
1.49 public void releaseLock() {
1.50 - locked = false;
1.51 + myOwn = locked;
1.52 + locked = null;
1.53 }
1.54
1.55 /** Whenever model changes a property. It should notify the
1.56 @@ -124,6 +140,9 @@
1.57 if (ko != null) {
1.58 ko.valueHasMutated(propName, null, null);
1.59 }
1.60 + if (observers != null) {
1.61 + observers = Watcher.Ref.valueHasMutated(observers, propName);
1.62 + }
1.63 }
1.64 });
1.65 }
1.66 @@ -151,6 +170,9 @@
1.67 if (ko != null) {
1.68 ko.valueHasMutated(propName, oldValue, newValue);
1.69 }
1.70 + if (observers != null) {
1.71 + observers = Watcher.Ref.valueHasMutated(observers, propName);
1.72 + }
1.73 }
1.74 });
1.75 }
1.76 @@ -439,6 +461,11 @@
1.77 type.onChange(obj, index);
1.78 }
1.79
1.80 + final Watcher watcher(String prop) {
1.81 + assert myOwn == null || prop.equals(myOwn.prop);
1.82 + return myOwn;
1.83 + }
1.84 +
1.85 /** Functionality used by the code generated by annotation
1.86 * processor for the {@link net.java.html.json.Model} annotation.
1.87 *
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Watcher.java Fri Aug 01 12:36:56 2014 +0200
2.3 @@ -0,0 +1,131 @@
2.4 +/**
2.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2.6 + *
2.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
2.8 + *
2.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
2.10 + * Other names may be trademarks of their respective owners.
2.11 + *
2.12 + * The contents of this file are subject to the terms of either the GNU
2.13 + * General Public License Version 2 only ("GPL") or the Common
2.14 + * Development and Distribution License("CDDL") (collectively, the
2.15 + * "License"). You may not use this file except in compliance with the
2.16 + * License. You can obtain a copy of the License at
2.17 + * http://www.netbeans.org/cddl-gplv2.html
2.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
2.19 + * specific language governing permissions and limitations under the
2.20 + * License. When distributing the software, include this License Header
2.21 + * Notice in each file and include the License file at
2.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
2.23 + * particular file as subject to the "Classpath" exception as provided
2.24 + * by Oracle in the GPL Version 2 section of the License file that
2.25 + * accompanied this code. If applicable, add the following below the
2.26 + * License Header, with the fields enclosed by brackets [] replaced by
2.27 + * your own identifying information:
2.28 + * "Portions Copyrighted [year] [name of copyright owner]"
2.29 + *
2.30 + * Contributor(s):
2.31 + *
2.32 + * The Original Software is NetBeans. The Initial Developer of the Original
2.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
2.34 + *
2.35 + * If you wish your version of this file to be governed by only the CDDL
2.36 + * or only the GPL Version 2, indicate your decision by adding
2.37 + * "[Contributor] elects to include this software in this distribution
2.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
2.39 + * single choice of license, a recipient has the option to distribute
2.40 + * your version of this file under either the CDDL, the GPL Version 2 or
2.41 + * to extend the choice of license to its licensees as provided above.
2.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
2.43 + * Version 2 license, then the option applies only if the new code is
2.44 + * made subject to such option by the copyright holder.
2.45 + */
2.46 +package org.apidesign.html.json.spi;
2.47 +
2.48 +import java.lang.ref.WeakReference;
2.49 +
2.50 +/**
2.51 + *
2.52 + * @author Jaroslav Tulach
2.53 + */
2.54 +final class Watcher {
2.55 + static final Watcher DUMMY = new Watcher(null, null);
2.56 +
2.57 + final Proto proto;
2.58 + final String prop;
2.59 +
2.60 + private Watcher(Proto proto, String prop) {
2.61 + this.proto = proto;
2.62 + this.prop = prop;
2.63 + }
2.64 +
2.65 +
2.66 + static Watcher computing(Proto proto, String prop) {
2.67 + proto.getClass();
2.68 + prop.getClass();
2.69 + return new Watcher(proto, prop);
2.70 + }
2.71 +
2.72 + Ref observe(Ref prev, String prop) {
2.73 + if (this == DUMMY) {
2.74 + throw new IllegalStateException();
2.75 + }
2.76 + return new Ref(this, prop).chain(prev);
2.77 + }
2.78 +
2.79 + static final class Ref extends WeakReference<Watcher> {
2.80 + private final String prop;
2.81 + private Ref next;
2.82 +
2.83 + public Ref(Watcher ref, String prop) {
2.84 + super(ref);
2.85 + this.prop = prop;
2.86 + }
2.87 +
2.88 + Ref chain(Ref prev) {
2.89 + this.next = dropDead(prev, null);
2.90 + return this;
2.91 + }
2.92 +
2.93 + private Watcher watcher() {
2.94 + Watcher w = get();
2.95 + if (w != null && w.proto.watcher(w.prop) == w) {
2.96 + return w;
2.97 + }
2.98 + return null;
2.99 + }
2.100 +
2.101 + private static Ref dropDead(Ref self, String fireProp) {
2.102 + while (self != null && self.watcher() == null) {
2.103 + self = self.next;
2.104 + }
2.105 + if (self == null) {
2.106 + return null;
2.107 + }
2.108 + Ref current = self;
2.109 + for (;;) {
2.110 + Watcher w = current.watcher();
2.111 + if (w != null && fireProp != null && fireProp.equals(current.prop)) {
2.112 + w.proto.valueHasMutated(w.prop);
2.113 + }
2.114 + for (;;) {
2.115 + Ref next = current.next;
2.116 + if (next == null) {
2.117 + return self;
2.118 + }
2.119 + if (next.watcher() != null) {
2.120 + current = next;
2.121 + break;
2.122 + } else {
2.123 + current.next = next.next;
2.124 + }
2.125 + }
2.126 + }
2.127 +
2.128 + }
2.129 +
2.130 + static Ref valueHasMutated(Ref self, String propName) {
2.131 + return dropDead(self, propName);
2.132 + }
2.133 + }
2.134 +}
3.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Thu Jul 31 15:35:28 2014 +0200
3.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java Fri Aug 01 12:36:56 2014 +0200
3.3 @@ -544,14 +544,14 @@
3.4
3.5 castTo = "java.util.List";
3.6 w.write(" public java.util.List<" + tn + "> " + gs[0] + "() {\n");
3.7 - w.write(" proto.verifyUnlocked();\n");
3.8 + w.write(" proto.accessValue(\"" + p.name() + "\");\n");
3.9 w.write(" return prop_" + p.name() + ";\n");
3.10 w.write(" }\n");
3.11 } else {
3.12 castTo = tn;
3.13 w.write(" private " + tn + " prop_" + p.name() + ";\n");
3.14 w.write(" public " + tn + " " + gs[0] + "() {\n");
3.15 - w.write(" proto.verifyUnlocked();\n");
3.16 + w.write(" proto.accessValue(\"" + p.name() + "\");\n");
3.17 w.write(" return prop_" + p.name() + ";\n");
3.18 w.write(" }\n");
3.19 w.write(" public void " + gs[1] + "(" + tn + " v) {\n");
3.20 @@ -676,7 +676,7 @@
3.21 depends.add(new String[] { sn, gs[0] });
3.22 }
3.23 w.write(" try {\n");
3.24 - w.write(" proto.acquireLock();\n");
3.25 + w.write(" proto.acquireLock(\"" + sn + "\");\n");
3.26 w.write(" return " + fqn(ee.getEnclosingElement().asType(), ee) + '.' + e.getSimpleName() + "(");
3.27 String sep = "";
3.28 for (int i = 1; i <= arg; i++) {
4.1 --- a/json/src/test/java/net/java/html/json/DeepChangeTest.java Thu Jul 31 15:35:28 2014 +0200
4.2 +++ b/json/src/test/java/net/java/html/json/DeepChangeTest.java Fri Aug 01 12:36:56 2014 +0200
4.3 @@ -83,7 +83,7 @@
4.4 static class Y {
4.5 }
4.6
4.7 - @Test public void isThereABinding() throws Exception {
4.8 + @Test public void isTransitiveChangeNotifiedProperly() throws Exception {
4.9 MyX p = Models.bind(
4.10 new MyX(new MyY("Ahoj"), new MyY("Hi"), new MyY("Hello")
4.11 ), c).applyBindings();