Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.beans.binding; 027 028import com.sun.javafx.binding.BindingHelperObserver; 029import com.sun.javafx.binding.MapExpressionHelper; 030import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection; 031import javafx.beans.InvalidationListener; 032import javafx.beans.Observable; 033import javafx.beans.property.ReadOnlyBooleanProperty; 034import javafx.beans.property.ReadOnlyBooleanPropertyBase; 035import javafx.beans.property.ReadOnlyIntegerProperty; 036import javafx.beans.property.ReadOnlyIntegerPropertyBase; 037import javafx.beans.value.ChangeListener; 038import javafx.collections.FXCollections; 039import javafx.collections.MapChangeListener; 040import javafx.collections.ObservableList; 041import javafx.collections.ObservableMap; 042 043/** 044 * Base class that provides most of the functionality needed to implement a 045 * {@link Binding} of an {@link javafx.collections.ObservableMap}. 046 * <p> 047 * {@code MapBinding} provides a simple invalidation-scheme. An extending 048 * class can register dependencies by calling {@link #bind(Observable...)}. 049 * If one of the registered dependencies becomes invalid, this 050 * {@code MapBinding} is marked as invalid. With 051 * {@link #unbind(Observable...)} listening to dependencies can be stopped. 052 * <p> 053 * To provide a concrete implementation of this class, the method 054 * {@link #computeValue()} has to be implemented to calculate the value of this 055 * binding based on the current state of the dependencies. It is called when 056 * {@link #get()} is called for an invalid binding. 057 * <p> 058 * See {@link DoubleBinding} for an example how this base class can be extended. 059 * 060 * @see Binding 061 * @see MapExpression 062 * 063 * @param <K> 064 * the type of the key elements 065 * @param <V> 066 * the type of the value elements 067 */ 068public abstract class MapBinding<K, V> extends MapExpression<K, V> implements Binding<ObservableMap<K, V>> { 069 070 private final MapChangeListener<K, V> mapChangeListener = new MapChangeListener<K, V>() { 071 @Override 072 public void onChanged(Change<? extends K, ? extends V> change) { 073 invalidateProperties(); 074 onInvalidating(); 075 MapExpressionHelper.fireValueChangedEvent(helper, change); 076 } 077 }; 078 079 private ObservableMap<K, V> value; 080 private boolean valid = false; 081 private BindingHelperObserver observer; 082 private MapExpressionHelper<K, V> helper = null; 083 084 private SizeProperty size0; 085 private EmptyProperty empty0; 086 087 @Override 088 public ReadOnlyIntegerProperty sizeProperty() { 089 if (size0 == null) { 090 size0 = new SizeProperty(); 091 } 092 return size0; 093 } 094 095 private class SizeProperty extends ReadOnlyIntegerPropertyBase { 096 @Override 097 public int get() { 098 return size(); 099 } 100 101 @Override 102 public Object getBean() { 103 return MapBinding.this; 104 } 105 106 @Override 107 public String getName() { 108 return "size"; 109 } 110 111 protected void fireValueChangedEvent() { 112 super.fireValueChangedEvent(); 113 } 114 } 115 116 @Override 117 public ReadOnlyBooleanProperty emptyProperty() { 118 if (empty0 == null) { 119 empty0 = new EmptyProperty(); 120 } 121 return empty0; 122 } 123 124 private class EmptyProperty extends ReadOnlyBooleanPropertyBase { 125 126 @Override 127 public boolean get() { 128 return isEmpty(); 129 } 130 131 @Override 132 public Object getBean() { 133 return MapBinding.this; 134 } 135 136 @Override 137 public String getName() { 138 return "empty"; 139 } 140 141 protected void fireValueChangedEvent() { 142 super.fireValueChangedEvent(); 143 } 144 } 145 146 @Override 147 public void addListener(InvalidationListener listener) { 148 helper = MapExpressionHelper.addListener(helper, this, listener); 149 } 150 151 @Override 152 public void removeListener(InvalidationListener listener) { 153 helper = MapExpressionHelper.removeListener(helper, listener); 154 } 155 156 @Override 157 public void addListener(ChangeListener<? super ObservableMap<K, V>> listener) { 158 helper = MapExpressionHelper.addListener(helper, this, listener); 159 } 160 161 @Override 162 public void removeListener(ChangeListener<? super ObservableMap<K, V>> listener) { 163 helper = MapExpressionHelper.removeListener(helper, listener); 164 } 165 166 @Override 167 public void addListener(MapChangeListener<? super K, ? super V> listener) { 168 helper = MapExpressionHelper.addListener(helper, this, listener); 169 } 170 171 @Override 172 public void removeListener(MapChangeListener<? super K, ? super V> listener) { 173 helper = MapExpressionHelper.removeListener(helper, listener); 174 } 175 176 /** 177 * Start observing the dependencies for changes. If the value of one of the 178 * dependencies changes, the binding is marked as invalid. 179 * 180 * @param dependencies 181 * the dependencies to observe 182 */ 183 protected final void bind(Observable... dependencies) { 184 if ((dependencies != null) && (dependencies.length > 0)) { 185 if (observer == null) { 186 observer = new BindingHelperObserver(this); 187 } 188 for (final Observable dep : dependencies) { 189 if (dep != null) { 190 dep.addListener(observer); 191 } 192 } 193 } 194 } 195 196 /** 197 * Stop observing the dependencies for changes. 198 * 199 * @param dependencies 200 * the dependencies to stop observing 201 */ 202 protected final void unbind(Observable... dependencies) { 203 if (observer != null) { 204 for (final Observable dep : dependencies) { 205 if (dep != null) { 206 dep.removeListener(observer); 207 } 208 } 209 observer = null; 210 } 211 } 212 213 /** 214 * A default implementation of {@code dispose()} that is empty. 215 */ 216 @Override 217 public void dispose() { 218 } 219 220 /** 221 * A default implementation of {@code getDependencies()} that returns an 222 * empty {@link javafx.collections.ObservableList}. 223 * 224 * @return an empty {@code ObservableList} 225 */ 226 @Override 227 @ReturnsUnmodifiableCollection 228 public ObservableList<?> getDependencies() { 229 return FXCollections.emptyObservableList(); 230 } 231 232 /** 233 * Returns the result of {@link #computeValue()}. The method 234 * {@code computeValue()} is only called if the binding is invalid. The 235 * result is cached and returned if the binding did not become invalid since 236 * the last call of {@code get()}. 237 * 238 * @return the current value 239 */ 240 @Override 241 public final ObservableMap<K, V> get() { 242 if (!valid) { 243 value = computeValue(); 244 valid = true; 245 if (value != null) { 246 value.addListener(mapChangeListener); 247 } 248 } 249 return value; 250 } 251 252 /** 253 * The method onInvalidating() can be overridden by extending classes to 254 * react, if this binding becomes invalid. The default implementation is 255 * empty. 256 */ 257 protected void onInvalidating() { 258 } 259 260 private void invalidateProperties() { 261 if (size0 != null) { 262 size0.fireValueChangedEvent(); 263 } 264 if (empty0 != null) { 265 empty0.fireValueChangedEvent(); 266 } 267 } 268 269 @Override 270 public final void invalidate() { 271 if (valid) { 272 if (value != null) { 273 value.removeListener(mapChangeListener); 274 } 275 valid = false; 276 invalidateProperties(); 277 onInvalidating(); 278 MapExpressionHelper.fireValueChangedEvent(helper); 279 } 280 } 281 282 @Override 283 public final boolean isValid() { 284 return valid; 285 } 286 287 /** 288 * Calculates the current value of this binding. 289 * <p> 290 * Classes extending {@code MapBinding} have to provide an implementation 291 * of {@code computeValue}. 292 * 293 * @return the current value 294 */ 295 protected abstract ObservableMap<K, V> computeValue(); 296 297 /** 298 * Returns a string representation of this {@code MapBinding} object. 299 * @return a string representation of this {@code MapBinding} object. 300 */ 301 @Override 302 public String toString() { 303 return valid ? "MapBinding [value: " + get() + "]" 304 : "MapBinding [invalid]"; 305 } 306 307}