/*
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.
*
*
*/