Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.sf.cuf.model.AspectAdapter Maven / Gradle / Ivy
package net.sf.cuf.model;
import java.util.List;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* A AspectAdapter is a ValueModel that takes a part (=an aspect) of an
* other object (called the source) as its value.
* To detect changes of the aspect, the source must be the value of
* a ValueModel. If the source is not wrapped in a ValueModel, a
* change in the source can't be detected.
* The aspect is described via a string like "attribut1/attribut2"
* and getting/setting the aspect is done via reflection like
* source.getAttribut1().getAttribut2() or
* source.getAttribut1().setAttribut2().
* An alternative to the reflection access is via usage of the Map
* get/put interface.
* The normal mode of operation is that a ValueModel holds some
* "transport" level type of object, which usally is quite dump,
* i.e. just a structure ("Value Object") with JavaBeans semantics.
* When different AspectAdapters wrap different parts of such
* a structure, it must be carefully avoided that more than one
* AspectAdapter maps (parts of) the same data, because those
* two adapters won't notice each other changes.
* @param the type we contain as our value
* @param the type the source is from
*/
public class AspectAdapter extends AbstractValueModel
implements ValueModel, ChangeListener, DelegateAccess, ExternalUpdate
{
/** The separator between attributes. */
public static final String SEPARATOR= ".";
/** null or the trigger */
private ValueModel extends S> mTrigger;
/** null or mTrigger if the trigger supports an external update propagation */
private ExternalUpdate mTriggerUpdate;
/** null or our source */
private Object mSource;
/** the aspect the adapter was initialized with, never null */
private String mAspectName;
/** our (base) source class (or interface), may be null */
private Class mSourceClass;
/** our strategy to get/set the values */
private AspectAccessAdapter mAccessAdapter;
/** Default get method name prefix. */
public static final String GET= "get";
/** Default set method name prefix. */
public static final String SET= "set";
/**
* Create a new AspectAdapter or re-use an existing one.
* This factory method uses the getDependents() method from the AbstractValueModel
* to find already existing AspectAdapter's for the same aspect.
* Warning: we don't search recursivly, so the aspect name must be exactly equal.
* @param pTrigger the trigger, must not be null
* @param pAspectName string describing the attributes (seperated by SEPARATOR) to our aspect
* @return a new or shared aspect adapter
*/
public static AspectAdapter,?> getShared(final ValueModel> pTrigger, final String pAspectName)
{
if (pTrigger==null)
throw new IllegalArgumentException("trigger must not be null");
AspectAdapter,?> back= null;
// search existing one
if (pTrigger instanceof AbstractValueModel)
{
AbstractValueModel> trigger= (AbstractValueModel>)pTrigger;
List dependents= trigger.getDependents();
for (final Object dependent : dependents)
{
if (dependent instanceof AspectAdapter)
{
AspectAdapter,?> aspectAdapter= (AspectAdapter,?>)dependent;
if (aspectAdapter.getAspectName().equals(pAspectName))
{
back= aspectAdapter;
break;
}
}
}
}
// create new AspectAdapter if we found no matching one
if (back==null)
{
back= new AspectAdapter(pTrigger, pAspectName);
}
return back;
}
/**
* Creates a new AspectAdapter.
* @param pTrigger the source object of our aspect is the trigger's value
* @param pAspectName a string describing the attributes (seperated by SEPARATOR)
* to our aspect.
* @throws IllegalArgumentException if pTrigger is null
* or pAspectName doesn't map to valid methods
*/
public AspectAdapter(final ValueModel pTrigger, final String pAspectName)
{
this(pTrigger, pAspectName, GET, SET);
}
/**
* Creates a new AspectAdapter.
* @param pTrigger the source object of our aspect is the trigger's value
* @param pAspectName a string describing the attributes (separated by SEPARATOR)
* to our aspect.
* @param pGetterPrefix the prefix we use for the "getter", must not be null nor empty
* @param pSetterPrefix the prefix we use for the "getter", must not be null nor empty nor the getter prefix
* @throws IllegalArgumentException if pTrigger is null
* or pAspectName doesn't map to valid methods
*/
public AspectAdapter(final ValueModel pTrigger, final String pAspectName, final String pGetterPrefix, final String pSetterPrefix)
{
super();
if (pTrigger==null)
throw new IllegalArgumentException("trigger must not be null");
S source= pTrigger.getValue();
init(source, (source==null)? null : (Class)source.getClass(), null, pTrigger, pAspectName, pGetterPrefix, pSetterPrefix);
}
/**
* Creates a new AspectAdapter.
* @param pTrigger the source object of our aspect is the trigger's value
* @param pSourceClass the class of the value of pTrigger, if this is a Map, the
* Map.get() and Map.put() methods are used to access the aspect.
* @param pAspectName a string describing the attributes (seperated by SEPARATOR)
* to our aspect.
* @throws IllegalArgumentException if pTrigger is null
* or pAspectName doesn't map to valid methods
*/
public AspectAdapter(final ValueModel pTrigger, final Class pSourceClass, final String pAspectName)
{
this(pTrigger, pSourceClass, null, pAspectName, GET, SET);
}
/**
* Creates a new AspectAdapter.
* @param pTrigger the source object of our aspect is the trigger's value
* @param pSourceClass the class of the value of pTrigger, if this is a Map, the
* Map.get() and Map.put() methods are used to access the aspect.
* @param pAspectName a string describing the attributes (seperated by SEPARATOR)
* to our aspect.
* @param pGetterPrefix the prefix we use for the "getter", must not be null nor empty
* @param pSetterPrefix the prefix we use for the "getter", must not be null nor empty nor the getter prefix
* @throws IllegalArgumentException if pTrigger is null
* or pAspectName doesn't map to valid methods
*/
public AspectAdapter(final ValueModel pTrigger, final Class pSourceClass, final String pAspectName, final String pGetterPrefix, final String pSetterPrefix)
{
this(pTrigger, pSourceClass, null, pAspectName, pGetterPrefix, pSetterPrefix);
}
/**
* Creates a new AspectAdapter.
* @param pTrigger the source object of our aspect is the trigger's value
* @param pSourceClass the class of the value of pTrigger, if this is a Map, the
* Map.get() and Map.put() methods are used to access the aspect.
* @param pAccessAdapter the access adapter, may be null
* @param pAspectName a string describing the attributes (seperated by SEPARATOR)
* to our aspect.
* @throws IllegalArgumentException if pTrigger is null
* or pAspectName doesn't map to valid methods
*/
public AspectAdapter(final ValueModel pTrigger, final Class pSourceClass,
final AspectAccessAdapter pAccessAdapter,
final String pAspectName)
{
this(pTrigger, pSourceClass, pAccessAdapter, pAspectName, GET, SET);
}
/**
* Creates a new AspectAdapter.
* @param pTrigger the source object of our aspect is the trigger's value
* @param pSourceClass the class of the value of pTrigger, if this is a Map, the
* Map.get() and Map.put() methods are used to access the aspect.
* @param pAccessAdapter the access adapter, may be null
* @param pAspectName a string describing the attributes (seperated by SEPARATOR)
* to our aspect.
* @param pGetterPrefix the prefix we use for the "getter", must not be null nor empty
* @param pSetterPrefix the prefix we use for the "getter", must not be null nor empty nor the getter prefix
* @throws IllegalArgumentException if pTrigger is null
* or pAspectName doesn't map to valid methods
*/
public AspectAdapter(final ValueModel pTrigger, final Class pSourceClass,
final AspectAccessAdapter pAccessAdapter,
final String pAspectName, final String pGetterPrefix, final String pSetterPrefix)
{
super();
if (pTrigger==null)
throw new IllegalArgumentException("trigger must not be null");
init(pTrigger.getValue(), pSourceClass, pAccessAdapter, pTrigger, pAspectName, pGetterPrefix, pSetterPrefix);
}
/**
* Creates a new AspectAdapter.
* @param pSource the source object of our aspect
* @param pAspectName a string describing the attributes (seperated by SEPARATOR)
* to our aspect.
* @throws IllegalArgumentException if pSource or pAspectName is null
* or pAspectName doesn't map to valid methods
*/
public AspectAdapter(final S pSource, final String pAspectName)
{
super();
if (pSource==null)
throw new IllegalArgumentException("source must not be null");
init(pSource, (Class)pSource.getClass(), null, null, pAspectName, GET, SET);
}
/**
* Creates a new AspectAdapter.
* @param pSource the source object of our aspect
* @param pAspectName a string describing the attributes (seperated by SEPARATOR)
* to our aspect.
* @param pGetterPrefix the prefix we use for the "getter", must not be null nor empty
* @param pSetterPrefix the prefix we use for the "getter", must not be null nor empty nor the getter prefix
* @throws IllegalArgumentException if pSource or pAspectName is null
* or pAspectName doesn't map to valid methods
*/
public AspectAdapter(final S pSource, final String pAspectName, final String pGetterPrefix, final String pSetterPrefix)
{
super();
if (pSource==null)
throw new IllegalArgumentException("source must not be null");
init(pSource, (Class)pSource.getClass(), null, null, pAspectName, pGetterPrefix, pSetterPrefix);
}
/**
* Handle common constructor stuff.
* @param pSource source of our aspect, may be null
* @param pSourceClass class of the source of our aspect, may be null
* @param pAccessAdapter the access adapter, may be null
* @param pTrigger trigger for our aspect, may be null
* @param pAspectName attributes to our aspect
* @param pGetterPrefix the prefix we use for the "getter", must not be null nor empty
* @param pSetterPrefix the prefix we use for the "getter", must not be null nor empty nor the getter prefix
*/
private void init(final Object pSource, final Class pSourceClass,
final AspectAccessAdapter pAccessAdapter,
final ValueModel extends S> pTrigger, final String pAspectName,
final String pGetterPrefix, final String pSetterPrefix)
{
if ((pAspectName==null) || (pAspectName.length()==0))
throw new IllegalArgumentException("aspect string must not be null or empty");
mSource = pSource;
mSourceClass = pSourceClass;
mTrigger = pTrigger;
// initialize the value
setInSetValue(false, false);
if (mTrigger instanceof ExternalUpdate)
{
mTriggerUpdate= (ExternalUpdate) mTrigger;
}
else
{
mTriggerUpdate= null;
}
mAspectName = pAspectName;
if (pAccessAdapter!=null)
{
mAccessAdapter= pAccessAdapter;
}
else
{
mAccessAdapter= new MixedAccessAdapter( pSourceClass, pAspectName, pGetterPrefix, pSetterPrefix);
}
if (mTrigger!=null)
{
mTrigger.addChangeListener(this);
}
}
/**
* @return the access adapter used to set and get values, never null
*/
public AspectAccessAdapter getAccessAdapter()
{
return mAccessAdapter;
}
/**
* Sets the access adapter to use when setting or getting values.
* @param pAccessAdapter the adapter to use, must not be null
*/
public void setAccessAdapter(final AspectAccessAdapter pAccessAdapter)
{
if (pAccessAdapter==null)
throw new IllegalArgumentException( "pAccessAdapter must not be null");
mAccessAdapter = pAccessAdapter;
}
/**
* Returns true if a setValue() is possible, and doesn't throw always an
* UnsupportedOperationException.
* @return false if setValue will always throw an exception
*/
public boolean isEditable()
{
return mAccessAdapter.isEditable();
}
/**
* Return the aspect of the aspect adapter.
* @return the access string, never null
*/
public String getAspectName()
{
return mAspectName;
}
/**
* Returns null or the trigger ValueModel of this aspect adapter.
* @return null or a ValueModel
*/
public ValueModel extends S> getTrigger()
{
return mTrigger;
}
/**
* Returns null or the source object of this aspect adapter.
* @return the source, may be null
*/
public Object getSource()
{
return mSource;
}
/**
* Cleanup all resources: disconnect from any input sources (like
* other ValueModel's ...), and remove all listeners.
*/
public void dispose()
{
super.dispose();
if (mTrigger!=null && !mTrigger.isDisposed())
{
mTrigger.removeChangeListener(this);
}
}
/**
* Set a new value, this will fire a ChangeEvent if the new value
* is different from the old value.
* If our source is null, the call does nothing.
* @param pValue the new value (null is o.k.)
* @param pIsSetForced true if a forced setValue should be done
* @throws UnsupportedOperationException if this ValueModel is not mutable
*/
public void setValue(final T pValue, final boolean pIsSetForced)
{
checkDisposed();
if (!isEditable())
{
throw new UnsupportedOperationException("value not mutable ("+mSourceClass+
", aspect: "+mAspectName+')');
}
if (mSource==null)
{
// silently ignore changes when there is no source
return;
}
try
{
setInSetValue(true, pIsSetForced);
mAccessAdapter.setValue(mSource, pValue);
if (mTriggerUpdate!=null)
{
mTriggerUpdate.signalExternalUpdate();
}
fireStateChanged();
}
finally
{
setInSetValue(false, false);
}
}
/**
* Get the current value, during a callback this is the new value.
* @return null or the value object
*/
public T getValue()
{
checkDisposed();
return (T)mAccessAdapter.getValue(mSource);
}
/**
* Transform the handed value to a new value.
* This should not change the current value or trigger any updates.
* @param pValue the value we should assume as our value
* @return null or the transformed/mapped value object
*/
public Object getValue(final Object pValue)
{
Object value= pValue;
if (mTrigger instanceof DelegateAccess)
{
value= ((DelegateAccess)mTrigger).getValue(pValue);
}
return mAccessAdapter.getValue(value);
}
/**
* Signal this object that portions of its data changed.
* If we have a trigger, we notify it, and assume that it will notify us,
* otherwise we notify our children directly.
*/
@Override
public void signalExternalUpdate()
{
if (mTrigger instanceof ExternalUpdate)
{
((ExternalUpdate)mTrigger).signalExternalUpdate();
}
else
{
super.signalExternalUpdate();
}
}
/**
* Signal this object that portions of its data changed.
* If we have a trigger, we notify it, and assume that it will notify us,
* otherwise we notify our children directly.
* @param pChangeEvent the specific change information
*/
@Override
public void signalExternalUpdate(ChangeEvent pChangeEvent)
{
if (mTrigger instanceof ExternalUpdate)
{
((ExternalUpdate)mTrigger).signalExternalUpdate(pChangeEvent);
}
else
{
super.signalExternalUpdate(pChangeEvent);
}
}
/**
* Invoked when the target of the listener has changed its state.
*
* @param pEvent a ChangeEvent object
*/
public void stateChanged(final ChangeEvent pEvent)
{
checkDisposed();
// ignore callbacks when we are the reason that our model changes
if (isInSetValue())
{
return;
}
Object newValue = mTrigger.getValue();
boolean isSetForced= mTrigger.isSetForced();
if (!isSetForced)
{
//noinspection ObjectEquality
if ((newValue==null && mSource==null) || (newValue==mSource))
{
// nothing changed
return;
}
}
if ((newValue!=null) &&
(mSourceClass!=null) &&
(!mSourceClass.isAssignableFrom(newValue.getClass())) )
{
throw new IllegalStateException("new value has wrong type (expected: "+
mSourceClass+", found: "+
newValue.getClass()+')');
}
mSource= newValue;
try
{
setInSetValue(false, isSetForced);
fireStateChanged();
}
finally
{
setInSetValue(false, false);
}
}
}