/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2001 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.foundation; import net.wotonomy.foundation.internal.Introspector; import net.wotonomy.foundation.internal.IntrospectorException; import net.wotonomy.foundation.internal.MissingPropertyException; import net.wotonomy.foundation.internal.NullPrimitiveException; import net.wotonomy.foundation.internal.WotonomyException; /** * NSKeyValueCoding defines an interface for classes that * need to have more control over the wotonomy's property * introspection facilities.

* * On an object that implements this interface, wotonomy * will call these methods, and otherwise use the static * methods on NSKeyValueCodingSupport.

* * NSKeyValueCodingSupport implements the default behaviors * for each of these methods, so classes implementing this * interface can call those methods to acheive the same * behavior.

* * valueForKey and takeValueForKey are called in response * to user actions, like viewing an object or updating its * value in a user interface. These should call the public * getter and setter methods on the object itself and the * operations should be subject to validation.

* * storedValueForKey and takeStoredValueForKey are called * in response to wotonomy actions, like snapshotting, * faulting, commits, and reverts. These operations should * bypass the public methods and directly modify the internal * state of the object without validation. * * @author michael@mpowers.net * @author $Author: cgruber $ * @version $Revision: 893 $ */ public interface NSKeyValueCoding { public static final Null NullValue = new Null(); /** * Returns the value for the specified property. * If the property does not exist, this method should * call handleQueryWithUnboundKey. */ Object valueForKey( String aKey ); /** * Sets the property to the specified value. * If the property does not exist, this method should * call handleTakeValueForUnboundKey. * If the property is of a type that cannot allow * null (e.g. primitive types) and aValue is null, * this method should call unableToSetNullForKey. */ void takeValueForKey( Object aValue, String aKey ); /** * Returns the value for the private field that * corresponds to the specified property. */ Object storedValueForKey( String aKey ); /** * Sets the the private field that corresponds to the * specified property to the specified value. */ void takeStoredValueForKey( Object aValue, String aKey ); /** * Called by valueForKey when the specified key is * not found on this object. Implementing classes * should handle the specified value or otherwise * throw an exception. */ Object handleQueryWithUnboundKey( String aKey ); /** * Called by takeValueForKey when the specified key * is not found on this object. Implementing classes * should handle the specified value or otherwise * throw an exception. */ void handleTakeValueForUnboundKey( Object aValue, String aKey ); /** * Called by takeValueForKey when the type of the * specified key is not allowed to be null, as is * the case with primitive types. Implementing * classes should handle this case appropriately * or otherwise throw an exception. */ void unableToSetNullForKey( String aKey ); /** * Static utility methods that * call the appropriate method if the object implements * NSKeyValueCoding, otherwise calls the method * on DefaultImplementation. */ public class Utility { /** * Calls the appropriate method if the object implements * NSKeyValueCoding, otherwise calls the method * on DefaultImplementation. */ static public Object valueForKey( Object anObject, String aKey ) { if ( anObject instanceof NSKeyValueCoding ) { return ((NSKeyValueCoding)anObject).valueForKey( aKey ); } return DefaultImplementation.valueForKey( anObject, aKey ); } /** * Calls the appropriate method if the object implements * NSKeyValueCoding, otherwise calls the method * on DefaultImplementation. */ static public void takeValueForKey( Object anObject, Object aValue, String aKey ) { if ( anObject instanceof NSKeyValueCoding ) { ((NSKeyValueCoding)anObject).takeValueForKey( aValue, aKey ); } DefaultImplementation.takeValueForKey( anObject, aValue, aKey ); } /** * Calls the appropriate method if the object implements * NSKeyValueCoding, otherwise calls the method * on DefaultImplementation. */ static public Object storedValueForKey( Object anObject, String aKey ) { if ( anObject instanceof NSKeyValueCoding ) { return ((NSKeyValueCoding)anObject).storedValueForKey( aKey ); } return DefaultImplementation.storedValueForKey( anObject, aKey ); } /** * Calls the appropriate method if the object implements * NSKeyValueCoding, otherwise calls the method * on DefaultImplementation. */ static public void takeStoredValueForKey( Object anObject, Object aValue, String aKey ) { if ( anObject instanceof NSKeyValueCoding ) { ((NSKeyValueCoding)anObject).takeStoredValueForKey( aValue, aKey ); } DefaultImplementation.takeStoredValueForKey( anObject, aValue, aKey ); } /** * Calls the appropriate method if the object implements * NSKeyValueCoding, otherwise calls the method * on DefaultImplementation. */ static public Object handleQueryWithUnboundKey( Object anObject, String aKey ) { if ( anObject instanceof NSKeyValueCoding ) { return ((NSKeyValueCoding)anObject).handleQueryWithUnboundKey( aKey ); } return DefaultImplementation.handleQueryWithUnboundKey( anObject, aKey ); } /** * Calls the appropriate method if the object implements * NSKeyValueCoding, otherwise calls the method * on DefaultImplementation. */ static public void handleTakeValueForUnboundKey( Object anObject, Object aValue, String aKey ) { if ( anObject instanceof NSKeyValueCoding ) { ((NSKeyValueCoding)anObject).handleTakeValueForUnboundKey( aValue, aKey ); } DefaultImplementation.handleTakeValueForUnboundKey( anObject, aValue, aKey ); } /** * Calls the appropriate method if the object implements * NSKeyValueCoding, otherwise calls the method * on DefaultImplementation. */ static public void unableToSetNullForKey( Object anObject, String aKey ) { if ( anObject instanceof NSKeyValueCoding ) { ((NSKeyValueCoding)anObject).unableToSetNullForKey( aKey ); } DefaultImplementation.unableToSetNullForKey( anObject, aKey ); } } public class DefaultImplementation { /** * Returns the value for the specified property key * on the specified object.

* * If the property does not exist, this method calls * handleQueryWithUnboundKey on the object if it * implements NSKeyValueCoding, otherwise calls * handleQueryWithUnboundKey on this class.

*/ static public Object valueForKey( Object anObject, String aKey ) { if ( anObject == null ) return null; //TODO: may need to handle "." nesting here so // that handleQueryWithUnboundKey gets called for // for the nested object, not the parent object //Correction: need to handle key paths in // KeyValueCodingAdditionsSupport. try { return Introspector.get( anObject, aKey ); } catch ( IntrospectorException exc ) { if ( anObject instanceof NSKeyValueCoding ) { return ((NSKeyValueCoding)anObject).handleQueryWithUnboundKey( aKey ); } return handleQueryWithUnboundKey( anObject, aKey ); } } /** * Sets the property to the specified value on * the specified object. * * If the property does not exist, this method calls * handleTakeValueForUnboundKey on the object if it * implements NSKeyValueCoding, otherwise calls * handleTakeValueForUnboundKey on this class. * * If the property is of a type that cannot allow * null (e.g. primitive types) and aValue is null, * this method should call unableToSetNullForKey * on the object if it implements NSKeyValueCoding, * otherwise calls unableToSetNullForKey on this class. */ static public void takeValueForKey( Object anObject, Object aValue, String aKey ) { if ( anObject == null ) return; //TODO: may need to handle "." nesting here so // that handleTakeValueForUnboundKey gets called for // for the nested object, not the parent object try { Introspector.set( anObject, aKey, aValue ); } catch ( NullPrimitiveException exc ) { if ( anObject instanceof NSKeyValueCoding ) { ((NSKeyValueCoding)anObject).unableToSetNullForKey( aKey ); } else { unableToSetNullForKey( anObject, aKey ); } } catch ( MissingPropertyException exc ) { if ( anObject instanceof NSKeyValueCoding ) { ((NSKeyValueCoding)anObject).handleTakeValueForUnboundKey( aValue, aKey ); } else { handleTakeValueForUnboundKey( anObject, aValue, aKey ); } } } /** * Returns the value for the private field that * corresponds to the specified property on * the specified object. * * This implementation currently calls valueForKey, * because java security currently prevents us from * accessing the fields of another object. */ static public Object storedValueForKey( Object anObject, String aKey ) { //TODO: this currently just calls valueForKey return valueForKey( anObject, aKey ); } /** * Sets the the private field that corresponds to the * specified property to the specified value on the * specified object. * * This implementation currently calls takeValueForKey, * because java security currently prevents us from * accessing the fields of another object. */ static public void takeStoredValueForKey( Object anObject, Object aValue, String aKey ) { //TODO: this currently just calls takeValueForKey takeValueForKey( anObject, aValue, aKey ); } /** * Called by valueForKey when the specified key is * not found on the specified object, if that object * does not implement NSKeyValueCoding. * * This implementation throws a WotonomyException. */ static public Object handleQueryWithUnboundKey( Object anObject, String aKey ) { throw new WotonomyException( "Key not found for object: " + aKey + " : " + anObject ); } /** * Called by takeValueForKey when the specified key * is not found on the specified object, if that object * does not implement NSKeyValueCoding. * * This implementation throws a WotonomyException. */ static public void handleTakeValueForUnboundKey( Object anObject, Object aValue, String aKey ) { throw new WotonomyException( "Key not found for object while setting value: " + aKey + " : " + anObject + " : " + aValue ); } /** * Called by takeValueForKey when the type of the * specified key is not allowed to be null, as is * the case with primitive types, if the specified * object does not implement NSKeyValueCoding. * * This implementation throws a WotonomyException. */ static public void unableToSetNullForKey( Object anObject, String aKey ) { throw new WotonomyException( "Tried to key on object to null: " + aKey + " : " + anObject ); } } public class Null { public Null() { super(); } public String toString() { return ""; } } } /* * $Log$ * Revision 1.2 2006/02/16 13:15:00 cgruber * Check in all sources in eclipse-friendly maven-enabled packages. * * Revision 1.3 2003/08/07 02:43:56 chochos * added NullValue, which points to a Null instance. * * Revision 1.2 2003/01/18 23:30:42 mpowers * WODisplayGroup now compiles. * * Revision 1.1 2003/01/17 14:40:49 mpowers * Adding files to fix build. * * Revision 1.2 2001/03/28 16:12:30 mpowers * Documented interface. * * Revision 1.1 2001/03/27 23:25:05 mpowers * Contributing interface, no docs yet. * * */