Skip to content
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

Merged
merged 5 commits into from
Jul 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 104 additions & 1 deletion Frameworks/CoreData/NSAttributeDescription.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

@bbowman bbowman Jul 13, 2016

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

Copy link
Author

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)


@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)
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Jul 6, 2016

Choose a reason for hiding this comment

The 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
154 changes: 153 additions & 1 deletion Frameworks/CoreData/NSEntityDescription.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Copy link
Member

@bbowman bbowman Jul 13, 2016

Choose a reason for hiding this comment

The 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

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alas :(


In reply to: 70721690 [](ancestors = 70721690)


[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 {
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Jul 6, 2016

Choose a reason for hiding this comment

The 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."
is this something to consider? #ByDesign

Copy link
Author

Choose a reason for hiding this comment

The 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.
Every property is intended to be made readonly by the manager, so we will have to do the work there when we get one.


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
Expand Down
2 changes: 1 addition & 1 deletion Frameworks/CoreData/NSManagedObjectContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
//
//******************************************************************************

#import <StubReturn.h>
#import <CoreData/NSManagedObjectContext.h>
#import <StubReturn.h>

NSString* const NSInsertedObjectsKey = @"NSInsertedObjectsKey";
NSString* const NSUpdatedObjectsKey = @"NSUpdatedObjectsKey";
Expand Down
Loading