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