Reimplement inverseMapping so that it no longer sucks. fixes #864, #1058

* Inversing non intelligently walks the mapping graph, ensuring that cyclic graphs are resolved properly
* Copy all attributes from the parent to the inverse mapping (date formatters, etc)
This commit is contained in:
Blake Watters
2012-12-22 16:36:55 -05:00
parent b78deb3eed
commit 7984d4f2ac
3 changed files with 94 additions and 33 deletions

View File

@@ -18,6 +18,7 @@
// limitations under the License.
//
#import <CoreFoundation/CoreFoundation.h>
#import "RKObjectMapping.h"
#import "RKRelationshipMapping.h"
#import "RKPropertyInspector.h"
@@ -30,13 +31,71 @@ typedef NSString * (^RKSourceToDesinationKeyTransformationBlock)(RKObjectMapping
// Constants
NSString * const RKObjectMappingNestingAttributeKeyName = @"<RK_NESTING_ATTRIBUTE>";
static NSUInteger RKObjectMappingMaximumInverseMappingRecursionDepth = 100;
// Private declaration
NSDate *RKDateFromStringWithFormatters(NSString *dateString, NSArray *formatters);
static RKSourceToDesinationKeyTransformationBlock defaultSourceToDestinationKeyTransformationBlock = nil;
@interface RKObjectMapping (Copying)
- (void)copyPropertiesFromMapping:(RKObjectMapping *)mapping;
@end
@interface RKMappingInverter : NSObject
@property (nonatomic, strong) RKObjectMapping *mapping;
@property (nonatomic, strong) NSMutableDictionary *invertedMappings;
- (id)initWithMapping:(RKObjectMapping *)mapping;
- (RKObjectMapping *)inverseMapping;
@end
@implementation RKMappingInverter
- (id)initWithMapping:(RKObjectMapping *)mapping
{
self = [self init];
if (self) {
self.mapping = mapping;
self.invertedMappings = [NSMutableDictionary dictionary];
}
return self;
}
- (RKObjectMapping *)invertMapping:(RKObjectMapping *)mapping
{
// Use an NSValue to obtain a non-copied key into our inversed mappings dictionary
NSValue *dictionaryKey = [NSValue valueWithNonretainedObject:mapping];
RKObjectMapping *inverseMapping = [self.invertedMappings objectForKey:dictionaryKey];
if (inverseMapping) return inverseMapping;
inverseMapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[self.invertedMappings setObject:inverseMapping forKey:dictionaryKey];
[inverseMapping copyPropertiesFromMapping:mapping];
for (RKAttributeMapping *attributeMapping in mapping.attributeMappings) {
[inverseMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:attributeMapping.destinationKeyPath toKeyPath:attributeMapping.sourceKeyPath]];
}
for (RKRelationshipMapping *relationshipMapping in mapping.relationshipMappings) {
RKObjectMapping *mapping = (RKObjectMapping *) relationshipMapping.mapping;
if (! [mapping isKindOfClass:[RKObjectMapping class]]) {
RKLogWarning(@"Unable to generate inverse mapping for relationship '%@': %@ relationships cannot be inversed.", relationshipMapping.sourceKeyPath, NSStringFromClass([mapping class]));
continue;
}
RKMapping *inverseRelationshipMapping = [self invertMapping:mapping];
if (inverseRelationshipMapping) [inverseMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:relationshipMapping.destinationKeyPath toKeyPath:relationshipMapping.sourceKeyPath withMapping:inverseRelationshipMapping]];
}
return inverseMapping;
}
- (RKObjectMapping *)inverseMapping
{
return [self invertMapping:self.mapping];
}
@end
@interface RKPropertyMapping ()
@property (nonatomic, weak, readwrite) RKObjectMapping *objectMapping;
@end
@@ -77,21 +136,26 @@ static RKSourceToDesinationKeyTransformationBlock defaultSourceToDestinationKeyT
return self;
}
- (void)copyPropertiesFromMapping:(RKObjectMapping *)mapping
{
self.setDefaultValueForMissingAttributes = mapping.setDefaultValueForMissingAttributes;
self.setNilForMissingRelationships = mapping.setNilForMissingRelationships;
self.forceCollectionMapping = mapping.forceCollectionMapping;
self.performKeyValueValidation = mapping.performKeyValueValidation;
self.dateFormatters = mapping.dateFormatters;
self.preferredDateFormatter = mapping.preferredDateFormatter;
self.sourceToDestinationKeyTransformationBlock = self.sourceToDestinationKeyTransformationBlock;
}
- (id)copyWithZone:(NSZone *)zone
{
RKObjectMapping *copy = [[[self class] allocWithZone:zone] init];
copy.objectClass = self.objectClass;
copy.setDefaultValueForMissingAttributes = self.setDefaultValueForMissingAttributes;
copy.setNilForMissingRelationships = self.setNilForMissingRelationships;
copy.forceCollectionMapping = self.forceCollectionMapping;
copy.performKeyValueValidation = self.performKeyValueValidation;
copy.dateFormatters = self.dateFormatters;
copy.preferredDateFormatter = self.preferredDateFormatter;
[copy copyPropertiesFromMapping:self];
copy.mutablePropertyMappings = [NSMutableArray new];
copy.sourceToDestinationKeyTransformationBlock = self.sourceToDestinationKeyTransformationBlock;
for (RKPropertyMapping *propertyMapping in self.propertyMappings) {
[copy addPropertyMapping:propertyMapping];
[copy addPropertyMapping:[propertyMapping copy]];
}
return copy;
@@ -264,29 +328,10 @@ static RKSourceToDesinationKeyTransformationBlock defaultSourceToDestinationKeyT
}
}
- (instancetype)inverseMappingAtDepth:(NSInteger)depth
{
NSAssert(depth < RKObjectMappingMaximumInverseMappingRecursionDepth, @"Exceeded max recursion level in inverseMapping. This is likely due to a loop in the serialization graph. To break this loop, specify one-way relationships by setting serialize to NO in mapKeyPath:toRelationship:withObjectMapping:serialize:");
RKObjectMapping *inverseMapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
for (RKAttributeMapping *attributeMapping in self.attributeMappings) {
[inverseMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:attributeMapping.destinationKeyPath toKeyPath:attributeMapping.sourceKeyPath]];
}
for (RKRelationshipMapping *relationshipMapping in self.relationshipMappings) {
RKMapping *mapping = relationshipMapping.mapping;
if (! [mapping isKindOfClass:[RKObjectMapping class]]) {
RKLogWarning(@"Unable to generate inverse mapping for relationship '%@': %@ relationships cannot be inversed.", relationshipMapping.sourceKeyPath, NSStringFromClass([mapping class]));
continue;
}
[inverseMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:relationshipMapping.destinationKeyPath toKeyPath:relationshipMapping.sourceKeyPath withMapping:[(RKObjectMapping *)mapping inverseMappingAtDepth:depth+1]]];
}
return inverseMapping;
}
- (instancetype)inverseMapping
{
return [self inverseMappingAtDepth:0];
RKMappingInverter *mappingInverter = [[RKMappingInverter alloc] initWithMapping:self];
return [mappingInverter inverseMapping];
}
- (void)addAttributeMappingFromKeyOfRepresentationToAttribute:(NSString *)attributeName