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