/*
Wotonomy: OpenStep design patterns for pure Java applications.
Copyright (C) 2000 Intersect Software Corporation
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, see http://www.gnu.org
*/
package net.wotonomy.control.internal;
import net.wotonomy.control.EOObserverCenter;
import net.wotonomy.foundation.NSMutableDictionary;
import net.wotonomy.foundation.internal.Introspector;
/**
* A Surrogate is a special object that can be used in a display group when you
* wish to emulate other objects or modify their behaviors. Because it is a Map,
* it makes use of Introspector's ability to treat keys in a map as if they were
* properties to implement the following features.
*
* - By default, Surrogate works like a Map, and reading and writing
* properties to a Surrogate gets and puts values in the Map.
* - If one or more delegate objects are specified, property keys that do not
* exist in the map are read from and written to the delegate object.
* - If a default value is specified, that value will be returned for all
* property reads that do not exist in the map or in the delegate object.
* (Subsequent writes to those properties will create a key in the map and then
* subsequent reads will read not read the default object.)
* - Subclasses can override the get(Object) method to further customize the
* behavior of a Surrogate.
*
*
* @author michael@mpowers.net
* @date $Date: 2006-02-18 17:46:44 -0500 (Sat, 18 Feb 2006) $
* @revision $Revision: 900 $
*/
public class Surrogate extends NSMutableDictionary {
protected Object[] delegates;
protected Object defaultValue;
/**
* Default constructor with no delegate object and no default value.
*/
public Surrogate() {
delegates = null;
defaultValue = null;
}
/**
* Constructor specifying a delegate object.
*/
public Surrogate(Object[] aDelegateArray) {
setDelegates(aDelegateArray);
}
/**
* Constructor specifying a default value.
*/
public Surrogate(Object aDefault) {
setDefaultValue(aDefault);
}
/**
* Constructor specifying a delegate object and a default value.
*/
public Surrogate(Object[] aDelegateArray, Object aDefault) {
setDelegates(aDelegateArray);
setDefaultValue(aDefault);
}
/**
* Returns the first delegate object, or null if no delegates exist.
*/
public Object getDelegate() {
if (delegates == null)
return null;
if (delegates.length == 0)
return null;
return delegates[0];
}
/**
* Sets the delegate object list to contain only the specified object.
*/
public void setDelegate(Object aDelegate) {
setDelegates(new Object[] { aDelegate });
}
/**
* Returns the list of delegates in the order in which they are consulted.
*/
public Object[] getDelegates() {
if (delegates == null)
delegates = new Object[0];
return delegates;
}
/**
* Sets the list of delegates in the order in which they will be consulted.
*/
public void setDelegates(Object[] aDelegateArray) {
delegates = aDelegateArray;
}
/**
* Returns the current default value, or null if no default exists.
*/
public Object getDefaultValue() {
return defaultValue;
}
/**
* Sets the default value.
*/
public void setDefaultValue(Object aDefault) {
defaultValue = aDefault;
}
/**
* Called by get to retrieve a value from the internal map. This implementation
* simply calls super.get().
*/
public Object directGet(Object aKey) {
return super.get(aKey);
}
/**
* Called by put to retrieve a value from the internal map. This implementation
* simply calls super.put().
*/
public Object directPut(Object aKey, Object aValue) {
return super.put(aKey, aValue);
}
/**
* Overridden to consult each delegate before checking the internal list of
* keys. No matching key is found, returns the default object, or null if no
* default object exists.
*/
public Object get(Object aKey) {
// check all delegates in order
int i, j;
Object[] list = getDelegates();
String[] properties;
for (i = 0; i < list.length; i++) {
// for each delegate
properties = Introspector.getReadPropertiesForObject(list[i]);
for (j = 0; j < properties.length; j++) {
// if delegate has property
if (properties[j].equals(aKey)) {
// use this delegate
return Introspector.get(list[i], aKey.toString());
}
}
}
// return from internal map
Object result = directGet(aKey);
if (result == null) {
// if not in map, return default object
result = getDefaultValue();
}
return result;
}
/**
* Overridden to attempt to write each delegate, writing to only the first
* successful delegate, before storing the value in the internal map.
*/
public Object put(Object aKey, Object aValue) {
// check all delegates in order
int i, j;
Object[] list = getDelegates();
String[] properties;
for (i = 0; i < list.length; i++) {
// for each delegate
properties = Introspector.getWritePropertiesForObject(list[i]);
for (j = 0; j < properties.length; j++) {
// if delegate has property
if (properties[j].equals(aKey)) {
// use this delegate
EOObserverCenter.notifyObserversObjectWillChange(list[i]);
return Introspector.set(list[i], aKey.toString(), aValue);
}
}
}
// set on internal map
EOObserverCenter.notifyObserversObjectWillChange(this);
return directPut(aKey, aValue);
}
/**
* Overridden to compare by reference.
*/
public boolean equals(Object anObject) {
return (this == anObject);
}
}
/*
* $Log$ Revision 1.1 2006/02/18 22:46:44 cgruber Add Surrogate map from .util
* into control's internal package, and fix imports.
*
* Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in
* eclipse-friendly maven-enabled packages.
*
* Revision 1.1.1.1 2000/12/21 15:52:21 mpowers Contributing wotonomy.
*
* Revision 1.2 2000/12/20 16:25:48 michael Added log to all files.
*
*
*/