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.property;
027
028import com.sun.javafx.binding.SetExpressionHelper;
029import javafx.beans.InvalidationListener;
030import javafx.beans.Observable;
031import javafx.beans.value.ChangeListener;
032import javafx.beans.value.ObservableValue;
033import javafx.collections.ObservableSet;
034import javafx.collections.SetChangeListener;
035
036/**
037 * The class {@code SetPropertyBase} is the base class for a property
038 * wrapping an {@link javafx.collections.ObservableSet}.
039 *
040 * It provides all the functionality required for a property except for the
041 * {@link #getBean()} and {@link #getName()} methods, which must be implemented
042 * by extending classes.
043 *
044 * @see javafx.collections.ObservableSet
045 * @see SetProperty
046 *
047 * @param <E> the type of the {@code Set} elements
048 */
049public abstract class SetPropertyBase<E> extends SetProperty<E> {
050
051    private final SetChangeListener<E> setChangeListener = new SetChangeListener<E>() {
052        @Override
053        public void onChanged(Change<? extends E> change) {
054            invalidateProperties();
055            invalidated();
056            fireValueChangedEvent(change);
057        }
058    };
059
060    private ObservableSet<E> value;
061    private ObservableValue<? extends ObservableSet<E>> observable = null;
062    private InvalidationListener listener = null;
063    private boolean valid = true;
064    private SetExpressionHelper<E> helper = null;
065
066    private SizeProperty size0;
067    private EmptyProperty empty0;
068
069    /**
070     * The Constructor of {@code SetPropertyBase}
071     */
072    public SetPropertyBase() {}
073
074    /**
075     * The constructor of the {@code SetPropertyBase}.
076     *
077     * @param initialValue
078     *            the initial value of the wrapped value
079     */
080    public SetPropertyBase(ObservableSet<E> initialValue) {
081        this.value = initialValue;
082        if (initialValue != null) {
083            initialValue.addListener(setChangeListener);
084        }
085    }
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 SetPropertyBase.this;
104        }
105
106        @Override
107        public String getName() {
108            return "size";
109        }
110
111        @Override
112        protected void fireValueChangedEvent() {
113            super.fireValueChangedEvent();
114        }
115    }
116
117    @Override
118    public ReadOnlyBooleanProperty emptyProperty() {
119        if (empty0 == null) {
120            empty0 = new EmptyProperty();
121        }
122        return empty0;
123    }
124
125    private class EmptyProperty extends ReadOnlyBooleanPropertyBase {
126
127        @Override
128        public boolean get() {
129            return isEmpty();
130        }
131
132        @Override
133        public Object getBean() {
134            return SetPropertyBase.this;
135        }
136
137        @Override
138        public String getName() {
139            return "empty";
140        }
141
142        @Override
143        protected void fireValueChangedEvent() {
144            super.fireValueChangedEvent();
145        }
146    }
147
148    @Override
149    public void addListener(InvalidationListener listener) {
150        helper = SetExpressionHelper.addListener(helper, this, listener);
151    }
152
153    @Override
154    public void removeListener(InvalidationListener listener) {
155        helper = SetExpressionHelper.removeListener(helper, listener);
156    }
157
158    @Override
159    public void addListener(ChangeListener<? super ObservableSet<E>> listener) {
160        helper = SetExpressionHelper.addListener(helper, this, listener);
161    }
162
163    @Override
164    public void removeListener(ChangeListener<? super ObservableSet<E>> listener) {
165        helper = SetExpressionHelper.removeListener(helper, listener);
166    }
167
168    @Override
169    public void addListener(SetChangeListener<? super E> listener) {
170        helper = SetExpressionHelper.addListener(helper, this, listener);
171    }
172
173    @Override
174    public void removeListener(SetChangeListener<? super E> listener) {
175        helper = SetExpressionHelper.removeListener(helper, listener);
176    }
177
178    /**
179     * Sends notifications to all attached
180     * {@link javafx.beans.InvalidationListener InvalidationListeners},
181     * {@link javafx.beans.value.ChangeListener ChangeListeners}, and
182     * {@link javafx.collections.SetChangeListener}.
183     *
184     * This method is called when the value is changed, either manually by
185     * calling {@link #set(javafx.collections.ObservableSet)} or in case of a bound property, if the
186     * binding becomes invalid.
187     */
188    protected void fireValueChangedEvent() {
189        SetExpressionHelper.fireValueChangedEvent(helper);
190    }
191
192    /**
193     * Sends notifications to all attached
194     * {@link javafx.beans.InvalidationListener InvalidationListeners},
195     * {@link javafx.beans.value.ChangeListener ChangeListeners}, and
196     * {@link javafx.collections.SetChangeListener}.
197     *
198     * This method is called when the content of the list changes.
199     *
200     * @param change the change that needs to be propagated
201     */
202    protected void fireValueChangedEvent(SetChangeListener.Change<? extends E> change) {
203        SetExpressionHelper.fireValueChangedEvent(helper, change);
204    }
205
206    private void invalidateProperties() {
207        if (size0 != null) {
208            size0.fireValueChangedEvent();
209        }
210        if (empty0 != null) {
211            empty0.fireValueChangedEvent();
212        }
213    }
214
215    private void markInvalid(ObservableSet<E> oldValue) {
216        if (valid) {
217            if (oldValue != null) {
218                oldValue.removeListener(setChangeListener);
219            }
220            valid = false;
221            invalidateProperties();
222            invalidated();
223            fireValueChangedEvent();
224        }
225    }
226
227
228
229    /**
230     * The method {@code invalidated()} can be overridden to receive
231     * invalidation notifications. This is the preferred option in
232     * {@code Objects} defining the property, because it requires less memory.
233     *
234     * The default implementation is empty.
235     */
236    protected void invalidated() {
237    }
238
239    @Override
240    public ObservableSet<E> get() {
241        if (!valid) {
242            value = observable == null ? value : observable.getValue();
243            valid = true;
244            if (value != null) {
245                value.addListener(setChangeListener);
246            }
247        }
248        return value;
249    }
250
251    @Override
252    public void set(ObservableSet<E> newValue) {
253        if (isBound()) {
254            throw new RuntimeException("A bound value cannot be set.");
255        }
256        if (value != newValue) {
257            final ObservableSet<E> oldValue = value;
258            value = newValue;
259            markInvalid(oldValue);
260        }
261    }
262
263    @Override
264    public boolean isBound() {
265        return observable != null;
266    }
267
268    @Override
269    public void bind(final ObservableValue<? extends ObservableSet<E>> newObservable) {
270        if (newObservable == null) {
271            throw new NullPointerException("Cannot bind to null");
272        }
273        
274        if (!newObservable.equals(this.observable)) {
275            unbind();
276            observable = newObservable;
277            if (listener == null) {
278                listener = new Listener();
279            }
280            observable.addListener(listener);
281            markInvalid(value);
282        }
283    }
284
285    @Override
286    public void unbind() {
287        if (observable != null) {
288            value = observable.getValue();
289            observable.removeListener(listener);
290            observable = null;
291        }
292    }
293
294    /**
295     * Returns a string representation of this {@code SetPropertyBase} object.
296     * @return a string representation of this {@code SetPropertyBase} object.
297     */
298    @Override
299    public String toString() {
300        final Object bean = getBean();
301        final String name = getName();
302        final StringBuilder result = new StringBuilder("SetProperty [");
303        if (bean != null) {
304            result.append("bean: ").append(bean).append(", ");
305        }
306        if ((name != null) && (!name.equals(""))) {
307            result.append("name: ").append(name).append(", ");
308        }
309        if (isBound()) {
310            result.append("bound, ");
311            if (valid) {
312                result.append("value: ").append(get());
313            } else {
314                result.append("invalid");
315            }
316        } else {
317            result.append("value: ").append(get());
318        }
319        result.append("]");
320        return result.toString();
321    }
322
323    private class Listener implements InvalidationListener {
324        @Override
325        public void invalidated(Observable valueModel) {
326            markInvalid(value);
327        }
328    }
329
330}