blob: a530676edab00b5259d63c22cebd7b167421f098 [file] [log] [blame]
package org.eclipse.jst.jsf.common.runtime.internal.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.jst.jsf.common.runtime.internal.model.decorator.Decorator;
/**
* The base type of all objects that participate in the lifecycle of a view.
* This may be used to hold either design time or runtime information.
*
* @author cbateman
*
*/
public abstract class ViewObject implements Serializable /* IAdaptable? */
{
private Map/* <Class, List<Decorator>> */_decorators;
private Map/* <Class, Object> */_adapters;
/**
*
*/
private static final long serialVersionUID = 1592205691642453075L;
/**
* This call may be create a new data structure and should be considered of
* much higher cost than most calls.
*
* @return all decorators of this object. List should be assumed by clients
* to be unmodifiable and may throw mutation exceptions
*/
public List getAllDecorators() {
final int size = getDecoratorMap().size();
if (size == 0) {
return Collections.EMPTY_LIST;
}
final List allDecorators = new ArrayList();
for (final Iterator entryIt = getDecoratorMap().entrySet().iterator(); entryIt
.hasNext();) {
final Map.Entry entry = (Map.Entry) entryIt.next();
final List decorators = (List) entry.getValue();
allDecorators.addAll(decorators);
}
return Collections.unmodifiableList(allDecorators);
}
/**
* @param decoratorType
* @return all decorators of this object associated with the class type. The
* returned list should be assumed to be unmodifiable. Returns an
* empty list if no decorators are associated with decoratorType
*/
public List getDecorators(final Class decoratorType) {
final List decorators = (List) getDecoratorMap().get(decoratorType);
if (decorators == null) {
return Collections.EMPTY_LIST;
}
return Collections.unmodifiableList(decorators);
}
/**
* Add the decorator using decorator.getClass to associate the type. Must be
* equivalent to addDecorator(decorator, decorator.getClass());
*
* @param decorator
* the decorator to add. <b>Must NOT be null.</b>
* @throws IllegalArgumentException
* if decorator is null.
*/
public void addDecorator(final Decorator decorator) {
if (decorator == null) {
throw new IllegalArgumentException("Arguments must not be null");
}
final Class associationType = decorator.getClass();
addDecorator(decorator, associationType);
}
/**
* Add the decorator using the associatedType. Normally you should call
* {@link #addDecorator(Decorator)} however in some cases you may wish to
* associated a decorator with a class type other than its own. For example,
* sub-classes of ValidatorDecorator will want to associated with
* ValidatorDecorator.getClass() in most cases so that it is included in any
* common examination of component validators.
*
* @param decorator
* to add. <b>Must NOT be null.</b>
* @param associatedType
* the key to store decorator <b>Must NOT be null</b>
* @throws IllegalArgumentException
* if decorator or associatedType is null
*/
public void addDecorator(final Decorator decorator,
final Class associatedType) {
if (decorator == null || associatedType == null) {
throw new IllegalArgumentException("Arguments must not be null");
}
List decoratorsByType = (List) getDecoratorMap().get(associatedType);
if (decoratorsByType == null) {
decoratorsByType = new ArrayList(2);
getDecoratorMap().put(associatedType, decoratorsByType);
}
decoratorsByType.add(decorator);
}
/**
* Removes decorator if it is associated to associatedType on this view
* object.
*
* @param decorator
* @param associatedType
* @return true if the decorator was found and removed.
* @throws IllegalArgumentException
* if decorator or associatedType is null
*/
public boolean removeDecorator(final Decorator decorator,
final Class associatedType) {
if (decorator == null || associatedType == null) {
throw new IllegalArgumentException("Arguments must not be null");
}
final List decoratorsByType = (List) getDecoratorMap().get(
associatedType);
if (decoratorsByType != null) {
return decoratorsByType.remove(decorator);
}
return false;
}
/**
* <p>Get the adapter associated the class key.</p>
*
* <p>If addAdapter has been called with adapterType, then this
* adapter object should always be called. In the default implementation,
* if there is no explicit adapter, "this" is returned if (this instanceof adapterType).
*
* @param adapterType
* @return the interface adapter associated with the class key or null if
* not found
*/
public Object getAdapter(final Class adapterType) {
if (adapterType == null) {
return null;
}
Object adapter = getAdapterMap().get(adapterType);
if (adapter == null)
{
if (adapterType.isInstance(this)) {
adapter = this;
}
}
return adapter;
}
/**
* Adds the interface adapter object under adapterType key. There can be at
* most one adapter registered for each class key.
*
* It is an error (throws exception) to try to add an adapter for adapterType
* which this is already instance. This restriction is necessary because
* otherwise local getters/setters would need to be aware of the adapter
* mechanism and verify inheritance hierarchies on every calls. This
* mechanism is intended only for adding interfaces to view object impls
* that don't already have them.
*
* @param adapterType
* @param adapter
* @throws IllegalArgumentException
* if adapterType or adapter is null or if casting adapter to
* adapterType would * cause a ClassCastException (i.e. if
* !(adapter instanceof adapterType))
* OR if this is already an instance of adapterType.
*/
public void addAdapter(final Class adapterType, final Object adapter) {
if (adapterType == null || adapter == null) {
throw new IllegalArgumentException("Arguments must not be null");
}
if (!adapterType.isInstance(adapter)) {
throw new IllegalArgumentException("adapter: " + adapter
+ " must be cast compatible to class: " + adapterType);
}
else if (adapterType.isInstance(this))
{
throw new IllegalArgumentException("this: " + this
+ " must not already be an instance of class: " + adapterType);
}
getAdapterMap().put(adapterType, adapter);
}
/**
* Note that {@link #getAdapter(Class)} may still return non-null after
* this is called if (this instanceof adapterType).
*
* @param adapterType
* @return the adapter for adapterType that was just removed or null if not
* found
*/
public Object removeAdapter(final Class adapterType) {
return getAdapterMap().remove(adapterType);
}
/**
* Note that this only returns those adapters added using
* {@link #addAdapter(Class, Object)}. It does not return any
* implicit adapters resulting from (this instanceof type).
*
* @return the map of all adapters. Maps is immutable and may throw
* exceptions on attempts to mutate.
*/
public Map getAllAdapters()
{
if (getAdapterMap().size() == 0)
{
return Collections.EMPTY_MAP;
}
return Collections.unmodifiableMap(getAdapterMap());
}
/**
* <p>
* The contract for this method is that it must always return a usable Map
* and that map must be the same on every call. Lazy construction may be
* used (as it is by default). The default map size is 4 and load factor is
* 3 meaning that there should be decent tradeoff between wasted table size
* and overhead used to increase it should the number of decorators exceed
* 3. <b>Must never return null.</b>
* </p>
*
* <p>
* Generally, the method should not need to be overridden, however it is
* provided to allow sub-classes to change the way the decorators map is
* constructed.
* </p>
*
* @return the map containing lists of decorators keyed by class.
*
*/
protected Map getDecoratorMap() {
if (_decorators == null) {
_decorators = new HashMap(4);
}
return _decorators;
}
/**
* <p>
* The contract for this method is that it must always return a usable Map
* and that map must be the same on every call. Lazy construction may be
* used (as it is by default). The default map size is 4 and load factor is
* 3 meaning that there should be decent tradeoff between wasted table size
* and overhead used to increase it should the number of decorators exceed
* 3. <b>Must never return null.</b>
* </p>
*
* <p>
* Generally, the method should not need to be overridden, however it is
* provided to allow sub-classes to change the way the decorators map is
* constructed.
* </p>
*
* @return the map containing lists of adapters keyed by class.
*
*/
protected Map getAdapterMap() {
if (_adapters == null) {
_adapters = new HashMap(4);
}
return _adapters;
}
}