summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java
diff options
context:
space:
mode:
authorBenjamin Culkin <scorpress@gmail.com>2024-05-19 17:56:33 -0400
committerBenjamin Culkin <scorpress@gmail.com>2024-05-19 17:56:33 -0400
commitaedc34d55462a75e329bbf342251ff6504cd117e (patch)
treebcc8f1f2352582717b484df302aeea6696b8f000 /projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java
Initial import from SVN
Diffstat (limited to 'projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java')
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java601
1 files changed, 601 insertions, 0 deletions
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java
new file mode 100644
index 0000000..cd07ebb
--- /dev/null
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOClassDescription.java
@@ -0,0 +1,601 @@
+/*
+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.control;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.wotonomy.foundation.NSArray;
+import net.wotonomy.foundation.NSMutableArray;
+import net.wotonomy.foundation.NSNotificationCenter;
+import net.wotonomy.foundation.internal.Introspector;
+import net.wotonomy.foundation.internal.WotonomyException;
+
+/**
+* EOClassDescription provides meta-information about a class
+* and is used to customize certain behaviors within wotonomy
+* and specifically within editing contexts and object stores.
+* <br><br>
+*
+* The default implementation works for most well-formed java beans,
+* but you will want to create your own subclass most typically
+* to customize the toOne and toMany relationships for your
+* class to ensure that an entire graph of objects is not
+* persisted in order to perist a single object.
+* <br><br>
+*
+* The easiest way to register your subclass is to create it
+* in the same package as the class it describes but with
+* a "ClassDesc" suffix. For example, "my.package.MyEntity"
+* would be described by "my.package.MyEntityClassDesc". <br><br>
+*
+* Note that while the interface is the same, the implementation
+* of this class differs substantially from the specification
+* in order to be more useful for java classes.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 900 $
+*/
+public class EOClassDescription
+{
+ /**
+ * A delete rule specifying that object(s) that reference
+ * this object should have those references set to null
+ * when this object is deleted.
+ */
+ public static final int DeleteRuleNullify = 0;
+
+ /**
+ * A delete rule specifying that object(s) referenced by
+ * this object should also be deleted when this object
+ * is deleted.
+ */
+ public static final int DeleteRuleCascade = 1;
+
+ /**
+ * A delete rule specicying that this object should
+ * not be allowed to be deleted if it references any
+ * object(s).
+ */
+ public static final int DeleteRuleDeny = 2;
+
+ /**
+ * A delete rule specifying that no action be taken
+ * when this object is deleted. This is the default.
+ */
+ public static final int DeleteRuleNoAction = 3;
+
+ /**
+ * Notification fired when a class description has been requested
+ * for a class. Observers should watch for this notification and
+ * call registerClassDescription so that class descriptions can be
+ * loaded on-demand.
+ * The notification's object is the requested class and the
+ * user info dictionary is null.
+ */
+ public static final String ClassDescriptionNeededForClassNotification =
+ "ClassDescriptionNeededForClassNotification";
+
+ /**
+ * Notification fired when a class description has been requested
+ * for an entity name. Observers should watch for this notification and
+ * call registerClassDescription so that class descriptions can be
+ * loaded on-demand.
+ * The notification's object is the requested name and the
+ * user info dictionary is null.
+ */
+ public static final String ClassDescriptionNeededForEntityNameNotification =
+ "ClassDescriptionNeededForEntityNameNotification";
+
+ public EOClassDescription() {
+ super();
+ }
+
+ /**
+ * Returns the class description that corresponds to the specified class.
+ * If the class description has not already been loaded, a
+ * ClassDescriptionNeededForClassNotification is posted.
+ * If the class description is still not found, the class' loader
+ * is consulted for a class in the same package and named the same as the
+ * specified class but appended with "ClassDesc", e.g. "EmployeeObjectClassDesc".
+ * If the class description is still not found, a class description is
+ * returned that uses java bean introspection to provide reasonable values.
+ */
+ public static EOClassDescription classDescriptionForClass(
+ Class aClass )
+ {
+ if ( classMap == null ) classMap = new HashMap();
+ EOClassDescription result = (EOClassDescription) classMap.get( aClass );
+ if ( result == null )
+ {
+ // if not found, post notification
+ NSNotificationCenter.defaultCenter().postNotification(
+ ClassDescriptionNeededForClassNotification, aClass, null );
+ result = (EOClassDescription) classMap.get( aClass );
+ }
+ if ( result == null )
+ {
+ // if not found, look for similarly named class
+ String className = aClass.getName() + ClassNameSuffix;
+ Class classDesc;
+ try
+ {
+ classDesc = aClass.getClassLoader().loadClass( className );
+ if ( classDesc != null )
+ {
+ result = (EOClassDescription) classDesc.newInstance();
+ registerClassDescription( result, aClass );
+ }
+ }
+ catch ( Exception exc )
+ {
+ // ignore exceptions and resume
+ }
+ }
+ if ( result == null )
+ {
+ // if not found, default to this class
+ result = new EOClassDescription( aClass );
+ registerClassDescription( result, aClass );
+ }
+ return result;
+ }
+
+ /**
+ * Returns the class description that corresponds to the specified
+ * entity name. If the class description has not already been
+ * loaded, a ClassDescriptionNeededForEntityNameNotification is posted.
+ * Returns null if no class description can be found for the entity name.
+ */
+ public static EOClassDescription classDescriptionForEntityName(
+ String aName )
+ {
+ if ( entityMap == null ) entityMap = new HashMap();
+ EOClassDescription result = (EOClassDescription) entityMap.get( aName );
+ if ( result == null )
+ {
+ // if not found, post notification
+ NSNotificationCenter.defaultCenter().postNotification(
+ ClassDescriptionNeededForEntityNameNotification, aName, null );
+ result = (EOClassDescription) entityMap.get( aName );
+ }
+ return result;
+ }
+
+ /**
+ * Clears all cached class descriptions so that new requests
+ * for class descriptions will be re-loaded on-demand.
+ */
+ public static void invalidateClassDescriptionCache()
+ {
+ classMap.clear();
+ entityMap.clear();
+ }
+
+ /**
+ * Registers the specified class descriptiong for the specified class.
+ * Nulls are not allowed - to clear the cache call invalidateClassDescriptionCache().
+ */
+ public static void registerClassDescription(
+ EOClassDescription description,
+ Class aClass )
+ {
+ if ( classMap == null ) classMap = new HashMap();
+ if ( entityMap == null ) entityMap = new HashMap();
+ description.theClass = aClass;
+ classMap.put( aClass, description );
+ entityMap.put( description.entityName(), description );
+ }
+
+/*
+ public static Object classDelegate()
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ public static void setClassDelegate(
+ Object aDelegate)
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+*/
+
+ /**
+ * The string appended to the java class name when
+ * searching the class path for an appropriate description.
+ */
+ private final static String ClassNameSuffix = "ClassDesc";
+
+ private static Map classMap;
+ private static Map entityMap;
+
+ protected Class theClass;
+ private NSMutableArray attributes;
+
+ /**
+ * Constructor may only be called by subclasses.
+ */
+ protected EOClassDescription( Class aClass )
+ {
+ theClass = aClass;
+ }
+
+ /**
+ * Returns a List of all the attributes for this class.
+ * This implementation reflects on the java class to produce
+ * a list of attributes, and then removes those keys that
+ * are returned by toOneRelationshipKeys and toManyRelationhipKeys.
+ */
+ public NSArray attributeKeys()
+ {
+ if ( attributes == null )
+ {
+ NSMutableArray readProperties = new NSMutableArray();
+ String[] read = Introspector.getReadPropertiesForClass( theClass );
+ for ( int i = 0; i < read.length; i++ )
+ {
+ readProperties.addObject( read[i] );
+ }
+
+ attributes = new NSMutableArray();
+ String[] write = Introspector.getWritePropertiesForClass( theClass );
+ for ( int i = 0; i < write.length; i++ )
+ {
+ attributes.addObject( write[i] );
+ }
+
+ // only use properties on both lists: read/write
+ attributes.retainAll( readProperties );
+
+ // remove relationship keys
+ attributes.removeAll( toOneRelationshipKeys() );
+ attributes.removeAll( toManyRelationshipKeys() );
+ }
+ return attributes;
+ }
+
+ /**
+ * This method is called when the specified object has been
+ * fetched into the specified editing context. Fetch means
+ * an object was fetched using a fetch specification - it is
+ * not the same thing as an insertion.
+ * This implementation does nothing.
+ */
+ public void awakeObjectFromFetch(
+ Object object,
+ EOEditingContext anEditingContext )
+ {
+ }
+
+ /**
+ * This method is called when the specified object has been
+ * inserted into the specified editing context. Insertion
+ * means an object was inserted by a display group - it does
+ * not mean the same thing as a fetch.
+ * This implementation does nothing.
+ */
+ public void awakeObjectFromInsertion(
+ Object object,
+ EOEditingContext anEditingContext )
+ {
+ // does nothing
+ }
+
+ /**
+ * Returns the class decription for the object referenced
+ * by the specified relationship key, or null if the
+ * class description cannot be determined for that key.
+ * This implementation returns null.
+ */
+ public EOClassDescription classDescriptionForDestinationKey(
+ String detailKey )
+ {
+ return null;
+ }
+
+ /**
+ * Creates a new instance of the class represented by this
+ * class description, registering it with the specified
+ * editing context and global id. The class description
+ * may not keep references to the newly created object.
+ * The editing context and/or the id may be null.
+ * This implementation constructs a new instance of the class
+ * and registers it with the specified editing context.
+ * If the global id is specified, the object will be populated
+ * with the appropriate data, otherwise the object will be
+ * treated as a newly inserted object.
+ * If no editing context is specified, the global id is
+ * ignored and the new instance of the class is returned.
+ */
+ public Object createInstanceWithEditingContext(
+ EOEditingContext anEditingContext,
+ EOGlobalID globalID )
+ {
+//System.out.println( "createInstanceWithEditingContext: " + this + " : " + theClass );
+ Object result = null;
+ try
+ {
+ result = theClass.newInstance();
+ if ( anEditingContext != null )
+ {
+ if ( globalID != null )
+ {
+ if ( result instanceof EOEnterpriseObject )
+ {
+ ((EOEnterpriseObject)result).awakeFromFetch( anEditingContext );
+ }
+ // register in editing context
+ anEditingContext.recordObject( result, globalID );
+ }
+ else // no global id specified
+ {
+ if ( result instanceof EOEnterpriseObject )
+ {
+ ((EOEnterpriseObject)result).awakeFromInsertion( anEditingContext );
+ }
+ // register as new object in editing context
+ anEditingContext.insertObject( result );
+ }
+ }
+ }
+ catch ( Exception exc )
+ {
+ // error instantiating
+ throw new WotonomyException( exc );
+ }
+ return result;
+ }
+
+/*
+ public NSFormatter defaultFormatterForKey(
+ String key )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+*/
+
+ /**
+ * Returns the delete rule to be used for the specified
+ * relationship key.
+ * This implementation returns DeleteRuleNoAction.
+ */
+ public int deleteRuleForRelationshipKey(
+ String relationshipKey )
+ {
+ return DeleteRuleNoAction;
+ }
+
+ /**
+ * Returns a human-readable title for the specified key.
+ * For example, displayNameForKey( "firstName" ) might
+ * return "First Name".
+ * This implementation attempts to construct such a string
+ * from the key, uppercasing the first character and
+ * inserting spaces before subsequent uppercase characters.
+ */
+ public String displayNameForKey(
+ String key )
+ {
+ if ( key == null ) return "";
+ if ( key.length() == 0 ) return "";
+
+ StringBuffer result = new StringBuffer();
+ result.append( Character.toUpperCase( key.charAt(0) ) );
+
+ char c;
+ int len = key.length();
+ for ( int i = 1; i < len; i++ )
+ {
+ c = key.charAt(i);
+ if ( Character.isUpperCase( c ) )
+ {
+ result.append( ' ' );
+ }
+ result.append( c );
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Returns a human-readable title for the class of objects
+ * that this class description represents. For example,
+ * class CustomerObject might return "Customer".
+ * This implementation returns the class name.
+ */
+ public String entityName()
+ {
+ String result = theClass.getName();
+ int index = result.lastIndexOf( "." );
+ if ( index == -1 ) return result;
+ return result.substring( index+1 );
+ }
+
+ /**
+ * Returns the fetch specification associated with this
+ * class description that corresponds to the specified name,
+ * or null if not found.
+ * This implementation returns null.
+ */
+ public EOFetchSpecification fetchSpecificationNamed(
+ String aString )
+ {
+ return null;
+ }
+
+ /**
+ * Returns the relationship key by which the object at the
+ * other end of the specified relationship key refers to
+ * this object, or null if not found.
+ * This implementation returns null.
+ */
+ public String inverseForRelationshipKey(
+ String relationshipKey )
+ {
+ return null;
+ }
+
+ public boolean ownsDestinationObjectsForRelationshipKey(
+ String relationshipKey )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Called when this object has been deleted from the
+ * specified editing context. The delete rules for this
+ * object's relationships should be executed.
+ */
+ public void propagateDeleteForObject(
+ Object object,
+ EOEditingContext anEditingContext )
+ {
+ throw new WotonomyException( "Not implemented yet." );
+ }
+
+ /**
+ * Returns a List of the "to many" relationships for
+ * this class.
+ * This implementation returns an empty list.
+ */
+ public NSArray toManyRelationshipKeys()
+ {
+ return NSArray.EmptyArray;
+ }
+
+ /**
+ * Returns a List of the "to one" relationships for
+ * this class.
+ * This implementation returns an empty list.
+ */
+ public NSArray toOneRelationshipKeys()
+ {
+ return NSArray.EmptyArray;
+ }
+
+ /**
+ * Returns a human-readable description of the specified object
+ * that should not exceed 60 characters.
+ * This implementation returns anObject.toString().
+ */
+ public String userPresentableDescriptionForObject(
+ Object anObject )
+ {
+ return anObject.toString();
+ }
+
+ /**
+ * Verifies that the specified object may be deleted.
+ * Throws an exception with a user-readable error message
+ * if the delete operation should not be allowed.
+ * This implementation does nothing.
+ */
+ public void validateObjectForDelete(
+ Object object )
+ {
+ // does nothing
+ }
+
+ /**
+ * Verifies that the specified object may be saved.
+ * Throws an exception with a user-readable error message
+ * if the save operation should not be allowed.
+ * This implementation does nothing.
+ */
+ public void validateObjectForSave(
+ Object object )
+ {
+ // does nothing
+ }
+
+ /**
+ * Validates the specified value for the specified key on this
+ * this class. Returns null if the value is acceptable, or
+ * returns an object that should be used in place of the specified
+ * object, or throws an exception with a user-readable error message
+ * if no acceptable value can be determined.
+ * This implementation returns null.
+ */
+ public Object validateValueForKey( Object value, String key)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the Java Class that this description describes.
+ * NOTE: This method is not in the specification.
+ */
+ public Class getDescribedClass()
+ {
+ return theClass;
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.3 2006/02/18 22:46:44 cgruber
+ * Add Surrogate map from .util into control's internal package, and fix imports.
+ *
+ * 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.11 2003/08/08 05:50:32 chochos
+ * theClass is protected instead of private
+ *
+ * Revision 1.10 2003/08/08 00:37:44 chochos
+ * default constructor is needed by subclasses
+ *
+ * Revision 1.9 2001/12/20 18:55:46 mpowers
+ * Hooks for awakeFromInsertion and awakeFromFetch.
+ *
+ * Revision 1.8 2001/12/01 23:51:45 mpowers
+ * Corrected createWithEditingContext.
+ *
+ * Revision 1.7 2001/11/25 22:43:38 mpowers
+ * Corrected createInstanceWithEditingContext.
+ *
+ * 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/28 14:12:23 mpowers
+ * Refactored cloning/copying into KeyValueCodingUtilities.
+ *
+ * Revision 1.3 2001/04/27 23:37:20 mpowers
+ * Now using EOClassDescription in the EODataSource class, as we should.
+ *
+ * Revision 1.2 2001/04/27 00:27:42 mpowers
+ * Partial implementation.
+ *
+ * Revision 1.1 2001/03/29 03:29:49 mpowers
+ * Now using KeyValueCoding and Support instead of Introspector.
+ *
+ *
+ */
+
+