Coverage Report - com.sun.javafx.runtime.location.AbstractLocation
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractLocation
93%
78/84
98%
49/50
0
WeakListener
100%
4/4
100%
2/2
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  
 /**
 32  
  * AbstractLocation is a base class for Location implementations, handling change listener notification and lazy updates.
 33  
  *
 34  
  * @author Brian Goetz
 35  
  */
 36  11557
 public abstract class AbstractLocation implements Location {
 37  
     private boolean isValid, mustRemoveDependencies;
 38  
 
 39  
     // We separate listeners from dependent locations because updating of dependent locations is split into an
 40  
     // invalidation phase and an update phase (this is to support lazy locations.)  So there are times when we want
 41  
     // to invalidate downstream locations but not yet invoke their change listeners.
 42  
     protected List<ChangeListener> listeners;
 43  
     protected List<WeakReference<Location>> dependentLocations;
 44  
 
 45  
     // The implementation of dynamic dependencies exploits the clearability of weak references.  When we register
 46  
     // ourselves as being dynamically dependent on another location for the first time, we create a weak reference
 47  
     // for ourselves that we remember and use for subsequence dynamic dependencies.  When we are asked to clear the
 48  
     // dynamic dependencies (unregister ourselves with any location with which we've registered as a dynamic dependency),
 49  
     // we clear() the weak reference and forget it, which will eventually cause those other locations to forget about us.
 50  
     private WeakReference<Location> weakMe;
 51  
 
 52  
     // Dynamic dependencies introduce a problem; we register dependent locations from the update() method, while the
 53  
     // list of dependencies is being iterated, causing CME.  So we defer adding anything to the list until we're done
 54  
     // iterating
 55  
     private int iterationDepth;
 56  
     private List<WeakReference<Location>> deferredDependencies;
 57  
 
 58  
     public boolean isValid() {
 59  40987
         return isValid;
 60  
     }
 61  
 
 62  
     public boolean isMutable() {
 63  0
         return true;
 64  
     }
 65  
 
 66  
     protected void setValid() {
 67  28951
         isValid = true;
 68  28951
     }
 69  
 
 70  
     public void invalidate() {
 71  5864
         isValid = false;
 72  5864
         doInvalidateDependencies();
 73  5864
     }
 74  
 
 75  
     /**
 76  
      * Notify change triggers that the value has changed.  This should be done automatically by mutative methods,
 77  
      * and is also used at object initialization time to defer notification of changes until the values provided
 78  
      * in the object literal are all set.
 79  
      */
 80  
     protected void invalidateDependencies() {
 81  26049
         doInvalidateDependencies();
 82  26049
     }
 83  
 
 84  
     private void purgeDeadDependencies() {
 85  
         //System.out.println("purge: "+ Thread.currentThread());
 86  5478
         if (dependentLocations != null) {
 87  5455
             for (Iterator<WeakReference<Location>> iterator = dependentLocations.iterator(); iterator.hasNext();) {
 88  19846
                 WeakReference<Location> locationRef = iterator.next();
 89  19846
                 Location loc = locationRef.get();
 90  19846
                 if (loc == null)
 91  29
                     iterator.remove();
 92  19846
             }
 93  
         }
 94  5478
     }
 95  
 
 96  
     private void doInvalidateDependencies() {
 97  31913
         notifyChangeListeners();
 98  31913
         if (dependentLocations != null) {
 99  
             //System.out.println("invalidate: "+ Thread.currentThread() + " " +dependentLocations.size());
 100  
             try {
 101  1535
                 ++iterationDepth;
 102  1535
                 for (WeakReference<Location> locationRef : dependentLocations) {
 103  2236
                     Location loc = locationRef.get();
 104  2236
                     if (loc == null)
 105  11
                         mustRemoveDependencies = true;
 106  
                     else {
 107  2225
                         loc.invalidate();
 108  
                         // Space optimization: try for early removal of dynamic dependencies, in the case that
 109  
                         // the dependency is a "weakMe" reference for some object that has been cleared in update()
 110  2225
                         if (locationRef.get() == null)
 111  3
                             mustRemoveDependencies = true;
 112  
                     }
 113  2236
                 }
 114  
             }
 115  
             finally {
 116  1535
                 --iterationDepth;
 117  1535
                 if (iterationDepth == 0) {
 118  1535
                     if (deferredDependencies != null && deferredDependencies.size() > 0) {
 119  
                         // @@@ Hack: overly-aggressive purge
 120  7
                         purgeDeadDependencies();
 121  7
                         dependentLocations.addAll(deferredDependencies);
 122  7
                         deferredDependencies.clear();
 123  
                     }
 124  1535
                     if (mustRemoveDependencies) {
 125  14
                         purgeDeadDependencies();
 126  14
                         mustRemoveDependencies = false;
 127  
                     }
 128  
                 }
 129  
             }
 130  
         }
 131  31913
     }
 132  
 
 133  
     // Change listeners are really invalidation listeners; triggers are managed by the higher-level XxxVariable classes.
 134  
     // Ideally we should merge change listeners and dependencies so there's only way of expressing an invalidation dependency.
 135  
     private void notifyChangeListeners() {
 136  31913
         if (listeners != null) {
 137  466
             for (Iterator<ChangeListener> iterator = listeners.iterator(); iterator.hasNext();) {
 138  473
                 ChangeListener listener = iterator.next();
 139  473
                 if (!listener.onChange())
 140  3
                     iterator.remove();
 141  473
             }
 142  
         }
 143  31913
     }
 144  
 
 145  
     public void addDependentLocation(WeakReference<Location> locationRef) {
 146  5412
         if (dependentLocations == null)
 147  4802
             dependentLocations = new ArrayList<WeakReference<Location>>();
 148  5412
         if (iterationDepth > 0) {
 149  9
             if (deferredDependencies == null)
 150  5
                 deferredDependencies = new ArrayList<WeakReference<Location>>();
 151  9
             deferredDependencies.add(locationRef);
 152  
         }
 153  
         else {
 154  
             // @@@ Hack: overly-aggressive purge
 155  5403
             purgeDeadDependencies();
 156  5403
             dependentLocations.add(locationRef);
 157  
         }
 158  5412
     }
 159  
 
 160  
     public void addChangeListener(ChangeListener listener) {
 161  
         // @@@ Would be nice to get rid of raw change listeners, and stick with the type-specific versions (e.g.,
 162  
         // ObjectChangeListener), but for now Bijection relies on access to the raw listener because it needs to
 163  
         // be able to unregister itself when the weak references are cleared
 164  122
         if (listeners == null)
 165  117
             listeners = new ArrayList<ChangeListener>();
 166  122
         listeners.add(listener);
 167  122
     }
 168  
 
 169  
     public void addWeakListener(ChangeListener listener) {
 170  2
         addChangeListener(new WeakListener(listener));
 171  2
     }
 172  
 
 173  
     public void addDependencies(Location... dependencies) {
 174  5544
         if (dependencies.length > 0) {
 175  2499
             WeakReference<Location> wr = new WeakReference<Location>(this);
 176  7067
             for (Location dep : dependencies)
 177  4568
                 dep.addDependentLocation(wr);
 178  
         }
 179  5544
     }
 180  
 
 181  
     public void addDynamicDependency(Location location) {
 182  1100
         if (weakMe == null)
 183  1097
             weakMe = new WeakReference<Location>(this);
 184  1100
         location.addDependentLocation(weakMe);
 185  1100
     }
 186  
 
 187  
     public void clearDynamicDependencies() {
 188  1098
         if (weakMe != null) {
 189  588
             weakMe.clear();
 190  588
             weakMe = null;
 191  
         }
 192  1098
     }
 193  
 
 194  
     public <T extends Location> T addDynamicDependent(T dep) {
 195  0
         addDynamicDependency(dep);
 196  0
         return dep;
 197  
     }
 198  
 
 199  
     public <T extends Location> T addStaticDependent(T dep) {
 200  0
         addDependencies(dep);
 201  0
         return dep;
 202  
     }
 203  
 
 204  
     public Collection<ChangeListener> getListeners() {
 205  80
         if (listeners == null)
 206  16
             return Collections.emptySet();
 207  
         else
 208  64
             return Collections.unmodifiableCollection(listeners);
 209  
     }
 210  
 
 211  
     public void update() {
 212  0
     }
 213  
 
 214  
     // For testing -- returns count of listeners plus dependent locations -- the "number of things depending on us"
 215  
     int getListenerCount() {
 216  54
         purgeDeadDependencies();
 217  54
         return (listeners == null ? 0 : listeners.size())
 218  
                 + (dependentLocations == null ? 0 : dependentLocations.size());
 219  
     }
 220  
 }
 221  
 
 222  
 class WeakListener extends WeakReference<ChangeListener> implements ChangeListener {
 223  
     public WeakListener(ChangeListener referent) {
 224  2
         super(referent);
 225  2
     }
 226  
 
 227  
     public boolean onChange() {
 228  6
         ChangeListener listener = get();
 229  6
         return listener == null ? false : listener.onChange();
 230  
     }
 231  
 }
 232