Coverage Report - net.wotonomy.control.PropertyDataSource
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertyDataSource
0% 
0% 
3.45
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 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.control;
 20  
 
 21  
 import java.lang.reflect.Array;
 22  
 import java.lang.reflect.Method;
 23  
 import java.util.ArrayList;
 24  
 import java.util.Collection;
 25  
 import java.util.HashSet;
 26  
 import java.util.Iterator;
 27  
 import java.util.LinkedList;
 28  
 import java.util.List;
 29  
 import java.util.TreeSet;
 30  
 
 31  
 import net.wotonomy.foundation.NSArray;
 32  
 import net.wotonomy.foundation.NSMutableArray;
 33  
 import net.wotonomy.foundation.internal.Introspector;
 34  
 import net.wotonomy.foundation.internal.WotonomyException;
 35  
 
 36  
 /**
 37  
 * A data source that reads and writes to an indexed 
 38  
 * property of a java object. This class is used by 
 39  
 * MasterDetailAssociation to retreive objects from 
 40  
 * the master display group.  
 41  
 *
 42  
 * @author michael@mpowers.net
 43  
 * @author $Author: cgruber $
 44  
 * @version $Revision: 894 $
 45  
 */
 46  
 public class PropertyDataSource extends OrderedDataSource
 47  
 {
 48  
     protected Object source;
 49  
     protected String key;
 50  
     protected Class lastKnownType; // for best-guessing
 51  
     protected EOClassDescription classDesc;
 52  
     protected EOEditingContext context;
 53  
 
 54  
     /**
 55  
     * Creates a new PropertyDataSource with no editing context
 56  
     * and will try to guess the appropriate class description
 57  
     * when trying to create objects.
 58  
     */
 59  
     public PropertyDataSource()
 60  
     {
 61  0
         this( null, (EOClassDescription) null );
 62  0
     }
 63  
     
 64  
     /**
 65  
     * Creates a new PropertyDataSource that uses the specified
 66  
     * editing context, but will try to guess the appropriate
 67  
     * class description when trying to create objects.
 68  
     */
 69  
     public PropertyDataSource( EOEditingContext aContext )
 70  
     {
 71  0
         this( aContext, (EOClassDescription) null );   
 72  0
     }
 73  
 
 74  
     /**
 75  
     * Creates a new PropertyDataSource that uses the specified
 76  
     * editing context and vends objects of the specified class.
 77  
     */
 78  
     public PropertyDataSource( 
 79  
         EOEditingContext aContext, Class aClass )
 80  
     {
 81  0
         this( aContext, EOClassDescription.classDescriptionForClass( aClass ) );
 82  0
     }
 83  
 
 84  
     /**
 85  
     * Creates a new PropertyDataSource that uses the specified
 86  
     * editing context and vends objects of the specified
 87  
     * class description.
 88  
     */
 89  0
     public PropertyDataSource( 
 90  
         EOEditingContext aContext, EOClassDescription aClassDesc )
 91  0
     {
 92  0
         source = null;
 93  0
         key = null;
 94  0
         lastKnownType = null;
 95  0
         classDesc = aClassDesc;
 96  0
         context = aContext;
 97  0
     }
 98  
     
 99  
     /**
 100  
     * Provides the master object for detail display groups.
 101  
     */
 102  
     public Object source()
 103  
     {
 104  0
         return source;
 105  
     }
 106  
     
 107  
     /**
 108  
     * Allows a detail display group to set the master object.
 109  
     */
 110  
     public void setSource( Object anObject )
 111  
     {
 112  0
         source = anObject;
 113  0
     }
 114  
     
 115  
     /**
 116  
     * Provides the detail key for detail display groups.
 117  
     */
 118  
     public String key()
 119  
     {
 120  0
         return key;
 121  
     }
 122  
 
 123  
     /**
 124  
     * Allows a detail display group to set the detail key.
 125  
     */
 126  
     public void setKey( String aKey )
 127  
     {
 128  0
         key = aKey;
 129  0
     }
 130  
 
 131  
     /**
 132  
     * Inserts the specified object into this data source.
 133  
     * Calls insertObjectAtIndex and appends to the end 
 134  
     * of the list.
 135  
     */
 136  
     public void insertObject ( Object anObject )
 137  
     {
 138  0
         insertObjectAtIndex( anObject, -1 ); // trick to force to end
 139  0
     }
 140  
 
 141  
     /**
 142  
     * Inserts the specified object into this data source,
 143  
     * at the specified index.
 144  
     */
 145  
     public void insertObjectAtIndex ( 
 146  
         Object anObject, int anIndex )
 147  
     {
 148  0
         if ( source == null ) return;
 149  0
         List list = readAsList();
 150  0
         if ( anIndex == -1 ) anIndex = list.size(); // force to end
 151  0
         if ( anIndex > list.size() ) anIndex = list.size(); // force to end
 152  0
         list.add( anIndex, anObject );
 153  0
         writeAsList( list );
 154  0
     }
 155  
 
 156  
     /**
 157  
     * Deletes the specified object from this data source.
 158  
     */
 159  
     public void deleteObject ( Object anObject )
 160  
     {
 161  0
         if ( source == null ) return;
 162  0
         List list = readAsList();
 163  0
         list.remove( anObject );
 164  0
         writeAsList( list );
 165  0
     }
 166  
 
 167  
     public EOEditingContext editingContext ()
 168  
     {
 169  0
         return context;
 170  
     }
 171  
 
 172  
     /**
 173  
     * Returns a List containing the objects in this
 174  
     * data source.  
 175  
     */
 176  
     public NSArray fetchObjects ()
 177  
     {
 178  0
         if ( source == null ) return NSArray.EmptyArray;
 179  0
         return readAsList(); 
 180  
     }
 181  
 
 182  
     /**
 183  
     * Returns a new instance of this class.
 184  
     */
 185  
     public EODataSource 
 186  
         dataSourceQualifiedByKey ( String aKey )
 187  
     {
 188  
         // determine the target class desc if possible
 189  0
         EOClassDescription keyClassDesc = null;
 190  0
         if ( classDesc != null )
 191  
         {
 192  0
             keyClassDesc = classDesc.classDescriptionForDestinationKey( aKey );
 193  
         }
 194  0
         return new PropertyDataSource( editingContext(), keyClassDesc );
 195  
     }
 196  
 
 197  
     /**
 198  
     * Restricts this data source to vend those 
 199  
     * objects that are associated with the specified 
 200  
     * key on the specified object.
 201  
     */
 202  
     public void 
 203  
         qualifyWithRelationshipKey ( 
 204  
         String aKey, Object anObject )
 205  
     {
 206  0
         source = anObject;
 207  0
         key = aKey;
 208  0
     }
 209  
 
 210  
     /**
 211  
     * Returns the class description passed to the 
 212  
     * constructor, if any.  If no class description and
 213  
     * if the bound property is an indexed property,
 214  
     * the type of the array is returned, otherwise
 215  
     * this method returns null.  This method is called
 216  
     * by createObject().
 217  
     */
 218  
     public EOClassDescription 
 219  
         classDescriptionForObjects ()
 220  
     {
 221  
         // just return the class description if we have one
 222  0
         if ( classDesc != null ) return classDesc;
 223  
         
 224  
         // otherwise, try to do some guesswork
 225  0
         EOClassDescription result = null;
 226  
         
 227  
         // lastKnownType is not updated here
 228  0
         Class type = lastKnownType;
 229  
 
 230  
         // if no last known type        
 231  0
         if ( type == null )
 232  
         {
 233  
             // if source and key were specified
 234  0
             if ( ( source != null ) && ( key != null ) )
 235  
             {
 236  
                 // try to get an array type
 237  0
                 Method m = Introspector.getPropertyReadMethod(
 238  0
                     source.getClass(), key, new Class[0] );
 239  0
                 if ( m != null )
 240  
                 {
 241  0
                     Class returnType = m.getReturnType();
 242  0
                     if ( returnType.isArray() )
 243  
                     {
 244  0
                         type = returnType.getComponentType();
 245  
                     }
 246  0
                 }
 247  
                 else
 248  
                 {
 249  0
                     throw new WotonomyException( "Key does not exist for object: " + key + " : " + source );   
 250  
                 }
 251  
             }
 252  
             
 253  
             // does not update lastKnownType because
 254  
             // we prefer to get that info from a fetch.
 255  
         }
 256  
         
 257  
         // if type has been determined
 258  0
         if ( type != null ) 
 259  
         {
 260  0
             result = 
 261  0
                 EOClassDescription.classDescriptionForClass( type );
 262  
         }
 263  
         
 264  0
         return result;
 265  
     }
 266  
     
 267  
     /**
 268  
     * Calls getValue() and returns the result as a List.
 269  
     * Sets lastKnownType to the retrieved type.
 270  
     */
 271  
     protected NSMutableArray readAsList()
 272  
     {
 273  0
         Object value = getValue();
 274  0
         if ( value == null )
 275  
         {
 276  0
             return new NSMutableArray();
 277  
         }
 278  
         
 279  
         Object o;
 280  0
         NSMutableArray result = new NSMutableArray();
 281  0
         boolean hasReadType = false;
 282  0
         lastKnownType = null;
 283  
         
 284  
                 // if instance of array, convert to list 
 285  0
                 if ( value.getClass().isArray() )
 286  
                 {
 287  0
                         int count = Array.getLength( value );
 288  0
                         for ( int i = 0; i < count; i++ )
 289  
                         {
 290  0
                 o = Array.get( value, i );
 291  0
                 if ( o != null )
 292  
                 {
 293  
                     // we've already found a type
 294  0
                     if ( hasReadType ) 
 295  
                     {
 296  
                         // check that this matches the last known type
 297  0
                         if ( o.getClass() != lastKnownType )
 298  
                         {
 299  
                             // not all of the same type: set to null
 300  0
                             lastKnownType = null;
 301  0
                         }
 302  
                     }
 303  
                     else // this is the first type we've found
 304  
                     {
 305  
                         // remember it
 306  0
                         hasReadType = true;
 307  0
                         lastKnownType = o.getClass();
 308  
                     }
 309  
                 }
 310  0
                                 result.add( o );        
 311  
                         }
 312  0
                 }
 313  
                 else
 314  0
                 if ( value instanceof Collection )
 315  
                 {
 316  
                         // convert to list so we handle sets, etc.
 317  0
             Iterator i = ((Collection)value).iterator();
 318  0
             while ( i.hasNext() )
 319  
             {
 320  0
                 o = i.next();   
 321  0
                 if ( o != null )
 322  
                 {
 323  
                     // we've already found a type
 324  0
                     if ( hasReadType ) 
 325  
                     {
 326  
                         // check that this matches the last known type
 327  0
                         if ( o.getClass() != lastKnownType )
 328  
                         {
 329  
                             // not all of the same type: set to null
 330  0
                             lastKnownType = null;
 331  0
                         }
 332  
                     }
 333  
                     else // this is the first type we've found
 334  
                     {
 335  
                         // remember it
 336  0
                         hasReadType = true;
 337  0
                         lastKnownType = o.getClass();
 338  
                     }
 339  
                 }
 340  0
                 result.add( o );
 341  0
             }
 342  0
                 }
 343  
                 else
 344  
                 {
 345  0
             lastKnownType = null;
 346  0
                         throw new WotonomyException( "PropertyDataSource: " +
 347  0
                                 "bound property was not an indexed property: " + key );
 348  
                 }
 349  
 
 350  0
         return result;
 351  
     }
 352  
     
 353  
     /**
 354  
     * Converts the specified List to lastKnownType
 355  
     * and calls setValue().
 356  
     */
 357  
     protected void writeAsList( List anObjectList )
 358  
     {
 359  0
         if ( source == null )
 360  
         {
 361  0
             throw new WotonomyException( "PropertyDataSource: " +
 362  0
                 "no source object: " + key );
 363  
         }
 364  
         
 365  0
         Class c = source.getClass();
 366  0
         Method m = Introspector.getPropertyReadMethod( c, key, new Class[0] );
 367  0
         if ( m == null )
 368  
         {
 369  0
             throw new WotonomyException( "Could not read property for object: " 
 370  0
                 + key + " : " + source + " (" + c + ")" );
 371  
         }
 372  
 
 373  0
         Class returnType = m.getReturnType();
 374  
 
 375  0
         int count = anObjectList.size();
 376  0
         Object result = null;
 377  
         
 378  0
         if ( returnType.isArray() )
 379  
         {
 380  0
             Class type = returnType.getComponentType();
 381  0
             result = Array.newInstance( type, count );
 382  0
             for ( int i = 0; i < count; i++ )
 383  
             {
 384  0
                 Array.set( result, i, anObjectList.get( i ) );
 385  
             }
 386  0
         }
 387  
         else
 388  
         {
 389  0
             Collection collection = null;
 390  
             
 391  0
             if ( ! returnType.isInterface() )
 392  
             {
 393  
                 try
 394  
                 {
 395  0
                     collection = (Collection) returnType.newInstance();
 396  
                 }
 397  0
                 catch ( Exception exc )
 398  
                 {
 399  
                     // no default constructor, leave null
 400  0
                 }
 401  
             }
 402  
             
 403  
             // try to find an acceptable collections type
 404  0
             if ( collection == null )
 405  
             {
 406  0
                 if ( returnType.isAssignableFrom( NSMutableArray.class ) )
 407  
                 {
 408  0
                     collection = new NSMutableArray();
 409  0
                 }
 410  
                 else
 411  0
                 if ( returnType.isAssignableFrom( LinkedList.class ) )
 412  
                 {
 413  0
                     collection = new LinkedList();
 414  0
                 }
 415  
                 else
 416  0
                 if ( returnType.isAssignableFrom( ArrayList.class ) )
 417  
                 {
 418  0
                     collection = new ArrayList();
 419  0
                 }
 420  
                 else
 421  0
                 if ( returnType.isAssignableFrom( HashSet.class ) )
 422  
                 {
 423  0
                     collection = new HashSet();
 424  0
                 }
 425  
                 else
 426  0
                 if ( returnType.isAssignableFrom( TreeSet.class ) )
 427  
                 {
 428  0
                     collection = new TreeSet();
 429  
                 }
 430  
             }
 431  
             
 432  0
             if ( collection == null )
 433  
             {
 434  0
                 throw new WotonomyException( "Could not create a collection of type: " + returnType );   
 435  
             }
 436  
             
 437  0
             collection.addAll( anObjectList );
 438  0
             result = collection;
 439  
         }
 440  
         
 441  0
         setValue( result );
 442  0
     }
 443  
     
 444  
     /**
 445  
     * Returns the value of the indexed property
 446  
     * specified by qualifyWithRelationshipKey.
 447  
     */
 448  
     protected Object getValue()
 449  
     {
 450  0
         if ( source instanceof EOKeyValueCoding )
 451  
         {
 452  0
             return ((EOKeyValueCoding)source).valueForKey( key );   
 453  
         }
 454  0
         return EOKeyValueCodingSupport.valueForKey( source, key );
 455  
     }
 456  
     
 457  
     /**
 458  
     * Sets the value of the indexed property
 459  
     * specified by qualifyWithRelationshipKey.
 460  
     * The argument is assumed to be of appropriate
 461  
     * type for the property.  EOObserverCenter is
 462  
     * notified that the object will change.
 463  
     */
 464  
     protected void setValue( Object aValue )
 465  
     {
 466  0
         EOClassDescription sourceDesc = 
 467  0
             EOClassDescription.classDescriptionForClass( source.getClass() );
 468  
             
 469  
             
 470  
         // if we're not editing a relationship (?)
 471  
 //        if ( ! sourceDesc.toManyRelationshipKeys().containsObject( key ) )
 472  
         {
 473  
             // mark the parent as changed
 474  0
             EOObserverCenter.notifyObserversObjectWillChange( source );
 475  
         }
 476  
 
 477  
         
 478  0
         if ( source instanceof EOKeyValueCoding )
 479  
         {
 480  0
             ((EOKeyValueCoding)source).takeValueForKey( aValue, key );   
 481  0
         }
 482  
         else
 483  
         {
 484  0
             EOKeyValueCodingSupport.takeValueForKey( source, aValue, key );
 485  
         }
 486  0
     }
 487  
     
 488  
 }
 489  
 
 490  
 /*
 491  
  * $Log$
 492  
  * Revision 1.2  2006/02/16 16:47:14  cgruber
 493  
  * Move some classes in to "internal" packages and re-work imports, etc.
 494  
  *
 495  
  * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
 496  
  *
 497  
  * Revision 1.1  2006/02/16 13:19:57  cgruber
 498  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 499  
  *
 500  
  * Revision 1.14  2003/01/18 23:30:42  mpowers
 501  
  * WODisplayGroup now compiles.
 502  
  *
 503  
  * Revision 1.13  2002/10/24 21:15:36  mpowers
 504  
  * New implementations of NSArray and subclasses.
 505  
  *
 506  
  * Revision 1.12  2002/10/24 18:18:12  mpowers
 507  
  * NSArray's are now considered read-only, so we can return our internal
 508  
  * representation to reduce unnecessary object allocation.
 509  
  *
 510  
  * Revision 1.11  2002/04/15 21:55:33  mpowers
 511  
  * Catching a condition where the get may not return the value passed to set.
 512  
  *
 513  
  * Revision 1.10  2002/03/08 23:20:37  mpowers
 514  
  * insertObject now calls insertObjectAtIndex.
 515  
  *
 516  
  * Revision 1.9  2001/06/05 19:10:41  mpowers
 517  
  * Better handling of null properties.
 518  
  *
 519  
  * Revision 1.8  2001/05/21 14:03:35  mpowers
 520  
  * Added a convenience constructor for java classes.
 521  
  *
 522  
  * Revision 1.7  2001/04/30 13:15:24  mpowers
 523  
  * Child contexts re-initializing objects invalidated in parent now
 524  
  * propery transpose relationships.
 525  
  *
 526  
  * Revision 1.6  2001/04/29 02:29:31  mpowers
 527  
  * Debugging relationship faulting.
 528  
  *
 529  
  * Revision 1.5  2001/04/28 22:17:51  mpowers
 530  
  * Revised PropertyDataSource to be EOClassDescription-aware.
 531  
  *
 532  
  * Revision 1.4  2001/04/27 23:37:20  mpowers
 533  
  * Now using EOClassDescription in the EODataSource class, as we should.
 534  
  *
 535  
  * Revision 1.3  2001/03/29 03:29:49  mpowers
 536  
  * Now using KeyValueCoding and Support instead of Introspector.
 537  
  *
 538  
  * Revision 1.2  2001/01/24 14:10:53  mpowers
 539  
  * Contributing OrderedDataSource, and PropertyDataSource extends it.
 540  
  *
 541  
  * Revision 1.1.1.1  2000/12/21 15:46:50  mpowers
 542  
  * Contributing wotonomy.
 543  
  *
 544  
  * Revision 1.3  2000/12/20 16:25:35  michael
 545  
  * Added log to all files.
 546  
  *
 547  
  *
 548  
  */
 549