/* 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 * achieve 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(); } @Override 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. * * */