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