-
Notifications
You must be signed in to change notification settings - Fork 806
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement a CoreData xcdatamodel parser. #610
Changes from all commits
ac080e6
5a23853
b6681ab
4d48bd2
f72642c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,8 +14,111 @@ | |
// | ||
//****************************************************************************** | ||
|
||
#import <CoreData/CoreData.h> | ||
#import <Foundation/Foundation.h> | ||
#import <StubReturn.h> | ||
#import <CoreData/NSAttributeDescription.h> | ||
|
||
#import <CoreData/NSManagedObjectModel-XMLParsing.h> | ||
|
||
@interface NSAttributeDescription () { | ||
StrongId<id> _minValue; | ||
StrongId<id> _maxValue; | ||
} | ||
@end | ||
|
||
@implementation NSAttributeDescription | ||
+ (NSAttributeType)_attributeTypeFromString:(NSString*)typeString { | ||
static StrongId<NSDictionary<NSString*, NSNumber*>> _attributeTypes = @{ | ||
@"Integer 16" : @(NSInteger16AttributeType), | ||
@"Integer 32" : @(NSInteger32AttributeType), | ||
@"Integer 64" : @(NSInteger64AttributeType), | ||
@"Decimal" : @(NSDecimalAttributeType), | ||
@"Double" : @(NSDoubleAttributeType), | ||
@"Float" : @(NSFloatAttributeType), | ||
@"String" : @(NSStringAttributeType), | ||
@"Boolean" : @(NSBooleanAttributeType), | ||
@"Date" : @(NSDateAttributeType), | ||
@"Binary" : @(NSBinaryDataAttributeType), | ||
@"Transformable" : @(NSTransformableAttributeType), | ||
@"ObjectID" : @(NSObjectIDAttributeType), | ||
}; | ||
return (NSAttributeType)[[_attributeTypes objectForKey:typeString] unsignedIntegerValue]; | ||
} | ||
|
||
+ (NSObject<NSCopying>*)_convertValueString:(NSString*)valueString forAttributeType:(NSAttributeType)attributeType { | ||
switch (attributeType) { | ||
case NSInteger16AttributeType: | ||
case NSInteger32AttributeType: | ||
case NSInteger64AttributeType: | ||
return [NSNumber numberWithLongLong:[valueString longLongValue]]; | ||
case NSDecimalAttributeType: | ||
return nil; // [NSDecimalNumber decimalNumberWithString:valueString]; when implemented (VSO 7346158) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: TODO 7346158 ...... #Resolved |
||
case NSFloatAttributeType: | ||
case NSDoubleAttributeType: | ||
return [NSNumber numberWithDouble:[valueString doubleValue]]; | ||
case NSStringAttributeType: | ||
return valueString; | ||
case NSBooleanAttributeType: | ||
return [NSNumber numberWithBool:[valueString boolValue]]; | ||
default: | ||
// This type can't have a parsed default. | ||
return nil; | ||
} | ||
return nil; | ||
} | ||
|
||
- (instancetype)_initWithXMLElementName:(NSString*)entityName attributes:(NSDictionary<NSString*, NSString*>*)attributes { | ||
if (self = [super _initWithXMLElementName:entityName attributes:attributes]) { | ||
_attributeType = [[self class] _attributeTypeFromString:attributes[@"attributeType"]]; | ||
_valueTransformerName = [attributes[@"valueTransformerName"] copy]; | ||
|
||
if (_attributeType == NSDateAttributeType) { | ||
NSString* defaultDateString = attributes[@"defaultDateTimeInterval"]; | ||
NSString* minDateString = attributes[@"minDateTimeInterval"]; | ||
NSString* maxDateString = attributes[@"maxDateTimeInterval"]; | ||
|
||
_defaultValue = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:[defaultDateString longLongValue]]; | ||
_minValue = [NSDate dateWithTimeIntervalSinceReferenceDate:[minDateString longLongValue]]; | ||
_maxValue = [NSDate dateWithTimeIntervalSinceReferenceDate:[maxDateString longLongValue]]; | ||
} else { | ||
_defaultValue = [[[self class] _convertValueString:attributes[@"defaultValueString"] forAttributeType:_attributeType] copy]; | ||
_minValue = [[self class] _convertValueString:attributes[@"minValueString"] forAttributeType:_attributeType]; | ||
_maxValue = [[self class] _convertValueString:attributes[@"maxValueString"] forAttributeType:_attributeType]; | ||
} | ||
|
||
NSMutableArray<NSPredicate*>* validationPredicates = [NSMutableArray array]; | ||
if (_minValue) { | ||
[validationPredicates addObject:[NSPredicate predicateWithFormat:@"SELF >= %@", (id)_minValue]]; | ||
} | ||
|
||
if (_maxValue) { | ||
[validationPredicates addObject:[NSPredicate predicateWithFormat:@"SELF <= %@", (id)_maxValue]]; | ||
} | ||
|
||
if (validationPredicates.count > 0) { | ||
self.validationPredicates = validationPredicates; | ||
} | ||
} | ||
return self; | ||
} | ||
|
||
- (void)dealloc { | ||
[_defaultValue release]; | ||
[_valueTransformerName release]; | ||
[super dealloc]; | ||
} | ||
|
||
- (NSString*)description { | ||
return [NSString stringWithFormat:@"<%@ %p: %@,%hs%hs%hs%hs%hs default=%@ userInfo=%@>", | ||
object_getClass(self), | ||
self, | ||
self.name, | ||
self.optional ? " optional" : "", | ||
self.transient ? " transient" : "", | ||
self.indexed ? " indexed" : "", | ||
self.storedInExternalRecord ? " external" : "", | ||
self.indexedBySpotlight ? " spotlight" : "", | ||
_defaultValue, | ||
self.userInfo]; | ||
} | ||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,10 +14,162 @@ | |
// | ||
//****************************************************************************** | ||
|
||
#import <CoreData/CoreData.h> | ||
#import <Foundation/Foundation.h> | ||
#import <StubReturn.h> | ||
#import <CoreData/NSEntityDescription.h> | ||
|
||
#import <CoreData/NSManagedObjectModel-XMLParsing.h> | ||
|
||
@interface NSEntityDescription () { | ||
NSManagedObjectModel* _managedObjectModel; | ||
StrongId<NSArray<NSPropertyDescription*>> _properties; | ||
StrongId<NSArray<NSEntityDescription*>> _subentities; | ||
StrongId<NSMutableArray<NSPropertyDescription*>> _unresolvedProperties; | ||
} | ||
@property (readwrite, assign) NSEntityDescription* superentity; | ||
@property (readwrite, copy) NSDictionary* subentitiesByName; | ||
@property (readwrite, copy) NSDictionary* propertiesByName; | ||
@property (readwrite, copy) NSDictionary* attributesByName; | ||
@property (readwrite, copy) NSDictionary* relationshipsByName; | ||
@end | ||
|
||
@implementation NSEntityDescription | ||
- (instancetype)_initWithXMLElementName:(NSString*)entityName attributes:(NSDictionary<NSString*, NSString*>*)attributes { | ||
if (self = [super init]) { | ||
// Properties not herein set are set via child entity parsing. | ||
_unresolvedProperties = [NSMutableArray new]; | ||
_name = [attributes[@"name"] copy]; | ||
_managedObjectClassName = [attributes[@"representedClassName"] copy]; | ||
_renamingIdentifier = [attributes[@"elementID"] copy]; | ||
_versionHashModifier = [attributes[@"versionHashModifier"] copy]; | ||
_abstract = [attributes[@"isAbstract"] isEqualToString:@"YES"]; | ||
} | ||
return self; | ||
} | ||
|
||
- (void)dealloc { | ||
[_name release]; | ||
[_managedObjectClassName release]; | ||
[_renamingIdentifier release]; | ||
[_userInfo release]; | ||
[_compoundIndexes release]; | ||
[_uniquenessConstraints release]; | ||
[_versionHash release]; | ||
[_versionHashModifier release]; | ||
|
||
// derived properties | ||
[_subentitiesByName release]; | ||
[_propertiesByName release]; | ||
[_attributesByName release]; | ||
[_relationshipsByName release]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes me so sad. I suppose this is due to synthesized properties but :-( ... RAII would be much nicer. #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
[super dealloc]; | ||
} | ||
|
||
- (NSManagedObjectModel*)managedObjectModel { | ||
@synchronized(self) { | ||
return _managedObjectModel; | ||
} | ||
} | ||
|
||
- (void)setManagedObjectModel:(NSManagedObjectModel*)model { | ||
@synchronized(self) { | ||
_managedObjectModel = model; | ||
} | ||
} | ||
|
||
- (bool)_insertChildElement:(id<_NSCDXMLCoding>)childElement { | ||
if ([childElement isKindOfClass:[NSPropertyDescription class]]) { | ||
[_unresolvedProperties addObject:static_cast<NSPropertyDescription*>(childElement)]; | ||
return YES; | ||
} | ||
return NO; | ||
} | ||
|
||
- (NSArray<NSPropertyDescription*>*)properties { | ||
@synchronized(self) { | ||
return _properties; | ||
} | ||
} | ||
|
||
- (void)setProperties:(NSArray*)properties { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Setting the properties raises an exception if the receiver’s model has been used by an object graph manager." There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we do not have an object graph manager, we don't need that right now. In reply to: 69756520 [](ancestors = 69756520) |
||
@synchronized(self) { | ||
_properties.attach([properties copy]); | ||
|
||
NSMutableDictionary* propertiesByName = [NSMutableDictionary dictionary]; | ||
NSMutableDictionary* attributesByName = [NSMutableDictionary dictionary]; | ||
NSMutableDictionary* relationshipsByName = [NSMutableDictionary dictionary]; | ||
for (NSPropertyDescription* prop in (id)_properties) { | ||
[prop setEntity:self]; | ||
propertiesByName[prop.name] = prop; | ||
if ([prop isKindOfClass:[NSAttributeDescription class]]) { | ||
attributesByName[prop.name] = prop; | ||
} else if ([prop isKindOfClass:[NSRelationshipDescription class]]) { | ||
relationshipsByName[prop.name] = prop; | ||
} | ||
} | ||
|
||
self.propertiesByName = propertiesByName; | ||
self.attributesByName = attributesByName; | ||
self.relationshipsByName = relationshipsByName; | ||
} | ||
} | ||
|
||
- (NSArray<NSEntityDescription*>*)subentities { | ||
@synchronized(self) { | ||
return _subentities; | ||
} | ||
} | ||
|
||
- (void)setSubentities:(NSArray*)subentities { | ||
@synchronized(self) { | ||
_subentities.attach([subentities copy]); | ||
|
||
NSMutableDictionary* subentitiesByName = [NSMutableDictionary dictionary]; | ||
for (NSEntityDescription* subentity in (id)_subentities) { | ||
subentitiesByName[subentity.name] = subentity; | ||
subentity.managedObjectModel = _managedObjectModel; | ||
subentity.superentity = self; | ||
} | ||
|
||
self.subentitiesByName = subentitiesByName; | ||
} | ||
} | ||
|
||
- (void)_awakeFromXML { | ||
self.properties = _unresolvedProperties; | ||
for (NSPropertyDescription* prop in (id)_unresolvedProperties) { | ||
[prop _awakeFromXML]; | ||
} | ||
_unresolvedProperties = nil; | ||
} | ||
|
||
- (void)_resolveRelationships { | ||
// This should only be done once the model above us is stabilized. | ||
for (NSRelationshipDescription* relationship in [_relationshipsByName allValues]) { | ||
relationship.destinationEntity = [_managedObjectModel.entitiesByName objectForKey:relationship._destinationEntityName]; | ||
|
||
NSEntityDescription* inverseEntity = [_managedObjectModel.entitiesByName objectForKey:relationship._inverseEntityName]; | ||
|
||
relationship.inverseRelationship = [inverseEntity.relationshipsByName objectForKey:relationship._inverseRelationshipName]; | ||
if (relationship._inverseEntityName != nil && relationship.inverseRelationship == nil) { | ||
FAIL_FAST_MSG("NSRelationshipDescription has an inverse entity, but no inverse relationship. Broken model?"); | ||
} | ||
} | ||
} | ||
|
||
- (NSString*)description { | ||
return [NSString stringWithFormat:@"<%@ %p: %@; attrs=%@ userInfo=%@ indexes=%@ constraints=%@ subentities=%@>", | ||
object_getClass(self), | ||
self, | ||
_name, | ||
_propertiesByName, | ||
_userInfo, | ||
_compoundIndexes, | ||
_uniquenessConstraints, | ||
_subentitiesByName]; | ||
} | ||
|
||
/** | ||
@Status Stub | ||
@Notes | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the hotness was to put this in the implementation? Also curious that _ things are publicly exposed. #ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
Our coding convention usually places ivars in the class extension block in the implementation file, which this is.
They're not exposed, though. min/max value are exposed only through the validation predicates.
In reply to: 70720550 [](ancestors = 70720550)