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.SetExpressionHelper; 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.ObservableList; 040import javafx.collections.ObservableSet; 041import javafx.collections.SetChangeListener; 042 043/** 044 * Base class that provides most of the functionality needed to implement a 045 * {@link Binding} of an {@link javafx.collections.ObservableSet}. 046 * <p> 047 * {@code SetBinding} 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 SetBinding} 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 SetExpression 062 * 063 * @param <E> 064 * the type of the {@code Set} elements 065 */ 066public abstract class SetBinding<E> extends SetExpression<E> implements Binding<ObservableSet<E>> { 067 068 private final SetChangeListener<E> setChangeListener = new SetChangeListener<E>() { 069 @Override 070 public void onChanged(Change<? extends E> change) { 071 invalidateProperties(); 072 onInvalidating(); 073 SetExpressionHelper.fireValueChangedEvent(helper, change); 074 } 075 }; 076 077 private ObservableSet<E> value; 078 private boolean valid = false; 079 private BindingHelperObserver observer; 080 private SetExpressionHelper<E> helper = null; 081 082 private SizeProperty size0; 083 private EmptyProperty empty0; 084 085 @Override 086 public ReadOnlyIntegerProperty sizeProperty() { 087 if (size0 == null) { 088 size0 = new SizeProperty(); 089 } 090 return size0; 091 } 092 093 private class SizeProperty extends ReadOnlyIntegerPropertyBase { 094 @Override 095 public int get() { 096 return size(); 097 } 098 099 @Override 100 public Object getBean() { 101 return SetBinding.this; 102 } 103 104 @Override 105 public String getName() { 106 return "size"; 107 } 108 109 protected void fireValueChangedEvent() { 110 super.fireValueChangedEvent(); 111 } 112 } 113 114 @Override 115 public ReadOnlyBooleanProperty emptyProperty() { 116 if (empty0 == null) { 117 empty0 = new EmptyProperty(); 118 } 119 return empty0; 120 } 121 122 private class EmptyProperty extends ReadOnlyBooleanPropertyBase { 123 124 @Override 125 public boolean get() { 126 return isEmpty(); 127 } 128 129 @Override 130 public Object getBean() { 131 return SetBinding.this; 132 } 133 134 @Override 135 public String getName() { 136 return "empty"; 137 } 138 139 protected void fireValueChangedEvent() { 140 super.fireValueChangedEvent(); 141 } 142 } 143 144 @Override 145 public void addListener(InvalidationListener listener) { 146 helper = SetExpressionHelper.addListener(helper, this, listener); 147 } 148 149 @Override 150 public void removeListener(InvalidationListener listener) { 151 helper = SetExpressionHelper.removeListener(helper, listener); 152 } 153 154 @Override 155 public void addListener(ChangeListener<? super ObservableSet<E>> listener) { 156 helper = SetExpressionHelper.addListener(helper, this, listener); 157 } 158 159 @Override 160 public void removeListener(ChangeListener<? super ObservableSet<E>> listener) { 161 helper = SetExpressionHelper.removeListener(helper, listener); 162 } 163 164 @Override 165 public void addListener(SetChangeListener<? super E> listener) { 166 helper = SetExpressionHelper.addListener(helper, this, listener); 167 } 168 169 @Override 170 public void removeListener(SetChangeListener<? super E> listener) { 171 helper = SetExpressionHelper.removeListener(helper, listener); 172 } 173 174 /** 175 * Start observing the dependencies for changes. If the value of one of the 176 * dependencies changes, the binding is marked as invalid. 177 * 178 * @param dependencies 179 * the dependencies to observe 180 */ 181 protected final void bind(Observable... dependencies) { 182 if ((dependencies != null) && (dependencies.length > 0)) { 183 if (observer == null) { 184 observer = new BindingHelperObserver(this); 185 } 186 for (final Observable dep : dependencies) { 187 if (dep != null) { 188 dep.addListener(observer); 189 } 190 } 191 } 192 } 193 194 /** 195 * Stop observing the dependencies for changes. 196 * 197 * @param dependencies 198 * the dependencies to stop observing 199 */ 200 protected final void unbind(Observable... dependencies) { 201 if (observer != null) { 202 for (final Observable dep : dependencies) { 203 if (dep != null) { 204 dep.removeListener(observer); 205 } 206 } 207 observer = null; 208 } 209 } 210 211 /** 212 * A default implementation of {@code dispose()} that is empty. 213 */ 214 @Override 215 public void dispose() { 216 } 217 218 /** 219 * A default implementation of {@code getDependencies()} that returns an 220 * empty {@link javafx.collections.ObservableList}. 221 * 222 * @return an empty {@code ObservableList} 223 */ 224 @Override 225 @ReturnsUnmodifiableCollection 226 public ObservableList<?> getDependencies() { 227 return FXCollections.emptyObservableList(); 228 } 229 230 /** 231 * Returns the result of {@link #computeValue()}. The method 232 * {@code computeValue()} is only called if the binding is invalid. The 233 * result is cached and returned if the binding did not become invalid since 234 * the last call of {@code get()}. 235 * 236 * @return the current value 237 */ 238 @Override 239 public final ObservableSet<E> get() { 240 if (!valid) { 241 value = computeValue(); 242 valid = true; 243 if (value != null) { 244 value.addListener(setChangeListener); 245 } 246 } 247 return value; 248 } 249 250 /** 251 * The method onInvalidating() can be overridden by extending classes to 252 * react, if this binding becomes invalid. The default implementation is 253 * empty. 254 */ 255 protected void onInvalidating() { 256 } 257 258 private void invalidateProperties() { 259 if (size0 != null) { 260 size0.fireValueChangedEvent(); 261 } 262 if (empty0 != null) { 263 empty0.fireValueChangedEvent(); 264 } 265 } 266 267 @Override 268 public final void invalidate() { 269 if (valid) { 270 if (value != null) { 271 value.removeListener(setChangeListener); 272 } 273 valid = false; 274 invalidateProperties(); 275 onInvalidating(); 276 SetExpressionHelper.fireValueChangedEvent(helper); 277 } 278 } 279 280 @Override 281 public final boolean isValid() { 282 return valid; 283 } 284 285 /** 286 * Calculates the current value of this binding. 287 * <p> 288 * Classes extending {@code SetBinding} have to provide an implementation 289 * of {@code computeValue}. 290 * 291 * @return the current value 292 */ 293 protected abstract ObservableSet<E> computeValue(); 294 295 /** 296 * Returns a string representation of this {@code SetBinding} object. 297 * @return a string representation of this {@code SetBinding} object. 298 */ 299 @Override 300 public String toString() { 301 return valid ? "SetBinding [value: " + get() + "]" 302 : "SetBinding [invalid]"; 303 } 304 305}