/* * Copyright (c) 2011, 2014, 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 javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.binding.IntegerBinding; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import com.sun.javafx.binding.ExpressionHelper; import java.lang.ref.WeakReference; import javafx.beans.WeakListener; import javafx.beans.value.ObservableIntegerValue; import javafx.beans.value.ObservableNumberValue; /** * The class {@code IntegerPropertyBase} is the base class for a property * wrapping a {@code int} value. * * 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 IntegerProperty * * * @since JavaFX 2.0 */ public abstract class IntegerPropertyBase extends IntegerProperty { private int value; private ObservableIntegerValue observable = null;; private InvalidationListener listener = null; private boolean valid = true; private ExpressionHelper helper = null; /** * The constructor of the {@code IntegerPropertyBase}. */ public IntegerPropertyBase() { } /** * The constructor of the {@code IntegerPropertyBase}. * * @param initialValue * the initial value of the wrapped value */ public IntegerPropertyBase(int initialValue) { this.value = initialValue; } @Override public void addListener(InvalidationListener listener) { helper = ExpressionHelper.addListener(helper, this, listener); } @Override public void removeListener(InvalidationListener listener) { helper = ExpressionHelper.removeListener(helper, listener); } @Override public void addListener(ChangeListener listener) { helper = ExpressionHelper.addListener(helper, this, listener); } @Override public void removeListener(ChangeListener listener) { helper = ExpressionHelper.removeListener(helper, listener); } /** * Sends notifications to all attached * {@link javafx.beans.InvalidationListener InvalidationListeners} and * {@link javafx.beans.value.ChangeListener ChangeListeners}. * * This method is called when the value is changed, either manually by * calling {@link #set(int)} or in case of a bound property, if the * binding becomes invalid. */ protected void fireValueChangedEvent() { ExpressionHelper.fireValueChangedEvent(helper); } private void markInvalid() { if (valid) { valid = false; invalidated(); fireValueChangedEvent(); } } /** * 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() { } /** * {@inheritDoc} */ @Override public int get() { valid = true; return observable == null ? value : observable.get(); } /** * {@inheritDoc} */ @Override public void set(int 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) { value = newValue; markInvalid(); } } /** * {@inheritDoc} */ @Override public boolean isBound() { return observable != null; } /** * {@inheritDoc} */ @Override public void bind(final ObservableValue rawObservable) { if (rawObservable == null) { throw new NullPointerException("Cannot bind to null"); } ObservableIntegerValue newObservable; if (rawObservable instanceof ObservableIntegerValue) { newObservable = (ObservableIntegerValue)rawObservable; } else if (rawObservable instanceof ObservableNumberValue) { final ObservableNumberValue numberValue = (ObservableNumberValue)rawObservable; newObservable = new ValueWrapper(rawObservable) { @Override protected int computeValue() { return numberValue.intValue(); } }; } else { newObservable = new ValueWrapper(rawObservable) { @Override protected int computeValue() { final Number value = rawObservable.getValue(); return (value == null)? 0 : value.intValue(); } }; } if (!newObservable.equals(observable)) { unbind(); observable = newObservable; if (listener == null) { listener = new Listener(this); } observable.addListener(listener); markInvalid(); } } /** * {@inheritDoc} */ @Override public void unbind() { if (observable != null) { value = observable.get(); observable.removeListener(listener); if (observable instanceof ValueWrapper) { ((ValueWrapper)observable).dispose(); } observable = null; } } /** * Returns a string representation of this {@code IntegerPropertyBase} object. * @return a string representation of this {@code IntegerPropertyBase} object. */ @Override public String toString() { final Object bean = getBean(); final String name = getName(); final StringBuilder result = new StringBuilder("IntegerProperty ["); 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 implements InvalidationListener, WeakListener { private final WeakReference wref; public Listener(IntegerPropertyBase ref) { this.wref = new WeakReference<>(ref); } @Override public void invalidated(Observable observable) { IntegerPropertyBase ref = wref.get(); if (ref == null) { observable.removeListener(this); } else { ref.markInvalid(); } } @Override public boolean wasGarbageCollected() { return wref.get() == null; } } private abstract class ValueWrapper extends IntegerBinding { private ObservableValue observable; public ValueWrapper(ObservableValue observable) { this.observable = observable; bind(observable); } @Override public void dispose() { unbind(observable); } } }