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.StringFormatter;
029import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection;
030import javafx.beans.InvalidationListener;
031import javafx.beans.property.ReadOnlyBooleanProperty;
032import javafx.beans.property.ReadOnlyIntegerProperty;
033import javafx.beans.value.ObservableSetValue;
034import javafx.collections.FXCollections;
035import javafx.collections.ObservableList;
036import javafx.collections.ObservableSet;
037import javafx.collections.SetChangeListener;
038
039import java.util.AbstractSet;
040import java.util.Collection;
041import java.util.Iterator;
042import java.util.NoSuchElementException;
043
044/**
045 * A {@code SetExpression} is a
046 * {@link javafx.beans.value.ObservableSetValue} plus additional convenience
047 * methods to generate bindings in a fluent style.
048 * <p>
049 * A concrete sub-class of {@code SetExpression} has to implement the method
050 * {@link javafx.beans.value.ObservableSetValue#get()}, which provides the
051 * actual value of this expression.
052 * <p>
053 * If the wrapped list of a {@code SetExpression} is {@code null}, all methods implementing the {@code Set}
054 * interface will behave as if they were applied to an immutable empty set.
055 *
056 * @param <E> the type of the {@code Set} elements
057 */
058public abstract class SetExpression<E> implements ObservableSetValue<E> {
059
060    private static final ObservableSet EMPTY_SET = new EmptyObservableSet();
061
062    private static class EmptyObservableSet<E> extends AbstractSet<E> implements ObservableSet<E> {
063
064        private static final Iterator iterator = new Iterator() {
065            @Override
066            public boolean hasNext() {
067                return false;
068            }
069
070            @Override
071            public Object next() {
072                throw new NoSuchElementException();
073            }
074
075            @Override
076            public void remove() {
077                throw new UnsupportedOperationException();
078
079            }
080        };
081
082        @Override
083        public Iterator<E> iterator() {
084            return iterator;
085        }
086
087        @Override
088        public int size() {
089            return 0;
090        }
091
092        @Override
093        public void addListener(SetChangeListener<? super E> setChangeListener) {
094            // no-op
095        }
096
097        @Override
098        public void removeListener(SetChangeListener<? super E> setChangeListener) {
099            // no-op
100        }
101
102        @Override
103        public void addListener(InvalidationListener listener) {
104            // no-op
105        }
106
107        @Override
108        public void removeListener(InvalidationListener listener) {
109            // no-op
110        }
111    }
112
113    @Override
114    public ObservableSet<E> getValue() {
115        return get();
116    }
117
118    /**
119     * Returns a {@code SetExpression} that wraps a
120     * {@link javafx.beans.value.ObservableSetValue}. If the
121     * {@code ObservableSetValue} is already a {@code SetExpression}, it
122     * will be returned. Otherwise a new
123     * {@link javafx.beans.binding.SetBinding} is created that is bound to
124     * the {@code ObservableSetValue}.
125     *
126     * @param value
127     *            The source {@code ObservableSetValue}
128     * @return A {@code SetExpression} that wraps the
129     *         {@code ObservableSetValue} if necessary
130     * @throws NullPointerException
131     *             if {@code value} is {@code null}
132     */
133    public static <E> SetExpression<E> setExpression(final ObservableSetValue<E> value) {
134        if (value == null) {
135            throw new NullPointerException("Set must be specified.");
136        }
137        return value instanceof SetExpression ? (SetExpression<E>) value
138                : new SetBinding<E>() {
139            {
140                super.bind(value);
141            }
142
143            @Override
144            public void dispose() {
145                super.unbind(value);
146            }
147
148            @Override
149            protected ObservableSet<E> computeValue() {
150                return value.get();
151            }
152
153            @Override
154            @ReturnsUnmodifiableCollection
155            public ObservableList<?> getDependencies() {
156                return FXCollections.singletonObservableList(value);
157            }
158        };
159    }
160
161    /**
162     * The size of the set
163     */
164    public int getSize() {
165        return size();
166    }
167
168    /**
169     * A boolean property that is {@code true}, if the set is empty.
170     */
171    public abstract ReadOnlyIntegerProperty sizeProperty();
172
173    public abstract ReadOnlyBooleanProperty emptyProperty();
174
175    /**
176     * Creates a new {@link BooleanBinding} that holds {@code true} if this set is equal to
177     * another {@link javafx.collections.ObservableSet}.
178     *
179     * @param other
180     *            the other {@code ObservableSet}
181     * @return the new {@code BooleanBinding}
182     * @throws NullPointerException
183     *             if {@code other} is {@code null}
184     */
185    public BooleanBinding isEqualTo(final ObservableSet<?> other) {
186        return Bindings.equal(this, other);
187    }
188
189    /**
190     * Creates a new {@link BooleanBinding} that holds {@code true} if this set is not equal to
191     * another {@link javafx.collections.ObservableSet}.
192     *
193     * @param other
194     *            the other {@code ObservableSet}
195     * @return the new {@code BooleanBinding}
196     * @throws NullPointerException
197     *             if {@code other} is {@code null}
198     */
199    public BooleanBinding isNotEqualTo(final ObservableSet<?> other) {
200        return Bindings.notEqual(this, other);
201    }
202
203    /**
204     * Creates a new {@link BooleanBinding} that holds {@code true} if the wrapped set is {@code null}.
205     *
206     * @return the new {@code BooleanBinding}
207     */
208    public BooleanBinding isNull() {
209        return Bindings.isNull(this);
210    }
211
212    /**
213     * Creates a new {@link BooleanBinding} that holds {@code true} if the wrapped set is not {@code null}.
214     *
215     * @return the new {@code BooleanBinding}
216     */
217    public BooleanBinding isNotNull() {
218        return Bindings.isNotNull(this);
219    }
220
221    /**
222     * Creates a {@link javafx.beans.binding.StringBinding} that holds the value
223     * of the {@code SetExpression} turned into a {@code String}. If the
224     * value of this {@code SetExpression} changes, the value of the
225     * {@code StringBinding} will be updated automatically.
226     *
227     * @return the new {@code StringBinding}
228     */
229    public StringBinding asString() {
230        return (StringBinding) StringFormatter.convert(this);
231    }
232
233    @Override
234    public int size() {
235        final ObservableSet<E> set = get();
236        return (set == null)? EMPTY_SET.size() : set.size();
237    }
238
239    @Override
240    public boolean isEmpty() {
241        final ObservableSet<E> set = get();
242        return (set == null)? EMPTY_SET.isEmpty() : set.isEmpty();
243    }
244
245    @Override
246    public boolean contains(Object obj) {
247        final ObservableSet<E> set = get();
248        return (set == null)? EMPTY_SET.contains(obj) : set.contains(obj);
249    }
250
251    @Override
252    public Iterator<E> iterator() {
253        final ObservableSet<E> set = get();
254        return (set == null)? EMPTY_SET.iterator() : set.iterator();
255    }
256
257    @Override
258    public Object[] toArray() {
259        final ObservableSet<E> set = get();
260        return (set == null)? EMPTY_SET.toArray() : set.toArray();
261    }
262
263    @Override
264    public <T> T[] toArray(T[] array) {
265        final ObservableSet<E> set = get();
266        return (set == null)? (T[]) EMPTY_SET.toArray(array) : set.toArray(array);
267     }
268
269    @Override
270    public boolean add(E element) {
271        final ObservableSet<E> set = get();
272        return (set == null)? EMPTY_SET.add(element) : set.add(element);
273    }
274
275    @Override
276    public boolean remove(Object obj) {
277        final ObservableSet<E> set = get();
278        return (set == null)? EMPTY_SET.remove(obj) : set.remove(obj);
279    }
280
281    @Override
282    public boolean containsAll(Collection<?> objects) {
283        final ObservableSet<E> set = get();
284        return (set == null)? EMPTY_SET.contains(objects) : set.containsAll(objects);
285    }
286
287    @Override
288    public boolean addAll(Collection<? extends E> elements) {
289        final ObservableSet<E> set = get();
290        return (set == null)? EMPTY_SET.addAll(elements) : set.addAll(elements);
291    }
292
293    @Override
294    public boolean removeAll(Collection<?> objects) {
295        final ObservableSet<E> set = get();
296        return (set == null)? EMPTY_SET.removeAll(objects) : set.removeAll(objects);
297    }
298
299    @Override
300    public boolean retainAll(Collection<?> objects) {
301        final ObservableSet<E> set = get();
302        return (set == null)? EMPTY_SET.retainAll(objects) : set.retainAll(objects);
303    }
304
305    @Override
306    public void clear() {
307        final ObservableSet<E> set = get();
308        if (set == null) {
309            EMPTY_SET.clear();
310        } else {
311            set.clear();
312        }
313    }
314
315}