net.sf.cuf.model.AbstractValueModel Maven / Gradle / Ivy
The newest version!
package net.sf.cuf.model;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
/**
* Abstract base class for a ValueModel, does the listener handling.
* The methods of this class are not threadsave, but in an
* UI environment that's o.k..
* A derived class needs only to implement the methods
* - void setValue(Object pValue, boolean pIsSetForced); and
*
- Object getValue();
*
* Inside the derived class' setValue() method the code may check if
* the value changed at all if the pIsSetForced flag is false.
* If there was no change (and pIsSetForced is false), the method
* may return immediately.
* If pIsSetForced if true, or the new and old value differ, the
* setValue() method should do the following tasks:
* - set the value
*
- call the setInSetValue(true, pIsSetForced) method
*
- call the fireStateChanged() method in a try/finally block
*
- call the setInSetValue(false, pIsSetForced) method in the
* finally block
*
* In the getValue() method, a derived class has not to do any
* special handling.
* @param the type of the value we contain
*/
@SuppressWarnings("unchecked")
public abstract class AbstractValueModel implements ValueModel, ExternalUpdate
{
/** our "Java style" listeners */
private ChangeListener[] mJListeners;
/** our "Smalltalk style" listeners */
private Object[] mSListeners;
/** our "Smalltalk style" listener methods */
private Method[] mSMethods;
// to avoid re-creation of (immutable) objects, each AbstractValueModel
// uses only one ChangeEvent and one args array, they are created when
// they are the required for the first time
/** null or the change event */
private ChangeEvent mChangeEvent;
/** null or the change args */
private Object[] mChangeArgs;
/** marker if we are in setValue() */
private boolean mInSetValue;
/** marker if we are in a forced setValue() */
private boolean mIsSetForced;
/** marker if we are disposed (= no longer valid) */
private boolean mIsDisposed;
/** our name (never null) */
private String mName;
/** shared empty change listener array */
private static final ChangeListener[] EMPTY_CL_LIST = new ChangeListener[0];
/** shared empty object array */
private static final Object[] EMPTY_OBJECT_LIST= new Object[0];
/** shared empty method array */
private static final Method[] EMPTY_METHOD_LIST= new Method[0];
/** shared empty class array */
private static final Class>[] PARAMS= {ChangeEvent.class};
/**
* Creates internal listener lists.
*/
protected AbstractValueModel()
{
mJListeners = EMPTY_CL_LIST;
mSListeners = EMPTY_OBJECT_LIST;
mSMethods = EMPTY_METHOD_LIST;
mInSetValue = false;
mIsSetForced= false;
mIsDisposed = false;
mName = "";
}
/**
* Add an dependent listener to a ValueModel, "Smalltalk style".
* @param pDependent the object to call
* @param pMethodName the method name to call back
* @throws IllegalArgumentException if pDependent or pMethod is null,
* or no method with the signature
* "public void pMethodName(ChangeEvent e)"
* is found for pDependent.
*/
public void onChangeSend(final Object pDependent, final String pMethodName)
{
checkDisposed();
if (pDependent==null)
throw new IllegalArgumentException("dependent object must not be null");
if (pMethodName==null)
throw new IllegalArgumentException("method name must not be null");
if (!Modifier.isPublic(pDependent.getClass().getModifiers()))
throw new IllegalArgumentException("The class "+pDependent.getClass().getName()+
" of the dependent object is not public");
/*
* create Method Object, the signature is always
* void (ChangeEvent e)
*/
Method method;
try
{
method= pDependent.getClass().getMethod(pMethodName, PARAMS);
}
catch (NoSuchMethodException e)
{
throw new IllegalArgumentException(
"could not extract method "+pMethodName+ " of class "+
pDependent.getClass().getName()+": "+ e.getMessage(), e);
}
// check if we know pDependent, we only change the method in that case
int oldLength= mSListeners.length;
for (int i = 0; i < oldLength; i++)
{
//noinspection ObjectEquality
if (mSListeners[i]==pDependent)
{
mSMethods[i]= method;
return;
}
}
// add entry at the end of each array
Object[] sListeners= new Object[oldLength+1];
Method[] sMethods = new Method[oldLength+1];
System.arraycopy(mSListeners, 0, sListeners, 0, oldLength);
System.arraycopy(mSMethods, 0, sMethods, 0, oldLength);
sListeners[oldLength]= pDependent;
sMethods [oldLength]= method;
mSListeners= sListeners;
mSMethods = sMethods;
}
/**
* Add an dependent listener to a ValueModel, "Java style".
* @param pDependent the object to call
* @throws IllegalArgumentException if pDependent is null
*/
public void addChangeListener(final ChangeListener pDependent)
{
checkDisposed();
if (pDependent==null)
{
throw new IllegalArgumentException("dependent must not be null");
}
// check if we know pDependent
int oldLength= mJListeners.length;
for (ChangeListener jListener : mJListeners)
{
//noinspection ObjectEquality
if (jListener == pDependent)
{
return;
}
}
// add entry at the end of the array
ChangeListener[] jListeners= new ChangeListener[oldLength+1];
System.arraycopy(mJListeners, 0, jListeners, 0, oldLength);
jListeners[oldLength]= pDependent;
mJListeners= jListeners;
}
/**
* Remove a dependent listener, "Smalltalk style".
* If pDependent is null or not known, nothing happens.
* @param pDependent object to de-register
*/
public void retractInterestsFor(final Object pDependent)
{
checkDisposed();
// check if we know pDependent
int i;
int oldLength= mSListeners.length;
for (i = 0; i < oldLength; i++)
{
Object sListener = mSListeners[i];
//noinspection ObjectEquality
if (sListener==pDependent)
break;
}
if (i==oldLength)
{
return;
}
// check if we get empty
int newLength = oldLength-1;
if (newLength<1)
{
mSListeners= EMPTY_OBJECT_LIST;
mSMethods = EMPTY_METHOD_LIST;
}
else
{
Object[] sListeners= new Object[newLength];
Method[] sMethods = new Method[newLength];
System.arraycopy(mSListeners, 0, sListeners, 0, i);
System.arraycopy(mSListeners, i+1, sListeners, i, newLength-i);
System.arraycopy(mSMethods , 0, sMethods , 0, i);
System.arraycopy(mSMethods , i+1, sMethods , i, newLength-i);
mSListeners= sListeners;
mSMethods = sMethods;
}
}
/**
* Remove a dependent listener, "Java style".
* If pDependent is null or not known, nothing happens.
* @param pDependent object to de-register
*/
public void removeChangeListener(final ChangeListener pDependent)
{
checkDisposed();
// check if we know pDependent
int i;
int oldLength= mJListeners.length;
for (i = 0; i < oldLength; i++)
{
Object jListener = mJListeners[i];
//noinspection ObjectEquality
if (jListener==pDependent)
break;
}
if (i==oldLength)
{
return;
}
// check if we get empty
int newLength = oldLength-1;
if (newLength<1)
{
mJListeners= EMPTY_CL_LIST;
}
else
{
ChangeListener[] jListeners= new ChangeListener[newLength];
System.arraycopy(mJListeners, 0, jListeners, 0, i);
System.arraycopy(mJListeners, i+1, jListeners, i, newLength-i);
mJListeners= jListeners;
}
}
/**
* Cleanup all resources: disconnect from any input sources (like
* other ValueModel's ...), and remove all listeners. If any method
* is called after the ValueModel was exposed, a IllegalStateException
* may be thrown.
*/
public void dispose()
{
mIsDisposed= true;
mJListeners= EMPTY_CL_LIST;
mSListeners= EMPTY_OBJECT_LIST;
mSMethods = EMPTY_METHOD_LIST;
}
/**
* Check if the value model is disposed.
* @return true if the value model is disposed (= no longer valid)
*/
public boolean isDisposed()
{
return mIsDisposed;
}
/**
* Helper method to throw a IllegalStateException if we are disposed.
* @throws IllegalStateException if the value model is disposed
*/
protected void checkDisposed() throws IllegalStateException
{
if (mIsDisposed)
{
throw new IllegalStateException("value model "+mName+" was already disposed");
}
}
/**
* Return a immutable list of all direct dependents of the value model.
* @return an immutablelList of all direct dependents
*/
public List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy