View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2001 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.foundation;
20  
21  import net.wotonomy.foundation.internal.Introspector;
22  import net.wotonomy.foundation.internal.IntrospectorException;
23  import net.wotonomy.foundation.internal.MissingPropertyException;
24  import net.wotonomy.foundation.internal.NullPrimitiveException;
25  import net.wotonomy.foundation.internal.WotonomyException;
26  
27  /***
28  * NSKeyValueCoding defines an interface for classes that
29  * need to have more control over the wotonomy's property
30  * introspection facilities.  <br><br>
31  * 
32  * On an object that implements this interface, wotonomy 
33  * will call these methods, and otherwise use the static
34  * methods on NSKeyValueCodingSupport.  <br><br>
35  *
36  * NSKeyValueCodingSupport implements the default behaviors 
37  * for each of these methods, so classes implementing this 
38  * interface can call those methods to acheive the same 
39  * behavior. <br><br>
40  *
41  * valueForKey and takeValueForKey are called in response
42  * to user actions, like viewing an object or updating its
43  * value in a user interface.  These should call the public
44  * getter and setter methods on the object itself and the
45  * operations should be subject to validation. <br><br>
46  * 
47  * storedValueForKey and takeStoredValueForKey are called 
48  * in response to wotonomy actions, like snapshotting, 
49  * faulting, commits, and reverts.  These operations should
50  * bypass the public methods and directly modify the internal
51  * state of the object without validation.
52  *
53  * @author michael@mpowers.net
54  * @author $Author: cgruber $
55  * @version $Revision: 893 $
56  */
57  public interface NSKeyValueCoding 
58  {
59  
60  	public static final Null NullValue = new Null();
61  
62      /***
63      * Returns the value for the specified property.
64      * If the property does not exist, this method should
65      * call handleQueryWithUnboundKey.
66      */
67      Object valueForKey( String aKey );
68  
69      /***
70      * Sets the property to the specified value.
71      * If the property does not exist, this method should
72      * call handleTakeValueForUnboundKey.
73      * If the property is of a type that cannot allow
74      * null (e.g. primitive types) and aValue is null,
75      * this method should call unableToSetNullForKey.
76      */
77      void takeValueForKey( Object aValue, String aKey );
78  
79      /***
80      * Returns the value for the private field that 
81      * corresponds to the specified property.
82      */
83      Object storedValueForKey( String aKey );
84  
85      /***
86      * Sets the the private field that corresponds to the 
87      * specified property to the specified value.
88      */
89      void takeStoredValueForKey( Object aValue, String aKey );
90  
91      /***
92      * Called by valueForKey when the specified key is
93      * not found on this object.  Implementing classes 
94      * should handle the specified value or otherwise 
95      * throw an exception.
96      */
97      Object handleQueryWithUnboundKey( String aKey );
98  
99      /***
100     * Called by takeValueForKey when the specified key
101     * is not found on this object.  Implementing classes
102     * should handle the specified value or otherwise 
103     * throw an exception.
104     */
105     void handleTakeValueForUnboundKey( Object aValue, String aKey );
106 
107     /***
108     * Called by takeValueForKey when the type of the
109     * specified key is not allowed to be null, as is
110     * the case with primitive types.  Implementing 
111     * classes should handle this case appropriately
112     * or otherwise throw an exception.
113     */
114     void unableToSetNullForKey( String aKey );
115 
116     /***
117     * Static utility methods that
118     * call the appropriate method if the object implements
119     * NSKeyValueCoding, otherwise calls the method
120     * on DefaultImplementation.
121     */
122     public class Utility
123     {
124         /***
125         * Calls the appropriate method if the object implements
126         * NSKeyValueCoding, otherwise calls the method
127         * on DefaultImplementation.
128         */
129         static public Object valueForKey( 
130             Object anObject, String aKey )
131         {
132             if ( anObject instanceof NSKeyValueCoding )
133             {
134                 return ((NSKeyValueCoding)anObject).valueForKey( aKey );
135             }
136             return DefaultImplementation.valueForKey( anObject, aKey );
137         }
138     
139         /***
140         * Calls the appropriate method if the object implements
141         * NSKeyValueCoding, otherwise calls the method
142         * on DefaultImplementation.
143         */
144         static public void takeValueForKey( 
145             Object anObject, Object aValue, String aKey )
146         {
147             if ( anObject instanceof NSKeyValueCoding )
148             {
149                 ((NSKeyValueCoding)anObject).takeValueForKey( aValue, aKey );
150             }
151             DefaultImplementation.takeValueForKey( anObject, aValue, aKey );
152         }
153     
154         /***
155         * Calls the appropriate method if the object implements
156         * NSKeyValueCoding, otherwise calls the method
157         * on DefaultImplementation.
158         */
159         static public Object storedValueForKey( 
160             Object anObject, String aKey )
161         {
162             if ( anObject instanceof NSKeyValueCoding )
163             {
164                 return ((NSKeyValueCoding)anObject).storedValueForKey( aKey );
165             }
166             return DefaultImplementation.storedValueForKey( anObject, aKey );
167         }
168     
169         /***
170         * Calls the appropriate method if the object implements
171         * NSKeyValueCoding, otherwise calls the method
172         * on DefaultImplementation.
173         */
174         static public void takeStoredValueForKey( 
175             Object anObject, Object aValue, String aKey )
176         {
177             if ( anObject instanceof NSKeyValueCoding )
178             {
179                 ((NSKeyValueCoding)anObject).takeStoredValueForKey( aValue, aKey );
180             }
181             DefaultImplementation.takeStoredValueForKey( anObject, aValue, aKey );
182         }
183     
184         /***
185         * Calls the appropriate method if the object implements
186         * NSKeyValueCoding, otherwise calls the method
187         * on DefaultImplementation.
188         */
189         static public Object handleQueryWithUnboundKey( 
190             Object anObject, String aKey )
191         {
192             if ( anObject instanceof NSKeyValueCoding )
193             {
194                 return ((NSKeyValueCoding)anObject).handleQueryWithUnboundKey( aKey );
195             }
196             return DefaultImplementation.handleQueryWithUnboundKey( anObject, aKey );
197         }
198     
199         /***
200         * Calls the appropriate method if the object implements
201         * NSKeyValueCoding, otherwise calls the method
202         * on DefaultImplementation.
203         */
204         static public void handleTakeValueForUnboundKey( 
205             Object anObject, Object aValue, String aKey )
206         {
207             if ( anObject instanceof NSKeyValueCoding )
208             {
209                 ((NSKeyValueCoding)anObject).handleTakeValueForUnboundKey( aValue, aKey );
210             }
211             DefaultImplementation.handleTakeValueForUnboundKey( anObject, aValue, aKey );
212         }
213     
214         /***
215         * Calls the appropriate method if the object implements
216         * NSKeyValueCoding, otherwise calls the method
217         * on DefaultImplementation.
218         */
219         static public void unableToSetNullForKey( 
220             Object anObject, String aKey )
221         {
222             if ( anObject instanceof NSKeyValueCoding )
223             {
224                 ((NSKeyValueCoding)anObject).unableToSetNullForKey( aKey );
225             }
226             DefaultImplementation.unableToSetNullForKey( anObject, aKey );
227         }
228     }
229     
230     public class DefaultImplementation
231     {
232         /***
233         * Returns the value for the specified property key
234         * on the specified object. <br><br>
235         *
236         * If the property does not exist, this method calls
237         * handleQueryWithUnboundKey on the object if it 
238         * implements NSKeyValueCoding, otherwise calls
239         * handleQueryWithUnboundKey on this class. <br><br>
240         */
241         static public Object valueForKey( 
242             Object anObject, String aKey )
243         {
244             if ( anObject == null ) return null;    
245             //TODO: may need to handle "." nesting here so
246             // that handleQueryWithUnboundKey gets called for 
247             // for the nested object, not the parent object
248             
249             //Correction: need to handle key paths in
250             // KeyValueCodingAdditionsSupport.
251                 
252             try
253             {
254                 return Introspector.get( anObject, aKey );        
255             }
256             catch ( IntrospectorException exc )
257             {
258                 if ( anObject instanceof NSKeyValueCoding )
259                 {
260                     return ((NSKeyValueCoding)anObject).handleQueryWithUnboundKey( aKey );
261                 }
262                 return handleQueryWithUnboundKey( anObject, aKey );   
263             }
264         }
265     
266         /***
267         * Sets the property to the specified value on
268         * the specified object.
269         *
270         * If the property does not exist, this method calls
271         * handleTakeValueForUnboundKey on the object if it 
272         * implements NSKeyValueCoding, otherwise calls
273         * handleTakeValueForUnboundKey on this class.
274         * 
275         * If the property is of a type that cannot allow
276         * null (e.g. primitive types) and aValue is null,
277         * this method should call unableToSetNullForKey
278         * on the object if it implements NSKeyValueCoding, 
279         * otherwise calls unableToSetNullForKey on this class.    
280         */
281         static public void takeValueForKey( 
282             Object anObject, Object aValue, String aKey )
283         {
284             if ( anObject == null ) return;    
285             //TODO: may need to handle "." nesting here so
286             // that handleTakeValueForUnboundKey gets called for 
287             // for the nested object, not the parent object
288             try
289             {
290                 Introspector.set( anObject, aKey, aValue );
291             }
292             catch ( NullPrimitiveException exc )
293             {
294                 if ( anObject instanceof NSKeyValueCoding )
295                 {
296                     ((NSKeyValueCoding)anObject).unableToSetNullForKey( aKey );
297                 } 
298                 else
299                 {
300                     unableToSetNullForKey( anObject, aKey );   
301                 }
302             }
303             catch ( MissingPropertyException exc )
304             {
305                 if ( anObject instanceof NSKeyValueCoding )
306                 {
307                     ((NSKeyValueCoding)anObject).handleTakeValueForUnboundKey( 
308                         aValue, aKey );
309                 } 
310                 else
311                 {
312                     handleTakeValueForUnboundKey( anObject, aValue, aKey );   
313                 }
314             }
315             
316         }
317     
318         /***
319         * Returns the value for the private field that 
320         * corresponds to the specified property on
321         * the specified object.
322         *
323         * This implementation currently calls valueForKey,
324         * because java security currently prevents us from 
325         * accessing the fields of another object.
326         */
327         static public Object storedValueForKey( 
328             Object anObject, String aKey )
329         {
330             //TODO: this currently just calls valueForKey
331             return valueForKey( anObject, aKey );
332         }
333     
334         /***
335         * Sets the the private field that corresponds to the 
336         * specified property to the specified value on the
337         * specified object.
338         *
339         * This implementation currently calls takeValueForKey,
340         * because java security currently prevents us from 
341         * accessing the fields of another object.
342         */
343         static public void takeStoredValueForKey( 
344             Object anObject, Object aValue, String aKey )
345         {
346             //TODO: this currently just calls takeValueForKey
347             takeValueForKey( anObject, aValue, aKey );
348         }
349     
350         /***
351         * Called by valueForKey when the specified key is
352         * not found on the specified object, if that object
353         * does not implement NSKeyValueCoding.
354         *
355         * This implementation throws a WotonomyException.
356         */
357         static public Object handleQueryWithUnboundKey( 
358             Object anObject, String aKey )
359         {
360             throw new WotonomyException(   
361                 "Key not found for object: " 
362                 + aKey + " : " + anObject );
363         }
364     
365         /***
366         * Called by takeValueForKey when the specified key
367         * is not found on the specified object, if that object
368         * does not implement NSKeyValueCoding.
369         *
370         * This implementation throws a WotonomyException.
371         */
372         static public void handleTakeValueForUnboundKey( 
373             Object anObject, Object aValue, String aKey )
374         {
375             throw new WotonomyException(   
376                 "Key not found for object while setting value: " 
377                 + aKey + " : " + anObject + " : " + aValue );
378         }
379     
380         /***
381         * Called by takeValueForKey when the type of the
382         * specified key is not allowed to be null, as is
383         * the case with primitive types, if the specified
384         * object does not implement NSKeyValueCoding.  
385         *
386         * This implementation throws a WotonomyException.
387         */
388         static public void unableToSetNullForKey( 
389             Object anObject, String aKey )
390         {
391             throw new WotonomyException(   
392                 "Tried to key on object to null: " 
393                 + aKey + " : " + anObject );
394         }
395     }
396 
397 	public class Null {
398 		public Null() {
399 			super();
400 		}
401 
402 		public String toString() {
403 			return "<null>";
404 		}
405 	}
406 }
407 
408 /*
409  * $Log$
410  * Revision 1.2  2006/02/16 13:15:00  cgruber
411  * Check in all sources in eclipse-friendly maven-enabled packages.
412  *
413  * Revision 1.3  2003/08/07 02:43:56  chochos
414  * added NullValue, which points to a Null instance.
415  *
416  * Revision 1.2  2003/01/18 23:30:42  mpowers
417  * WODisplayGroup now compiles.
418  *
419  * Revision 1.1  2003/01/17 14:40:49  mpowers
420  * Adding files to fix build.
421  *
422  * Revision 1.2  2001/03/28 16:12:30  mpowers
423  * Documented interface.
424  *
425  * Revision 1.1  2001/03/27 23:25:05  mpowers
426  * Contributing interface, no docs yet.
427  *
428  *
429  */
430     
431