View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Intersect Software Corporation
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
17  */
18  
19  package net.wotonomy.control.internal;
20  
21  import net.wotonomy.control.EOObserverCenter;
22  import net.wotonomy.foundation.NSMutableDictionary;
23  import net.wotonomy.foundation.internal.Introspector;
24  
25  /***
26  * A Surrogate is a special object that can be used in a display
27  * group when you wish to emulate other objects or modify their
28  * behaviors.  Because it is a Map, it makes use of Introspector's
29  * ability to treat keys in a map as if they were properties to
30  * implement the following features.
31  * <ul>
32  * <li>By default, Surrogate works like a Map, and reading and 
33  * writing properties to a Surrogate gets and puts values in the 
34  * Map.</li>
35  * <li>If one or more delegate objects are specified, property keys 
36  * that do not exist in the map are read from and written to the 
37  * delegate object.</li>
38  * <li>If a default value is specified, that value will be returned
39  * for all property reads that do not exist in the map or in the
40  * delegate object.  (Subsequent writes to those properties will
41  * create a key in the map and then subsequent reads will read not
42  * read the default object.)</li>
43  * <li>Subclasses can override the get(Object) method to further
44  * customize the behavior of a Surrogate.
45  * </ul>
46  *
47  * @author michael@mpowers.net
48  * @date $Date: 2006-02-18 22:46:44 +0000 (Sat, 18 Feb 2006) $
49  * @revision $Revision: 900 $
50  */
51  public class Surrogate extends NSMutableDictionary
52  {
53  	protected Object[] delegates;
54  	protected Object defaultValue;
55  	
56  	/***
57  	* Default constructor with no delegate object and no default value.
58  	*/
59  	public Surrogate()
60  	{
61  		delegates = null;
62  		defaultValue = null;
63  	}
64  	
65  	/***
66  	* Constructor specifying a delegate object.
67  	*/
68  	public Surrogate( Object[] aDelegateArray )
69  	{
70  		setDelegates( aDelegateArray );
71  	}
72  
73  	/***
74  	* Constructor specifying a default value.
75  	*/
76  	public Surrogate( Object aDefault )
77  	{
78  		setDefaultValue( aDefault );
79  	}
80  
81  	/***
82  	* Constructor specifying a delegate object and a default value.
83  	*/
84  	public Surrogate( Object[] aDelegateArray, Object aDefault )
85  	{
86  		setDelegates( aDelegateArray );
87  		setDefaultValue( aDefault );
88  	}
89  
90  	/***
91  	* Returns the first delegate object, or null if no delegates exist.
92  	*/
93  	public Object getDelegate()
94  	{
95  		if ( delegates == null ) return null;
96  		if ( delegates.length == 0 ) return null;
97  		return delegates[0];
98  	}
99  	
100 	/***
101 	* Sets the delegate object list to contain only the
102 	* specified object.
103 	*/
104 	public void setDelegate( Object aDelegate )
105 	{
106 		setDelegates( new Object[] { aDelegate } );
107 	}
108 	
109 	/***
110 	* Returns the list of delegates in the order in which
111 	* they are consulted.
112 	*/
113 	public Object[] getDelegates()
114 	{
115 		if ( delegates == null ) delegates = new Object[0];
116 		return delegates;	
117 	}
118 	
119 	/***
120 	* Sets the list of delegates in the order in which they
121 	* will be consulted.
122 	*/
123 	public void setDelegates( Object[] aDelegateArray )
124 	{
125 		delegates = aDelegateArray;
126 	}
127 
128 	/***
129 	* Returns the current default value, or null if no default exists.
130 	*/
131 	public Object getDefaultValue()
132 	{
133 		return defaultValue;
134 	}
135 	
136 	/***
137 	* Sets the default value.
138 	*/
139 	public void setDefaultValue( Object aDefault )
140 	{
141 		defaultValue = aDefault;
142 	}
143 	
144 	/***
145 	* Called by get to retrieve a value from the internal map.
146 	* This implementation simply calls super.get().
147 	*/
148 	public Object directGet( Object aKey )
149 	{
150 		return super.get( aKey );	
151 	}
152 	
153 	/***
154 	* Called by put to retrieve a value from the internal map.
155 	* This implementation simply calls super.put().
156 	*/
157 	public Object directPut( Object aKey, Object aValue )
158 	{
159 		return super.put( aKey, aValue );	
160 	}
161 	
162 	/***
163 	* Overridden to consult each delegate before 
164 	* checking the internal list of keys.  No matching
165 	* key is found, returns the default object, or 
166 	* null if no default object exists.
167 	*/
168 	public Object get( Object aKey )
169 	{
170 		// check all delegates in order
171 		int i, j;
172 		Object[] list = getDelegates();
173 		String[] properties;
174 		for ( i = 0; i < list.length; i++ )
175 		{
176 			// for each delegate
177 			properties = 
178 				Introspector.getReadPropertiesForObject( list[i] );
179 			for ( j = 0; j < properties.length; j++ )
180 			{
181 				// if delegate has property
182 				if ( properties[j].equals( aKey ) )
183 				{
184 					// use this delegate
185 					return Introspector.get( list[i], aKey.toString() );	
186 				}
187 			}
188 		}
189 		
190 		// return from internal map
191 		Object result = directGet( aKey );
192 		if ( result == null )
193 		{
194 			// if not in map, return default object
195 			result = getDefaultValue();
196 		}
197 		return result;		
198 	}
199 	
200 	/*** 
201 	* Overridden to attempt to write each delegate, writing to
202 	* only the first successful delegate, before storing the 
203 	* value in the internal map.
204 	*/
205 	public Object put( Object aKey, Object aValue )
206 	{
207 		// check all delegates in order
208 		int i, j;
209 		Object[] list = getDelegates();
210 		String[] properties;
211 		for ( i = 0; i < list.length; i++ )
212 		{
213 			// for each delegate
214 			properties = 
215 				Introspector.getWritePropertiesForObject( list[i] );
216 			for ( j = 0; j < properties.length; j++ )
217 			{
218 				// if delegate has property
219 				if ( properties[j].equals( aKey ) )
220 				{
221 					// use this delegate
222 					EOObserverCenter.notifyObserversObjectWillChange( list[i] );
223 					return Introspector.set( list[i], aKey.toString(), aValue );	
224 				}
225 			}
226 		}
227 		
228 		// set on internal map
229 		EOObserverCenter.notifyObserversObjectWillChange( this );
230 		return directPut( aKey, aValue );
231 	}
232 
233 	/***
234 	* Overridden to compare by reference.
235 	*/
236 	public boolean equals( Object anObject )
237 	{
238 		return ( this == anObject );
239 	}
240 
241 }
242 
243 /*
244  * $Log$
245  * Revision 1.1  2006/02/18 22:46:44  cgruber
246  * Add Surrogate map from .util into control's internal package, and fix imports.
247  *
248  * Revision 1.1  2006/02/16 13:22:22  cgruber
249  * Check in all sources in eclipse-friendly maven-enabled packages.
250  *
251  * Revision 1.1.1.1  2000/12/21 15:52:21  mpowers
252  * Contributing wotonomy.
253  *
254  * Revision 1.2  2000/12/20 16:25:48  michael
255  * Added log to all files.
256  *
257  *
258  */
259