diff options
Diffstat (limited to 'projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/PropertyDataSource.java')
| -rw-r--r-- | projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/PropertyDataSource.java | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/PropertyDataSource.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/PropertyDataSource.java new file mode 100644 index 0000000..76f7219 --- /dev/null +++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/PropertyDataSource.java @@ -0,0 +1,549 @@ +/* +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; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.TreeSet; + +import net.wotonomy.foundation.NSArray; +import net.wotonomy.foundation.NSMutableArray; +import net.wotonomy.foundation.internal.Introspector; +import net.wotonomy.foundation.internal.WotonomyException; + +/** +* A data source that reads and writes to an indexed +* property of a java object. This class is used by +* MasterDetailAssociation to retreive objects from +* the master display group. +* +* @author michael@mpowers.net +* @author $Author: cgruber $ +* @version $Revision: 894 $ +*/ +public class PropertyDataSource extends OrderedDataSource +{ + protected Object source; + protected String key; + protected Class lastKnownType; // for best-guessing + protected EOClassDescription classDesc; + protected EOEditingContext context; + + /** + * Creates a new PropertyDataSource with no editing context + * and will try to guess the appropriate class description + * when trying to create objects. + */ + public PropertyDataSource() + { + this( null, (EOClassDescription) null ); + } + + /** + * Creates a new PropertyDataSource that uses the specified + * editing context, but will try to guess the appropriate + * class description when trying to create objects. + */ + public PropertyDataSource( EOEditingContext aContext ) + { + this( aContext, (EOClassDescription) null ); + } + + /** + * Creates a new PropertyDataSource that uses the specified + * editing context and vends objects of the specified class. + */ + public PropertyDataSource( + EOEditingContext aContext, Class aClass ) + { + this( aContext, EOClassDescription.classDescriptionForClass( aClass ) ); + } + + /** + * Creates a new PropertyDataSource that uses the specified + * editing context and vends objects of the specified + * class description. + */ + public PropertyDataSource( + EOEditingContext aContext, EOClassDescription aClassDesc ) + { + source = null; + key = null; + lastKnownType = null; + classDesc = aClassDesc; + context = aContext; + } + + /** + * Provides the master object for detail display groups. + */ + public Object source() + { + return source; + } + + /** + * Allows a detail display group to set the master object. + */ + public void setSource( Object anObject ) + { + source = anObject; + } + + /** + * Provides the detail key for detail display groups. + */ + public String key() + { + return key; + } + + /** + * Allows a detail display group to set the detail key. + */ + public void setKey( String aKey ) + { + key = aKey; + } + + /** + * Inserts the specified object into this data source. + * Calls insertObjectAtIndex and appends to the end + * of the list. + */ + public void insertObject ( Object anObject ) + { + insertObjectAtIndex( anObject, -1 ); // trick to force to end + } + + /** + * Inserts the specified object into this data source, + * at the specified index. + */ + public void insertObjectAtIndex ( + Object anObject, int anIndex ) + { + if ( source == null ) return; + List list = readAsList(); + if ( anIndex == -1 ) anIndex = list.size(); // force to end + if ( anIndex > list.size() ) anIndex = list.size(); // force to end + list.add( anIndex, anObject ); + writeAsList( list ); + } + + /** + * Deletes the specified object from this data source. + */ + public void deleteObject ( Object anObject ) + { + if ( source == null ) return; + List list = readAsList(); + list.remove( anObject ); + writeAsList( list ); + } + + public EOEditingContext editingContext () + { + return context; + } + + /** + * Returns a List containing the objects in this + * data source. + */ + public NSArray fetchObjects () + { + if ( source == null ) return NSArray.EmptyArray; + return readAsList(); + } + + /** + * Returns a new instance of this class. + */ + public EODataSource + dataSourceQualifiedByKey ( String aKey ) + { + // determine the target class desc if possible + EOClassDescription keyClassDesc = null; + if ( classDesc != null ) + { + keyClassDesc = classDesc.classDescriptionForDestinationKey( aKey ); + } + return new PropertyDataSource( editingContext(), keyClassDesc ); + } + + /** + * Restricts this data source to vend those + * objects that are associated with the specified + * key on the specified object. + */ + public void + qualifyWithRelationshipKey ( + String aKey, Object anObject ) + { + source = anObject; + key = aKey; + } + + /** + * Returns the class description passed to the + * constructor, if any. If no class description and + * if the bound property is an indexed property, + * the type of the array is returned, otherwise + * this method returns null. This method is called + * by createObject(). + */ + public EOClassDescription + classDescriptionForObjects () + { + // just return the class description if we have one + if ( classDesc != null ) return classDesc; + + // otherwise, try to do some guesswork + EOClassDescription result = null; + + // lastKnownType is not updated here + Class type = lastKnownType; + + // if no last known type + if ( type == null ) + { + // if source and key were specified + if ( ( source != null ) && ( key != null ) ) + { + // try to get an array type + Method m = Introspector.getPropertyReadMethod( + source.getClass(), key, new Class[0] ); + if ( m != null ) + { + Class returnType = m.getReturnType(); + if ( returnType.isArray() ) + { + type = returnType.getComponentType(); + } + } + else + { + throw new WotonomyException( "Key does not exist for object: " + key + " : " + source ); + } + } + + // does not update lastKnownType because + // we prefer to get that info from a fetch. + } + + // if type has been determined + if ( type != null ) + { + result = + EOClassDescription.classDescriptionForClass( type ); + } + + return result; + } + + /** + * Calls getValue() and returns the result as a List. + * Sets lastKnownType to the retrieved type. + */ + protected NSMutableArray readAsList() + { + Object value = getValue(); + if ( value == null ) + { + return new NSMutableArray(); + } + + Object o; + NSMutableArray result = new NSMutableArray(); + boolean hasReadType = false; + lastKnownType = null; + + // if instance of array, convert to list + if ( value.getClass().isArray() ) + { + int count = Array.getLength( value ); + for ( int i = 0; i < count; i++ ) + { + o = Array.get( value, i ); + if ( o != null ) + { + // we've already found a type + if ( hasReadType ) + { + // check that this matches the last known type + if ( o.getClass() != lastKnownType ) + { + // not all of the same type: set to null + lastKnownType = null; + } + } + else // this is the first type we've found + { + // remember it + hasReadType = true; + lastKnownType = o.getClass(); + } + } + result.add( o ); + } + } + else + if ( value instanceof Collection ) + { + // convert to list so we handle sets, etc. + Iterator i = ((Collection)value).iterator(); + while ( i.hasNext() ) + { + o = i.next(); + if ( o != null ) + { + // we've already found a type + if ( hasReadType ) + { + // check that this matches the last known type + if ( o.getClass() != lastKnownType ) + { + // not all of the same type: set to null + lastKnownType = null; + } + } + else // this is the first type we've found + { + // remember it + hasReadType = true; + lastKnownType = o.getClass(); + } + } + result.add( o ); + } + } + else + { + lastKnownType = null; + throw new WotonomyException( "PropertyDataSource: " + + "bound property was not an indexed property: " + key ); + } + + return result; + } + + /** + * Converts the specified List to lastKnownType + * and calls setValue(). + */ + protected void writeAsList( List anObjectList ) + { + if ( source == null ) + { + throw new WotonomyException( "PropertyDataSource: " + + "no source object: " + key ); + } + + Class c = source.getClass(); + Method m = Introspector.getPropertyReadMethod( c, key, new Class[0] ); + if ( m == null ) + { + throw new WotonomyException( "Could not read property for object: " + + key + " : " + source + " (" + c + ")" ); + } + + Class returnType = m.getReturnType(); + + int count = anObjectList.size(); + Object result = null; + + if ( returnType.isArray() ) + { + Class type = returnType.getComponentType(); + result = Array.newInstance( type, count ); + for ( int i = 0; i < count; i++ ) + { + Array.set( result, i, anObjectList.get( i ) ); + } + } + else + { + Collection collection = null; + + if ( ! returnType.isInterface() ) + { + try + { + collection = (Collection) returnType.newInstance(); + } + catch ( Exception exc ) + { + // no default constructor, leave null + } + } + + // try to find an acceptable collections type + if ( collection == null ) + { + if ( returnType.isAssignableFrom( NSMutableArray.class ) ) + { + collection = new NSMutableArray(); + } + else + if ( returnType.isAssignableFrom( LinkedList.class ) ) + { + collection = new LinkedList(); + } + else + if ( returnType.isAssignableFrom( ArrayList.class ) ) + { + collection = new ArrayList(); + } + else + if ( returnType.isAssignableFrom( HashSet.class ) ) + { + collection = new HashSet(); + } + else + if ( returnType.isAssignableFrom( TreeSet.class ) ) + { + collection = new TreeSet(); + } + } + + if ( collection == null ) + { + throw new WotonomyException( "Could not create a collection of type: " + returnType ); + } + + collection.addAll( anObjectList ); + result = collection; + } + + setValue( result ); + } + + /** + * Returns the value of the indexed property + * specified by qualifyWithRelationshipKey. + */ + protected Object getValue() + { + if ( source instanceof EOKeyValueCoding ) + { + return ((EOKeyValueCoding)source).valueForKey( key ); + } + return EOKeyValueCodingSupport.valueForKey( source, key ); + } + + /** + * Sets the value of the indexed property + * specified by qualifyWithRelationshipKey. + * The argument is assumed to be of appropriate + * type for the property. EOObserverCenter is + * notified that the object will change. + */ + protected void setValue( Object aValue ) + { + EOClassDescription sourceDesc = + EOClassDescription.classDescriptionForClass( source.getClass() ); + + + // if we're not editing a relationship (?) +// if ( ! sourceDesc.toManyRelationshipKeys().containsObject( key ) ) + { + // mark the parent as changed + EOObserverCenter.notifyObserversObjectWillChange( source ); + } + + + if ( source instanceof EOKeyValueCoding ) + { + ((EOKeyValueCoding)source).takeValueForKey( aValue, key ); + } + else + { + EOKeyValueCodingSupport.takeValueForKey( source, aValue, key ); + } + } + +} + +/* + * $Log$ + * Revision 1.2 2006/02/16 16:47:14 cgruber + * Move some classes in to "internal" packages and re-work imports, etc. + * + * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions. + * + * Revision 1.1 2006/02/16 13:19:57 cgruber + * Check in all sources in eclipse-friendly maven-enabled packages. + * + * Revision 1.14 2003/01/18 23:30:42 mpowers + * WODisplayGroup now compiles. + * + * Revision 1.13 2002/10/24 21:15:36 mpowers + * New implementations of NSArray and subclasses. + * + * Revision 1.12 2002/10/24 18:18:12 mpowers + * NSArray's are now considered read-only, so we can return our internal + * representation to reduce unnecessary object allocation. + * + * Revision 1.11 2002/04/15 21:55:33 mpowers + * Catching a condition where the get may not return the value passed to set. + * + * Revision 1.10 2002/03/08 23:20:37 mpowers + * insertObject now calls insertObjectAtIndex. + * + * Revision 1.9 2001/06/05 19:10:41 mpowers + * Better handling of null properties. + * + * Revision 1.8 2001/05/21 14:03:35 mpowers + * Added a convenience constructor for java classes. + * + * Revision 1.7 2001/04/30 13:15:24 mpowers + * Child contexts re-initializing objects invalidated in parent now + * propery transpose relationships. + * + * Revision 1.6 2001/04/29 02:29:31 mpowers + * Debugging relationship faulting. + * + * Revision 1.5 2001/04/28 22:17:51 mpowers + * Revised PropertyDataSource to be EOClassDescription-aware. + * + * Revision 1.4 2001/04/27 23:37:20 mpowers + * Now using EOClassDescription in the EODataSource class, as we should. + * + * Revision 1.3 2001/03/29 03:29:49 mpowers + * Now using KeyValueCoding and Support instead of Introspector. + * + * Revision 1.2 2001/01/24 14:10:53 mpowers + * Contributing OrderedDataSource, and PropertyDataSource extends it. + * + * Revision 1.1.1.1 2000/12/21 15:46:50 mpowers + * Contributing wotonomy. + * + * Revision 1.3 2000/12/20 16:25:35 michael + * Added log to all files. + * + * + */ + |
