package er.extensions.foundation; import java.util.concurrent.CopyOnWriteArrayList; import com.webobjects.foundation.NSArray; import com.webobjects.foundation._NSDelegate; /** * <p>By design, WebObjects' classes that accept a delegate only accept a single object. ERXMulticastingDelegate allows multiple * delegate objects to be aggregated and presented as a single delegate object.</p> * * <p>Delegates are called in the order they are added. Methods are called until one of the delegates handles the message. * Delegate methods that have a Object or boolean return type are called until one returns a non-null response that is not the default value. * Delegate methods that have a void return type are always called.</p> * * <p>Here is an example if you have multiple delegates that you want to set up.</p> * <pre> * ERXDatabaseContextMulticastingDelegate multiDelegate = new ERXDatabaseContextMulticastingDelegate(); * multiDeletegate.addDelegate(new ERXDatabaseContextDelegate()); * multiDeletegate.addDelegate(new ERXEntityDependencyOrderingDelegate()); * EODatabaseContext.setDefaultDelegate(multiDelegate); * </pre> * * <p>Here is a usage example to handle the case where a deletegate may already be set</p> * <pre> * Object newDelegate = new ERXEntityDependencyOrderingDelegate(); * ERXDatabaseContextMulticastingDelegate multiDelegate; * if (EODatabaseContext.defaultDelegate() == null) { * multiDelegate = new ERXDatabaseContextMulticastingDelegate(); * } * else { * if (EODatabaseContext.defaultDelegate() instanceof ERXDatabaseContextMulticastingDelegate) { * multiDelegate = (ERXDatabaseContextMulticastingDelegate)EODatabaseContext.defaultDelegate(); * } * else { * multiDelegate = new ERXDatabaseContextMulticastingDelegate(); * multiDelegate.addDelegate(EODatabaseContext.defaultDelegate()); * } * } * multiDelegate.addDelegate(newDelegate); * EODatabaseContext.setDefaultDelegate(multiDelegate); * </pre> * * <p>This class needs to be implemented for each delegate interface. All methods on the interface should be implemented * and should call one of the perform... or booleanPerform... methods on this class. See ERXDatabaseContextMulticastingDelegate * for example usage. One result of this implementation is that delegates can be added and removed at any time.</p> * * @author chill */ public abstract class ERXMulticastingDelegate { private CopyOnWriteArrayList delegates = new CopyOnWriteArrayList(); /** * Adds <code>delegate</code> at the end of the chain. It becomes the last delegate called. * * @param delegate Object to add as one of the delegates called */ public void addDelegate(Object delegate) { if (hasDelegate(delegate)) { throw new IllegalArgumentException("Delegate is already included"); } _NSDelegate delegateObject = new _NSDelegate(getClass(), delegate); delegates.add(delegateObject); } /** * Adds <code>delegate</code> at the start of the chain. It becomes the first delegate called. * * @param delegate Object to add as one of the delegates called */ public void addDelegateAtStart(Object delegate) { if (hasDelegate(delegate)) { throw new IllegalArgumentException("Delegate is already included"); } _NSDelegate delegateObject = new _NSDelegate(getClass(), delegate); delegates.add(0, delegateObject); } /** * Removes <code>delegate</code> from the delegates called. * * @param delegate Object to remove as one of the delegates called */ public void removeDelegate(Object delegate) { if ( ! hasDelegate(delegate)) { throw new IllegalArgumentException("Delegate is not present"); } for (int i = 0; i < delegates.size(); i++) { _NSDelegate delegateObject = (_NSDelegate)delegates.get(i); if (delegateObject.delegate().equals(delegate)) { delegates.remove(i); break; } } } /** * Returns <code>true</code> if delegate is represented in delegates(). * * @param delegate Object to test for membership in delegates() * @return <code>true</code> if delegate is represented in delegates() */ public boolean hasDelegate(Object delegate) { for (int i = 0; i < delegates.size(); i++) { _NSDelegate delegateObject = (_NSDelegate)delegates.get(i); if (delegateObject.delegate().equals(delegate)) { return true; } } return false; } /** * This method returns an array of <code>com.webobjects.foundation._NSDelegate</code>, <b>not</b> * the delegate objects originally added by calling addDelegate... Call <code>delegate()</code> * on the elements in this list to examine the delegate objects originally added by calling addDelegate... * * @return the delegates in the order they will be called */ public NSArray delegates() { return new NSArray(delegates); } /** * Use this to set the delegate order if the addDelegate... methods are not sufficient. * <code>orderedDelegates</code> should be a re-arrangement of the list returned by <code>delegates()</code>. * * @param orderedDelegates array of <code>com.webobjects.foundation._NSDelegate</code> in the order in which delegates should be called */ public void setDelegateOrder(NSArray orderedDelegates) { for (int i = 0; i < orderedDelegates.count(); i++) { if ( ! (orderedDelegates.objectAtIndex(i) instanceof _NSDelegate)) { throw new IllegalArgumentException("Object of class " + orderedDelegates.objectAtIndex(i).getClass().getName() + " must be instanceof _NSDelegate"); } } delegates.clear(); delegates.addAll(orderedDelegates); } /** * This is the central method for dispatching messages to the delegates aggregated by this object. * The other perform... and booleanPerform... methods simply call this method. * * @param methodName the name of the delegate method to call * @param args 0 or more arguments to pass to the delegate method * @param defaultResult the value to return if none of the delegates implement this method * @return value returned by the last delegate called */ protected Object perform(String methodName, Object args[], Object defaultResult) { Object result = null; for (int i = 0; (result == null || result.equals(defaultResult)) && i < delegates.size(); i++) { _NSDelegate delegate = (_NSDelegate) delegates.get(i); if (delegate.respondsTo(methodName)) { result = delegate.perform(methodName, args); } } return result == null ? defaultResult : result; } protected Object perform(String methodName, Object defaultResult) { return perform(methodName, new Object[0], defaultResult); } protected Object perform(String methodName, Object arg, Object defaultResult) { return perform(methodName, new Object[] {arg}, defaultResult); } protected Object perform(String methodName, Object arg1, Object arg2, Object defaultResult) { return perform(methodName, new Object[] {arg1, arg2}, defaultResult); } protected Object perform(String methodName, Object arg1, Object arg2, Object arg3, Object defaultResult) { return perform(methodName, new Object[] {arg1, arg2, arg3}, defaultResult); } protected Object perform(String methodName, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object defaultResult) { return perform(methodName, new Object[] {arg1, arg2, arg3, arg4, arg5}, defaultResult); } protected boolean booleanPerform(String methodName, boolean defaultResult) { return booleanPerform(methodName, new Object[0], defaultResult); } protected boolean booleanPerform(String methodName, Object arg, boolean defaultResult) { return booleanPerform(methodName, new Object[] {arg}, defaultResult); } protected boolean booleanPerform(String methodName, Object arg1, Object arg2, boolean defaultResult) { return booleanPerform(methodName, new Object[] {arg1, arg2}, defaultResult); } protected boolean booleanPerform(String methodName, Object arg1, Object arg2, Object arg3, boolean defaultResult) { return booleanPerform(methodName, new Object[] {arg1, arg2, arg3}, defaultResult); } protected boolean booleanPerform(String methodName, Object args[], boolean defaultResult) { Boolean result = (Boolean) perform(methodName, args, Boolean.valueOf(defaultResult)); return result != null ? result.booleanValue() : false; } }