Coverage Report - com.sun.javafx.runtime.location.Bindings
 
Classes in this File Line Coverage Branch Coverage Complexity
Bindings
76%
22/29
81%
13/16
0
Bindings$1
100%
3/3
N/A
0
Bindings$BijectiveBinding
100%
31/31
83%
20/24
0
Bindings$BijectiveBinding$1
100%
14/14
58%
7/12
0
Bindings$BijectiveBinding$2
93%
13/14
50%
6/12
0
Bindings$BijectiveBinding$BijectiveChangeListener
N/A
N/A
0
 
 1  
 /*
 2  
  * Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
 3  
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 4  
  *
 5  
  * This code is free software; you can redistribute it and/or modify it
 6  
  * under the terms of the GNU General Public License version 2 only, as
 7  
  * published by the Free Software Foundation.  Sun designates this
 8  
  * particular file as subject to the "Classpath" exception as provided
 9  
  * by Sun in the LICENSE file that accompanied this code.
 10  
  *
 11  
  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  
  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  
  * version 2 for more details (a copy is included in the LICENSE file that
 15  
  * accompanied this code).
 16  
  *
 17  
  * You should have received a copy of the GNU General Public License version
 18  
  * 2 along with this work; if not, write to the Free Software Foundation,
 19  
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  
  *
 21  
  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 22  
  * CA 95054 USA or visit www.sun.com if you need additional information or
 23  
  * have any questions.
 24  
  */
 25  
 
 26  
 package com.sun.javafx.runtime.location;
 27  
 
 28  
 import java.lang.ref.WeakReference;
 29  
 import java.util.*;
 30  
 
 31  
 import com.sun.javafx.runtime.BindingException;
 32  
 import com.sun.javafx.runtime.CircularBindingException;
 33  
 
 34  
 /**
 35  
  * Bindings -- helper class for setting up bijective bindings.
 36  
  *
 37  
  * Bijective bindings between locations A and B are implemented by attaching a (strong) listener to each A and
 38  
  * B, and updating the other when one is updated. The listeners share state, so they do not pass the updated
 39  
  * on to the other if the other is already in the right state, avoiding an infinite mutual ping-ponging of
 40  
  * updates. The listeners share weak references back to A and B, so that when one of A or B is garbage
 41  
  * collected, the next time the other is updated, the listeners observe that and return false from the
 42  
  * onChange() method to detach the listener.
 43  
  *
 44  
  * @author Brian Goetz
 45  
  */
 46  0
 public class Bindings {
 47  
 
 48  
     public static<T> Bijection<T, T> identityBinding() {
 49  10
         return new Bijection<T, T>() {
 50  
             public T mapForwards(T a) {
 51  6
                 return a;
 52  
             }
 53  
 
 54  
             public T mapBackwards(T b) {
 55  19
                 return b;
 56  
             }
 57  
         };
 58  
     }
 59  
 
 60  
     /** Create a bijective binding between objects of type T and U */
 61  
     public static <T, U> void bijectiveBind(ObjectLocation<T> a, ObjectLocation<U> b, Bijection<T, U> mapper) {
 62  29
         new BijectiveBinding<T, U>(a, b, mapper);
 63  17
     }
 64  
 
 65  
     /** Create a bijective binding between objects of type T and U */
 66  
     public static <T> void bijectiveBind(ObjectLocation<T> a, ObjectLocation<T> b) {
 67  10
         Bijection<T, T> id = identityBinding();
 68  10
         bijectiveBind(a, b, id);
 69  10
     }
 70  
 
 71  
     /** Create a new location that is bidirectionally bound to another */
 72  
     public static<T> ObjectLocation<T> makeBijectiveBind(ObjectLocation<T> other) {
 73  0
         ObjectVariable<T> me = ObjectVariable.make();
 74  0
         bijectiveBind(me, other);
 75  0
         return me;
 76  
     }
 77  
 
 78  
     /** Return the set of locations that are "peered" with this one through a chain of bidirectional bindings */
 79  
     static Collection<Location> getPeerLocations(Location location) {
 80  42
         Collection<Location> newLocs = BijectiveBinding.getDirectPeers(location);
 81  42
         if (newLocs.size() == 0)
 82  17
             return newLocs;
 83  
 
 84  25
         Set<Location> knownLocs = new HashSet<Location>();
 85  25
         LinkedList<Location> toExplore = new LinkedList<Location>(newLocs);
 86  101
         while (toExplore.size() > 0) {
 87  76
             Location loc = toExplore.removeFirst();
 88  76
             while (loc instanceof StaticViewLocation)
 89  0
                 loc = ((StaticViewLocation) loc).getUnderlyingLocation();
 90  76
             if (!knownLocs.contains(loc) && loc != location) {
 91  38
                 knownLocs.add(loc);
 92  38
                 toExplore.addAll(BijectiveBinding.getDirectPeers(loc));
 93  
             }
 94  76
         }
 95  25
         return knownLocs;
 96  
     }
 97  
 
 98  
     static boolean isPeerLocation(Location a, Location b) {
 99  31
         Collection<Location> aPeers = getPeerLocations(a);
 100  31
         while (b instanceof StaticViewLocation)
 101  0
             b = ((StaticViewLocation) b).getUnderlyingLocation();
 102  31
         return (aPeers != null && aPeers.contains(b));
 103  
     }
 104  
 
 105  245
     private static class BijectiveBinding<T, U> {
 106  
         private final WeakReference<ObjectLocation<T>> aRef;
 107  
         private final WeakReference<ObjectLocation<U>> bRef;
 108  
         private final Bijection<T, U> mapper;
 109  
         private T lastA;
 110  
         private U lastB;
 111  
 
 112  29
         public BijectiveBinding(ObjectLocation<T> a, ObjectLocation<U> b, Bijection<T, U> mapper) {
 113  29
             if (!(a.isMutable()) || !(b.isMutable()))
 114  6
                 throw new BindingException("Both components of bijective bind must be mutable");
 115  23
             if (isPeerLocation(a, b))
 116  6
                 throw new CircularBindingException("Binding circularity detected");
 117  
             
 118  17
             this.aRef = new WeakReference<ObjectLocation<T>>(a);
 119  17
             this.bRef = new WeakReference<ObjectLocation<U>>(b);
 120  17
             this.mapper = mapper;
 121  
 
 122  
             // Set A before setting up the listeners
 123  17
             a.set(mapper.mapBackwards(b.get()));
 124  
 
 125  17
             a.addChangeListener(new BijectiveChangeListener() {
 126  
                 public boolean onChange() {
 127  28
                     ObjectLocation<T> a = aRef.get();
 128  28
                     ObjectLocation<U> b = bRef.get();
 129  28
                     if (a == null || b == null)
 130  1
                         return false;
 131  27
                     T newA = a.get();
 132  27
                     if ((newA == null && lastA == null) || (newA != null && newA.equals(lastA)))
 133  16
                         return true;
 134  11
                     U newB = BijectiveBinding.this.mapper.mapForwards(newA);
 135  11
                     lastA = newA;
 136  11
                     lastB = newB;
 137  11
                     b.set(newB);
 138  11
                     return true;
 139  
                 }
 140  
 
 141  
                 public BijectiveBinding getBijectiveBinding() {
 142  38
                     return BijectiveBinding.this;
 143  
                 }
 144  
             });
 145  17
             b.addChangeListener(new BijectiveChangeListener() {
 146  
                 public boolean onChange() {
 147  27
                     ObjectLocation<T> a = aRef.get();
 148  27
                     ObjectLocation<U> b = bRef.get();
 149  27
                     if (a == null || b == null)
 150  0
                         return false;
 151  27
                     U newB = b.get();
 152  27
                     if ((newB == null && lastB == null) || (newB != null && newB.equals(lastB)))
 153  11
                         return true;
 154  16
                     T newA = BijectiveBinding.this.mapper.mapBackwards(newB);
 155  16
                     lastA = newA;
 156  16
                     lastB = newB;
 157  16
                     a.set(newA);
 158  16
                     return true;
 159  
                 }
 160  
 
 161  
                 public BijectiveBinding getBijectiveBinding() {
 162  38
                     return BijectiveBinding.this;
 163  
                 }
 164  
             });
 165  17
         }
 166  
 
 167  
         public static Collection<Location> getDirectPeers(Location loc) {
 168  80
             Set<Location> set = null;
 169  80
             for (ChangeListener cl : loc.getListeners()) {
 170  76
                 if (cl instanceof BijectiveChangeListener) {
 171  76
                     BijectiveBinding<?, ?> bb = ((BijectiveChangeListener) cl).getBijectiveBinding();
 172  76
                     ObjectLocation<?> a = (ObjectLocation) bb.aRef.get();
 173  76
                     ObjectLocation<?> b = (ObjectLocation) bb.bRef.get();
 174  76
                     if (a != null && a != loc) {
 175  38
                         if (set == null)
 176  38
                             set = new HashSet<Location>();
 177  38
                         set.add(a);
 178  
                     }
 179  76
                     if (b != null && b != loc) {
 180  38
                         if (set == null)
 181  25
                             set = new HashSet<Location>();
 182  38
                         set.add(b);
 183  
                     }
 184  76
                 }
 185  
             }
 186  80
             if (set != null)
 187  63
                 return set;
 188  
             else
 189  17
                 return Collections.emptySet();
 190  
         }
 191  
 
 192  
         private interface BijectiveChangeListener extends ChangeListener {
 193  
             public abstract BijectiveBinding getBijectiveBinding();
 194  
         }
 195  
     }
 196  
 
 197  
 }