EOClassDescription

Inherits From:
NSObject

Declared in: EOControl/EOClassDescription.h

Class Description

The EOClassDescription class provides a mechanism for extending classes by giving them access to metadata not available in the Objective-C run-time system. This is achieved as follows:

Enterprise Objects Framework implements a default subclass of EOClassDescription, EOEntityClassDescription. EOEntityClassDescription extends the behavior of enterprise objects by deriving information about them (such as NULL constraints and referential integrity rules) from an associated model file.

How Does It Work?

As noted above, Enterprise Objects Framework implements a default subclass of EOClassDescription, EOEntityClassDescription. In the typical scenario in which an enterprise object has a corresponding model file, a particular operation (such as validating a value) results in the broadcast of an EOClassDescriptionNeeded notification. When an EOModel object receives this notification it registers the metadata (class description) for the EOEntity on which the enterprise object is based.

An enterprise object takes advantage of the metadata registered for it by using the EOClassDescription-related methods that the Framework adds to NSObject. Primary among these methods is classDescription, which returns the class description associated with the enterprise object. Through this class description the enterprise object has access to all of the information relating to its entity in an EOModel file.

In addition to methods that return information based on an enterprise object's class description, the EOClassDescription-related methods that the Framework adds to NSObject include methods that are automatically invoked when a particular operation occurs. These include validation methods and methods that are invoked whenever an enterprise object is inserted or fetched.

All of this comes together in your running application. When a user tries to perform a particular operation on an enterprise object (such as attempting to delete it), the EOEditingContext sends these validation messages to your enterprise object, which in turn (by default) forwards them to its EOClassDescription. Based on the result, the operation is either accepted or refused. For example, referential integrity constraints in your model might state that you can't delete a department object that has employees. If a user attempts to delete a department that has employees, an exception is returned and the deletion is refused.

Using EOClassDescription

For the most part, you don't need to programmatically interact with EOClassDescription. It extends the behavior of your enterprise objects transparently. However, there are two cases in which you do need to programmatically interact with it:

Overriding Methods in an Enterprise Object

As described above, Enterprise Objects Framework adds several EOClassDescription-related methods to NSObject. It's common for enterprise object classes to override the following methods to either perform validation, to assign default values (awakeFromInsertionInEditingContext:), or to provide additional initialization to newly fetched objects (awakeFromFetchInEditingContext:):

validateValue:forKey:

validateForSave

validateForDelete

validateForInsert

validateForUpdate

awakeFromInsertionInEditingContext:

awakeFromFetchInEditingContext:

For example, an enterprise object class can implement a validateForSave method that checks the values of salary and jobLevel properties before allowing the values to be saved to the database:

- (NSException *)validateForSave

{

    if (salary > 1500 && jobLevel < 2)

        return [NSException validationExceptionWithFormat:
@"The salary is too high for that position!"];

    // pass the check on to the EOClassDescription

    return [super validateForSave];  

}

For more discussion of this subject, see the chapter "Designing Enterprise Objects" in the Enterprise Objects Framework Developer's Guide, and the class specification "Extensions to NSObject."

Working with Objects That Don't Have EOModels

Although an EOModel is the most common source of an EOClassDescription for a class, it isn't the only one. Objects that don't have an EOModel can implement EOClassDescription methods directly as instance methods, and the rest of the Framework will treat them just as it does enterprise objects that have this information provided by an external EOModel.

There are a few reasons you might want to do this. First of all, if your object implements the methods entityName, attributeKeys, toOneRelationshipKeys, and toManyRelationshipKeys, EOEditingContexts can snapshot the object and thereby provide undo for it.

For example, the following code excerpt shows an implementation of attributeKeys for a Circle class:

- (NSArray *)attributeKeys {

    static NSArray *array = nil;

    if (!array)

        array = [[NSArray alloc] initWithObjects:@"radius", @"x",
@"y", @"color", nil];

    return array;

}

Secondly, you might want to implement EOClassDescription's validation or referential integrity methods to add these features to your classes.

Implementing EOClassDescription methods on a per-class basis in this way is a good alternative to creating a subclass of EOClassDescription.

Creating a Subclass of EOClassDescription

You create a subclass of EOClassDescription when you want to use an external source of information other than an EOModel to extend your objects. Another possible scenario is if you've added information to an EOModel (such as in its user dictionary) and you want that information to become part of your class description-in that case, you'd probably want to create a subclass of EOEntityClassDescription.

When you create a subclass of EOClassDescription, you only need to implement the methods that have significance for your subclass.

If you're using an external source of information other than an EOModel, you need to decide how to register class descriptions, which you do by invoking the method registerClassDescription:forClass:. You can either invoke registerClassDescription:forClass: in response to an EOClassDescriptionNeeded notification, or you can invoke it at the time you initialize your application (in other words, you can register all potential class descriptions ahead of time). The default implementation in Enterprise Objects Framework is based on responding to an EOClassDescriptionNeeded notification. When EOModel objects receive this notification, they supply a class description for the specified class by invoking registerClassDescription:forClass:.

EOEntityClassDescription

There are only three methods in EOClassDescription have meaningful implementations (that is, that don't either return nil or simply return): invalidateClassDescriptionCache, registerClassDescription:forClass:, and propagateDeleteForObject:editingContext:. The default behavior of the rest of the methods in Enterprise Objects Framework comes from the implementation in the EOClassDescription subclass EOEntityClassDescription. For more information, see the EOEntityClassDescription class specification.

The EOClassDescription's Delegate

You can assign a delegate to the EOClassDescription class. EOClassDescription sends the message shouldPropagateDeleteForObject:inEditingContext:forRelationshipKey: to its delegate when delete propagation is about to take place for a particular object. The delegate can either allow or deny the operation for a specified relationship key. For more information, see the method description for shouldPropagateDeleteForObject:inEditingContext:forRelationshipKey: .

Managing EOClassDescriptions
+ invalidateClassDescriptionCache
+ registerClassDescription:forClass:
Getting EOClassDescriptions
+ classDescriptionForClass:
+ classDescriptionForEntityName:
Allocating new object instances
- createInstanceWithEditingContext:globalID:zone:
Propagating delete
- propagateDeleteForObject:editingContext:
Returning information from the EOClassDescription
- entityName
- attributeKeys
- classDescriptionForDestinationKey:
- toManyRelationshipKeys
- toOneRelationshipKeys
- inverseForRelationshipKey:
- ownsDestinationObjectsForRelationshipKey:
- deleteRuleForRelationshipKey:
Performing validation
- validateObjectForDelete:
- validateObjectForSave:
- validateValue:forKey:
Providing default characteristics for key display
- defaultFormatterForKey:
- displayNameForKey:
Handling newly inserted and newly fetched objects
- awakeObject:fromFetchInEditingContext:
- awakeObject:fromInsertionInEditingContext:
Setting the delegate
+ delegate
+ setDelegate:
Getting an object's description
- userPresentableDescriptionForObject:

Class Methods

classDescriptionForClass:

+ (EOClassDescription *)classDescriptionForClass:(Class)aClass

Invoked by the default implementation of the NSObject method classDescription to return the EOClassDescription for aClass. It's generally not safe to use this method directly-for example, individual EOGenericRecord instances can have different class descriptions.

classDescriptionForEntityName:

+ (EOClassDescription *)classDescriptionForEntityName:(NSString *)entityName

Returns the EOClassDescription registered under entityName.

delegate

+ (id)delegate

Returns the EOClassDescription delegate.

See also: + setDelegate:

invalidateClassDescriptionCache

+ (void)invalidateClassDescriptionCache

Flushes the class description cache. Because the EOModel objects in an application supply and register EOClassDescriptions on demand, the cache continues to be repopulated as needed after you invalidate it.

You'd use this method when a provider of EOClassDescriptions (such as an EOModel) has newly become available, or is about to go away. However, you should rarely need to directly invoke this method unless you're using an external source of information other than an EOModel.

registerClassDescription:forClass:

+ (void)registerClassDescription:(EOClassDescription *)description forClass:(Class)class

Registers an EOClassDescription object for class in the EOClassDescription cache. You should rarely need to directly invoke this method unless you're using an external source of information other than an EOModel.

setDelegate:

+ (void)setDelegate:(id)delegate

Sets the EOClassDescription delegate to delegate, without retaining it.

See also: + delegate

Instance Methods

attributeKeys

- (NSArray *)attributeKeys

Overridden by subclasses to return an array of keys for attributes of the object. Attributes contain data (such as NSNumbers and NSStrings), as opposed to pointers to other enterprise objects. EOClassDescription's implementation of this method returns nil.

See also: - entityName , - toOneRelationshipKeys, - toManyRelationshipKeys

awakeObject:fromFetchInEditingContext:

- (void)awakeObject:(id)object
fromFetchInEditingContext:(EOEditingContext *)anEditingContext

Overridden by subclasses to perform standard post-fetch initialization for object in anEditingContext. EOClassDescription's implementation of this method does nothing.

awakeObject:fromInsertionInEditingContext:

- (void)awakeObject:(id)object
fromInsertionInEditingContext:(EOEditingContext *)anEditingContext

Assigns empty arrays to to-many relationship properties of newly inserted enterprise objects. Can be overridden by subclasses to propagate inserts for the newly inserted object in anEditingContext. More specifically, if object has a relationship (or relationships) that propagates the object's primary key and if no object yet exists at the destination of that relationship, subclasses should create the new object at the destination of the relationship.

classDescriptionForDestinationKey:

- (EOClassDescription *)classDescriptionForDestinationKey:(NSString *)detailKey

Overridden by subclasses to return the class description for objects at the destination of the relationship identified by detailKey. For example, the statement:

[project classDescriptionForDestinationKey:@"leader"] 

might return the class description for the Employee class. EOClassDescription's implementation of this method returns nil.

createInstanceWithEditingContext:globalID:zone:

- (id)createInstanceWithEditingContext:(EOEditingContext *)anEditingContext globalID:(EOGlobalID *)globalID zone:(NSZone *)zone

Overridden by subclasses to allocate an object of the appropriate class in anEditingContext, with globalID, in zone (in typical usage, all three of the method's arguments are nil). If the object responds to initWithEditingContext:classDescription:globalID subclasses should invoke that method, otherwise they should invoke init. Implementations of this method should return an autoreleased object. Enterprise Objects Framework uses this method to allocate new instances of objects when fetching existing enterprise objects or inserting new ones in an EODisplayGroup. EOClassDescription's implementation of this method returns nil.

defaultFormatterForKey:

- (NSFormatter *)defaultFormatterForKey:(NSString *)key

Returns the default NSFormatter to use when parsing values for assignment to key. EOClassDescription's implementation returns nil. EOEntityClassDescription's implementation returns an NSFormatter based on the Objective-C data type specified for key in the associated model file.

deleteRuleForRelationshipKey:

- (EODeleteRule)deleteRuleForRelationshipKey:(NSString *)relationshipKey

Overridden by subclasses to return a delete rule indicating how to treat the destination of the given relationship when the receiving object is deleted. For example, the class description for an Invoice object might return EODeleteRuleCascade for the relationship lineItems , because when an Invoice is removed from an external store, its line items should be removed also. EOClassDescription's implementation of this method returns the delete rule EODeleteRuleNullify. In the common case, the delete rule for an enterprise object is defined in its EOModel. For more discussion of delete rules, see the class specification "NSObject Additions."

displayNameForKey:

- (NSString *)displayNameForKey:(NSString *)key

Returns the default string to use in the user interface when displaying key. By convention, lowercase words are capitalized (for example, "revenue" becomes "Revenue"), and spaces are inserted into words with mixed case (for example, "firstName" becomes "First Name").

entityName

- (NSString *)entityName

Overridden by subclasses to return a unique type name for objects of this class. EOEntityClassDescription returns its EOEntity's name. EOClassDescription's implementation of this method returns nil.

See also: - attributeKeys , - toOneRelationshipKeys, - toManyRelationshipKeys

inverseForRelationshipKey:

- (NSString *)inverseForRelationshipKey:(NSString *)relationshipKey

Overridden by subclasses to return the name of the relationship pointing back at the receiver from the destination of the relationship specified by relationshipKey. For example, suppose an Employee object has a relationship called department to a Department object, and Department has a relationship called employees back to Employee. The statement:

[employee inverseForRelationshipKey:@"department"] 

returns the string "employees".

EOClassDescription's implementation of this method returns nil.

ownsDestinationObjectsForRelationshipKey:

- (BOOL)ownsDestinationObjectsForRelationshipKey:(NSString *)relationshipKey

Overridden by subclasses to return YES or NO to indicate whether the objects at the destination of the relationship specified by relationshipKey should be deleted if they are removed from the relationship (and not transferred to the corresponding relationship of another object). For example, an Invoice object owns its line items. If a LineItem object is removed from an Invoice it should be deleted since it can't exist outside of an Invoice. EOClassDescription's implementation of this method returns NO. In the common case, this behavior for an enterprise object is defined in its EOModel.

propagateDeleteForObject:editingContext:

- (void)propagateDeleteForObject:(id)object
editingContext:(EOEditingContext *)anEditingContext

Propagates a delete operation for object in anEditingContext, according to the delete rules specified in the object's EOModel. This method is invoked whenever a delete operation needs to be propagated, as indicated by the delete rule specified for the EOEntity's relationship key. For more discussion of delete rules, see the class specification "NSObject Additions."

See also: - deleteRuleForRelationshipKey:

toManyRelationshipKeys

- (NSArray *)toManyRelationshipKeys

Overridden by subclasses to return the keys for the to-many relationship properties of the receiver. To-many relationship properties contain arrays of pointers to other enterprise objects. EOClassDescription's implementation of this method returns nil.

See also: - entityName , - toOneRelationshipKeys, - attributeKeys

toOneRelationshipKeys

- (NSArray *)toOneRelationshipKeys

Overridden by subclasses to return the keys for the to-one relationship properties of the receiver. To-one relationship properties are pointers to other enterprise objects. EOClassDescription's implementation of this method returns nil.

See also: - entityName , - toManyRelationshipKeys, - attributeKeys

userPresentableDescriptionForObject:

- (NSString *)userPresentableDescriptionForObject:(id)anObject

Returns a short (no longer than 60 characters) description of anObject based on its data. First checks to see if anObject has an attribute called "name" and if so, it returns that attribute's value. Otherwise, checks for an attribute called "title" and returns that attribute's value. If neither of those attributes exists, this method enumerates anObject's attributeKeys and returns each attribute's value, separated by commas and with the default formatter applied for numbers and dates.

validateObjectForDelete:

- (NSException *)validateObjectForDelete:(id)object

Overridden by subclasses to determine whether it's permissible to delete the object. Subclasses should return nil if the delete operation should proceed, or an unevaluated exception containing a user-presentable (localized) error message if not. EOClassDescription's implementation of this method returns nil.

validateObjectForSave:

- (NSException *)validateObjectForSave:(id)object

Overridden by subclasses to determine whether the values being saved for the object are acceptable. Subclasses should return nil if the values are acceptable and the save operation should therefore proceed, or an unevaluated exception containing a user-presentable (localized) error message if not. EOClassDescription's implementation of this method returns nil.

validateValue:forKey:

- (NSException *)validateValue:(id *)valueP forKey:(NSString *)key

Overridden by subclasses to validate the value pointed to by valueP. Subclasses should return nil if the value is acceptable, or an unevaluated exception containing a user-presentable (localized) error message if not. Implementations can replace *valueP with a converted value (for example, an EOAttribute might convert an NSString to an NSNumber). EOClassDescription's implementation of this method returns nil.

shouldPropagateDeleteForObject:inEditingContext:forRelationshipKey:

- (BOOL)shouldPropagateDeleteForObject:(id)anObject
inEditingContext:(EOEditingContext *)anEditingContext
forRelationshipKey:(NSString *)key

Invoked from propagateDeleteForObject:editingContext: . If the delegate returns NO, it prevents anObject in anEditingContext from propagating deletion to the objects at the destination of key. This can be useful if you have a large model and a small application that only deals with a subset of the model's entities. In such a case you might want to disable delete propagation to entities that will never be accessed. You should use this method with caution, however-returning NO and not propagating deletion can lead to dangling references in your object graph.

The following notification is declared by EOClassDescription and posted by enterprise objects in your application.

EOClassDescriptionNeededForClassNotification

Notification Object
Enterprise object class
userInfo Dictionary
None
One of the EOClassDescription-related methods that Enterprise Objects Framework adds to NSObject to extend the behavior of enterprise objects is classDescription. The first time an enterprise object receives a classDescription message (for example, when changes to the object are being saved to the database), it posts EOClassDescriptionNeededForClassNotification to notify observers (by default, the associated EOModel object) that a class description is needed. The observer then locates the appropriate class description and registers it in the application.

EOClassDescriptionNeededForEntityNameNotification

Notification Object
Entity name
userInfo Dictionary
None
When classDescriptionForEntityName: is invoked for a previously unregistered entity name, this notification is broadcast with the requested entity name as the object of the notification.

Copyright © 1997, Apple Computer, Inc. All rights reserved.