org.jdesktop.beansbinding.PropertyHelper Maven / Gradle / Ivy
/***********************************************************************************************************************
*
* BetterBeansBinding - keeping JavaBeans in sync
* ==============================================
*
* Copyright (C) 2009 by Tidalwave s.a.s. (http://www.tidalwave.it)
* http://betterbeansbinding.kenai.com
*
* This is derived work from BeansBinding: http://beansbinding.dev.java.net
* BeansBinding is copyrighted (C) by Sun Microsystems, Inc.
*
***********************************************************************************************************************
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
* Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
***********************************************************************************************************************
*
* $Id: PropertyHelper.java 63 2009-06-11 19:48:05Z fabriziogiudici $
*
**********************************************************************************************************************/
package org.jdesktop.beansbinding;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
/**
* An abstract subclass of {@code Property} that helps with the management of
* {@code PropertyStateListeners} by implementing the methods for adding, removing,
* and getting listeners. {@code PropertyHelper} can be constructed
* to manage listeners for multiple source objects, or to ignore the source
* object argument when dealing with listeners and associate them directly with
* the {@code PropertyHelper} instance itself. This makes {@code PropertyHelper}
* useful as a base for both property types described in the documentation for
* {@code Property}.
*
* {@code PropertyHelper} also provides, by way of the protected methods
* {@link #listeningStarted} and {@link #listeningStopped} a hook for subclasses
* to know when it's time to start tracking changes to a particular source object.
*
* @param the type of source object that this {@code Property} operates on
* @param the type of value that this {@code Property} represents
*
* @author Shannon Hickey
*/
public abstract class PropertyHelper extends Property {
private final boolean ignoresSource;
private Object listeners;
/**
* Create a {@code PropertyHelper} that manages listeners for multiple
* source objects.
*/
public PropertyHelper() {
this(false);
}
/**
* Create a {@code PropertyHelper}, specifying whether it manages
* listeners for multiple source objects, or ignores the source object
* argument when dealing with listeners
*
* @param ignoresSource whether or not the source argument is ignored
* when dealing with listeners
*/
public PropertyHelper(boolean ignoresSource) {
this.ignoresSource = ignoresSource;
}
private List getListeners(S source, boolean create) {
if (ignoresSource) {
List list = (List) listeners;
if ((list == null) && create) {
list = new ArrayList();
listeners = list;
}
return list;
}
IdentityHashMap> map = (IdentityHashMap>) listeners;
if (map == null) {
if (create) {
map = new IdentityHashMap>();
listeners = map;
} else {
return null;
}
}
List list = map.get(source);
if ((list == null) && create) {
list = new ArrayList();
map.put(source, list);
}
return list;
}
/**
* {@inheritDoc}
* @throws UnsupportedOperationException {@inheritDoc}
*/
public abstract Class getWriteType(S source);
/**
* {@inheritDoc}
* @throws UnsupportedOperationException {@inheritDoc}
*/
public abstract V getValue(S source);
/**
* {@inheritDoc}
* @throws UnsupportedOperationException {@inheritDoc}
*/
public abstract void setValue(S source, V value);
/**
* {@inheritDoc}
* @throws UnsupportedOperationException {@inheritDoc}
*/
public abstract boolean isReadable(S source);
/**
* {@inheritDoc}
* @throws UnsupportedOperationException {@inheritDoc}
*/
public abstract boolean isWriteable(S source);
/**
* Called when this {@code PropertyHelper} changes from having
* no listeners installed for the given source object to
* having listeners installed for the given source object. This
* is the ideal time for subclasses to install any listeners needed
* to track change on the source object.
*
* @see #listeningStopped
*/
protected void listeningStarted(S source) {
}
/**
* Called when this {@code PropertyHelper} changes from having
* listeners installed for the given source object to
* having no listeners installed for the given source object. This
* is the ideal time for subclasses to remove any listeners that
* they've installed to track changes on the source object.
*
* @see #listeningStopped
*/
protected void listeningStopped(S source) {
}
/**
* {@inheritDoc}
*/
public final void addPropertyStateListener(S source,
PropertyStateListener listener) {
if (listener == null) {
return;
}
List listeners = getListeners(source, true);
boolean wasListening = (listeners.size() != 0);
listeners.add(listener);
if (!wasListening) {
listeningStarted(ignoresSource ? null : source);
}
}
/**
* {@inheritDoc}
*/
public final void removePropertyStateListener(S source,
PropertyStateListener listener) {
if (listener == null) {
return;
}
List listeners = getListeners(source, false);
if (listeners == null) {
return;
}
boolean wasListening = (listeners.size() != 0);
listeners.remove(listener);
if (wasListening && (listeners.size() == 0)) {
listeningStopped(ignoresSource ? null : source);
}
}
/**
* {@inheritDoc}
*/
public final PropertyStateListener[] getPropertyStateListeners(S source) {
List listeners = getListeners(source, false);
if (listeners == null) {
return new PropertyStateListener[0];
}
PropertyStateListener[] ret = new PropertyStateListener[listeners.size()];
ret = listeners.toArray(ret);
return ret;
}
/**
* Notify listeners that the state of this property has changed, as
* characterized by the given {@code PropertyStateEvent}. If this
* {@code PropertyHelper} is managing listeners for multiple sources, only
* the listeners associated with the object returned by the
* {@code PropertyStateEvent's getSourceObject()} method are notified.
*
* @param pse the {@code PropertyStateEvent} characterizing the state change
*/
protected final void firePropertyStateChange(PropertyStateEvent pse) {
List listeners = getListeners((S) pse.getSourceObject(),
false);
if (listeners == null) {
return;
}
for (PropertyStateListener listener : listeners) {
listener.propertyStateChanged(pse);
}
}
/**
* Returns whether or not there are any {@code PropertyStateListeners}
* installed for the given source object.
*
* @param source the source object of interest
* @return whether or not there are any {@code PropertyStateListeners}
* installed for the given source object
*/
public final boolean isListening(S source) {
List listeners = getListeners(source, false);
return (listeners != null) && (listeners.size() != 0);
}
}