summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/PropertyDataSource.java
diff options
context:
space:
mode:
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.java549
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.
+ *
+ *
+ */
+