/*
 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.beans.property;

import com.sun.javafx.binding.MapExpressionHelper;
import java.lang.ref.WeakReference;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.WeakListener;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.*;

The class MapPropertyBase is the base class for a property wrapping an ObservableMap. It provides all the functionality required for a property except for the ReadOnlyProperty.getBean() and ReadOnlyProperty.getName() methods, which must be implemented by extending classes.
Type parameters:
  • <K> – the type of the key elements of the Map
  • <V> – the type of the value elements of the Map
See Also:
Since:JavaFX 2.1
/** * The class {@code MapPropertyBase} is the base class for a property * wrapping an {@link javafx.collections.ObservableMap}. * * It provides all the functionality required for a property except for the * {@link #getBean()} and {@link #getName()} methods, which must be implemented * by extending classes. * * @see javafx.collections.ObservableMap * @see MapProperty * * @param <K> the type of the key elements of the {@code Map} * @param <V> the type of the value elements of the {@code Map} * @since JavaFX 2.1 */
public abstract class MapPropertyBase<K, V> extends MapProperty<K, V> { private final MapChangeListener<K, V> mapChangeListener = change -> { invalidateProperties(); invalidated(); fireValueChangedEvent(change); }; private ObservableMap<K, V> value; private ObservableValue<? extends ObservableMap<K, V>> observable = null; private InvalidationListener listener = null; private boolean valid = true; private MapExpressionHelper<K, V> helper = null; private SizeProperty size0; private EmptyProperty empty0;
The Constructor of MapPropertyBase
/** * The Constructor of {@code MapPropertyBase} */
public MapPropertyBase() {}
The constructor of the MapPropertyBase.
Params:
  • initialValue – the initial value of the wrapped value
/** * The constructor of the {@code MapPropertyBase}. * * @param initialValue * the initial value of the wrapped value */
public MapPropertyBase(ObservableMap<K, V> initialValue) { this.value = initialValue; if (initialValue != null) { initialValue.addListener(mapChangeListener); } } @Override public ReadOnlyIntegerProperty sizeProperty() { if (size0 == null) { size0 = new SizeProperty(); } return size0; } private class SizeProperty extends ReadOnlyIntegerPropertyBase { @Override public int get() { return size(); } @Override public Object getBean() { return MapPropertyBase.this; } @Override public String getName() { return "size"; } @Override protected void fireValueChangedEvent() { super.fireValueChangedEvent(); } } @Override public ReadOnlyBooleanProperty emptyProperty() { if (empty0 == null) { empty0 = new EmptyProperty(); } return empty0; } private class EmptyProperty extends ReadOnlyBooleanPropertyBase { @Override public boolean get() { return isEmpty(); } @Override public Object getBean() { return MapPropertyBase.this; } @Override public String getName() { return "empty"; } @Override protected void fireValueChangedEvent() { super.fireValueChangedEvent(); } } @Override public void addListener(InvalidationListener listener) { helper = MapExpressionHelper.addListener(helper, this, listener); } @Override public void removeListener(InvalidationListener listener) { helper = MapExpressionHelper.removeListener(helper, listener); } @Override public void addListener(ChangeListener<? super ObservableMap<K, V>> listener) { helper = MapExpressionHelper.addListener(helper, this, listener); } @Override public void removeListener(ChangeListener<? super ObservableMap<K, V>> listener) { helper = MapExpressionHelper.removeListener(helper, listener); } @Override public void addListener(MapChangeListener<? super K, ? super V> listener) { helper = MapExpressionHelper.addListener(helper, this, listener); } @Override public void removeListener(MapChangeListener<? super K, ? super V> listener) { helper = MapExpressionHelper.removeListener(helper, listener); }
Sends notifications to all attached InvalidationListeners, ChangeListeners, and MapChangeListener. This method is called when the value is changed, either manually by calling set(ObservableMap) or in case of a bound property, if the binding becomes invalid.
/** * Sends notifications to all attached * {@link javafx.beans.InvalidationListener InvalidationListeners}, * {@link javafx.beans.value.ChangeListener ChangeListeners}, and * {@link javafx.collections.MapChangeListener}. * * This method is called when the value is changed, either manually by * calling {@link #set(javafx.collections.ObservableMap)} or in case of a bound property, if the * binding becomes invalid. */
protected void fireValueChangedEvent() { MapExpressionHelper.fireValueChangedEvent(helper); }
Sends notifications to all attached InvalidationListeners, ChangeListeners, and MapChangeListener. This method is called when the content of the list changes.
Params:
  • change – the change that needs to be propagated
/** * Sends notifications to all attached * {@link javafx.beans.InvalidationListener InvalidationListeners}, * {@link javafx.beans.value.ChangeListener ChangeListeners}, and * {@link javafx.collections.MapChangeListener}. * * This method is called when the content of the list changes. * * @param change the change that needs to be propagated */
protected void fireValueChangedEvent(MapChangeListener.Change<? extends K, ? extends V> change) { MapExpressionHelper.fireValueChangedEvent(helper, change); } private void invalidateProperties() { if (size0 != null) { size0.fireValueChangedEvent(); } if (empty0 != null) { empty0.fireValueChangedEvent(); } } private void markInvalid(ObservableMap<K, V> oldValue) { if (valid) { if (oldValue != null) { oldValue.removeListener(mapChangeListener); } valid = false; invalidateProperties(); invalidated(); fireValueChangedEvent(); } }
The method invalidated() can be overridden to receive invalidation notifications. This is the preferred option in Objects defining the property, because it requires less memory. The default implementation is empty.
/** * The method {@code invalidated()} can be overridden to receive * invalidation notifications. This is the preferred option in * {@code Objects} defining the property, because it requires less memory. * * The default implementation is empty. */
protected void invalidated() { } @Override public ObservableMap<K, V> get() { if (!valid) { value = observable == null ? value : observable.getValue(); valid = true; if (value != null) { value.addListener(mapChangeListener); } } return value; } @Override public void set(ObservableMap<K, V> newValue) { if (isBound()) { throw new java.lang.RuntimeException((getBean() != null && getName() != null ? getBean().getClass().getSimpleName() + "." + getName() + " : ": "") + "A bound value cannot be set."); } if (value != newValue) { final ObservableMap<K, V> oldValue = value; value = newValue; markInvalid(oldValue); } } @Override public boolean isBound() { return observable != null; } @Override public void bind(final ObservableValue<? extends ObservableMap<K, V>> newObservable) { if (newObservable == null) { throw new NullPointerException("Cannot bind to null"); } if (!newObservable.equals(observable)) { unbind(); observable = newObservable; if (listener == null) { listener = new Listener<>(this); } observable.addListener(listener); markInvalid(value); } } @Override public void unbind() { if (observable != null) { value = observable.getValue(); observable.removeListener(listener); observable = null; } }
Returns a string representation of this MapPropertyBase object.
Returns:a string representation of this MapPropertyBase object.
/** * Returns a string representation of this {@code MapPropertyBase} object. * @return a string representation of this {@code MapPropertyBase} object. */
@Override public String toString() { final Object bean = getBean(); final String name = getName(); final StringBuilder result = new StringBuilder("MapProperty ["); if (bean != null) { result.append("bean: ").append(bean).append(", "); } if ((name != null) && (!name.equals(""))) { result.append("name: ").append(name).append(", "); } if (isBound()) { result.append("bound, "); if (valid) { result.append("value: ").append(get()); } else { result.append("invalid"); } } else { result.append("value: ").append(get()); } result.append("]"); return result.toString(); } private static class Listener<K,V> implements InvalidationListener, WeakListener { private final WeakReference<MapPropertyBase<K,V>> wref; public Listener(MapPropertyBase<K,V> ref) { this.wref = new WeakReference<>(ref); } @Override public void invalidated(Observable observable) { MapPropertyBase<K,V> ref = wref.get(); if (ref == null) { observable.removeListener(this); } else { ref.markInvalid(ref.value); } } @Override public boolean wasGarbageCollected() { return wref.get() == null; } } }