Files
RestKit/Code/ObjectMapping/RKObjectMappingProvider.m
Blake Watters 6abbb34ef0 Introduced mapping contexts support.
Extended RKObjectMappingProvider to store collections of object mappings for different use cases. The framework
now stores object mappings, serialization mappings, an error mapping and a pagination mapping using the context
support. Contexts can be added to the provider via method calls or extension via a category.
2012-01-20 10:21:19 -05:00

236 lines
10 KiB
Objective-C

//
// RKObjectMappingProvider.m
// RestKit
//
// Created by Jeremy Ellison on 5/6/11.
// Copyright 2011 RestKit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "RKObjectMappingProvider.h"
@implementation RKObjectMappingProvider
+ (RKObjectMappingProvider *)mappingProvider {
return [[self new] autorelease];
}
- (id)init {
self = [super init];
if (self) {
mappingContexts = [NSMutableDictionary new];
[self initializeContext:RKObjectMappingProviderContextObjectsByKeyPath withValue:[NSMutableDictionary dictionary]];
[self initializeContext:RKObjectMappingProviderContextObjectsByType withValue:[NSMutableArray array]];
[self initializeContext:RKObjectMappingProviderContextObjectsByURL withValue:[NSMutableDictionary dictionary]];
[self initializeContext:RKObjectMappingProviderContextSerialization withValue:[NSMutableDictionary dictionary]];
[self initializeContext:RKObjectMappingProviderContextErrors withValue:[NSMutableArray array]];
}
return self;
}
- (void)dealloc {
[mappingContexts release];
[super dealloc];
}
- (void)setObjectMapping:(id<RKObjectMappingDefinition>)objectOrDynamicMapping forKeyPath:(NSString *)keyPath {
[self setMapping:objectOrDynamicMapping forKeyPath:keyPath context:RKObjectMappingProviderContextObjectsByKeyPath];
}
- (void)removeObjectMappingForKeyPath:(NSString *)keyPath {
[self removeMappingForKeyPath:keyPath context:RKObjectMappingProviderContextObjectsByKeyPath];
}
- (id<RKObjectMappingDefinition>)objectMappingForKeyPath:(NSString *)keyPath {
return [self mappingForKeyPath:keyPath context:RKObjectMappingProviderContextObjectsByKeyPath];
}
- (void)setSerializationMapping:(RKObjectMapping *)mapping forClass:(Class)objectClass {
[self setMapping:mapping forKeyPath:NSStringFromClass(objectClass) context:RKObjectMappingProviderContextSerialization];
}
- (RKObjectMapping *)serializationMappingForClass:(Class)objectClass {
return [self mappingForKeyPath:NSStringFromClass(objectClass) context:RKObjectMappingProviderContextSerialization];
}
- (NSDictionary*)objectMappingsByKeyPath {
return [NSDictionary dictionaryWithDictionary:(NSDictionary *) [self valueForContext:RKObjectMappingProviderContextObjectsByKeyPath]];
}
- (void)registerObjectMapping:(RKObjectMapping *)objectMapping withRootKeyPath:(NSString *)keyPath {
// TODO: Should generate logs
objectMapping.rootKeyPath = keyPath;
[self setMapping:objectMapping forKeyPath:keyPath];
RKObjectMapping* inverseMapping = [objectMapping inverseMapping];
inverseMapping.rootKeyPath = keyPath;
[self setSerializationMapping:inverseMapping forClass:objectMapping.objectClass];
}
- (void)addObjectMapping:(RKObjectMapping *)objectMapping {
[self addMapping:objectMapping context:RKObjectMappingProviderContextObjectsByType];
}
- (NSArray *)objectMappingsForClass:(Class)theClass {
NSMutableArray *mappings = [NSMutableArray array];
NSArray *mappingByType = [self valueForContext:RKObjectMappingProviderContextObjectsByType];
NSArray *mappingByKeyPath = [[self valueForContext:RKObjectMappingProviderContextObjectsByKeyPath] allValues];
NSArray *mappingsToSearch = [[NSArray arrayWithArray:mappingByType] arrayByAddingObjectsFromArray:mappingByKeyPath];
for (NSObject <RKObjectMappingDefinition> *candidateMapping in mappingsToSearch) {
if ( ![candidateMapping respondsToSelector:@selector(objectClass)] || [mappings containsObject:candidateMapping])
continue;
Class mappedClass = [candidateMapping performSelector:@selector(objectClass)];
if (mappedClass && [NSStringFromClass(mappedClass) isEqualToString:NSStringFromClass(theClass)]) {
[mappings addObject:candidateMapping];
}
}
return [NSArray arrayWithArray:mappings];
}
- (RKObjectMapping *)objectMappingForClass:(Class)theClass {
NSArray* objectMappings = [self objectMappingsForClass:theClass];
return ([objectMappings count] > 0) ? [objectMappings objectAtIndex:0] : nil;
}
#pragma mark - Error Mappings
- (RKObjectMapping *)errorMapping {
return [self mappingForContext:RKObjectMappingProviderContextErrors];
}
- (void)setErrorMapping:(RKObjectMapping *)errorMapping {
[self setMapping:errorMapping context:RKObjectMappingProviderContextErrors];
}
#pragma mark - Pagination Mapping
- (RKObjectMapping *)paginationMapping {
return [self mappingForContext:RKObjectMappingProviderContextPagination];
}
- (void)setPaginationMapping:(RKObjectMapping *)paginationMapping {
[self setMapping:paginationMapping context:RKObjectMappingProviderContextPagination];
}
#pragma mark - Mapping Context Primitives
- (void)initializeContext:(RKObjectMappingProviderContext)context withValue:(id)value {
NSAssert([self valueForContext:context] == nil, @"Attempt to reinitialized an existing mapping provider context.");
[self setValue:value forContext:context];
}
- (id)valueForContext:(RKObjectMappingProviderContext)context {
NSNumber *contextNumber = [NSNumber numberWithInteger:context];
return [mappingContexts objectForKey:contextNumber];
}
- (void)setValue:(id)value forContext:(RKObjectMappingProviderContext)context {
NSNumber *contextNumber = [NSNumber numberWithInteger:context];
[mappingContexts setObject:value forKey:contextNumber];
}
- (void)assertStorageForContext:(RKObjectMappingProviderContext)context isKindOfClass:(Class)theClass {
id contextValue = [self valueForContext:context];
NSAssert([contextValue isKindOfClass:theClass], @"Storage type mismatch for context %d: expected a %@, got %@.", context, theClass, [contextValue class]);
}
- (void)setMapping:(id<RKObjectMappingDefinition>)mapping context:(RKObjectMappingProviderContext)context {
NSNumber *contextNumber = [NSNumber numberWithInteger:context];
[mappingContexts setObject:mapping forKey:contextNumber];
}
- (id<RKObjectMappingDefinition>)mappingForContext:(RKObjectMappingProviderContext)context {
id contextValue = [self valueForContext:context];
if (contextValue == nil) return nil;
Protocol *protocol = @protocol(RKObjectMappingDefinition);
NSAssert([contextValue conformsToProtocol:protocol], @"Storage type mismatch for context %d: expected a %@, got %@.", context, protocol, [contextValue class]);
return contextValue;
}
- (NSArray *)mappingsForContext:(RKObjectMappingProviderContext)context {
id contextValue = [self valueForContext:context];
if (contextValue == nil) return [NSArray array];
[self assertStorageForContext:context isKindOfClass:[NSArray class]];
return [NSArray arrayWithArray:contextValue];
}
- (void)addMapping:(id<RKObjectMappingDefinition>)mapping context:(RKObjectMappingProviderContext)context {
NSMutableArray *contextValue = [self valueForContext:context];
if (contextValue == nil) {
contextValue = [NSMutableArray arrayWithCapacity:1];
[self setValue:contextValue forContext:context];
}
[self assertStorageForContext:context isKindOfClass:[NSArray class]];
[contextValue addObject:mapping];
}
- (void)removeMapping:(id<RKObjectMappingDefinition>)mapping context:(RKObjectMappingProviderContext)context {
NSMutableArray *contextValue = [self valueForContext:context];
NSAssert(contextValue, @"Attempted to remove mapping from undefined context: %d", context);
[self assertStorageForContext:context isKindOfClass:[NSArray class]];
NSAssert([contextValue containsObject:mapping], @"Attempted to remove mapping from collection that does not include it for context: %d", context);
[contextValue removeObject:mapping];
}
- (id<RKObjectMappingDefinition>)mappingForKeyPath:(NSString *)keyPath context:(RKObjectMappingProviderContext)context {
NSMutableDictionary *contextValue = [self valueForContext:context];
NSAssert(contextValue, @"Attempted to retrieve mapping from undefined context: %d", context);
[self assertStorageForContext:context isKindOfClass:[NSDictionary class]];
return [contextValue valueForKey:keyPath];
}
- (void)setMapping:(id<RKObjectMappingDefinition>)mapping forKeyPath:(NSString *)keyPath context:(RKObjectMappingProviderContext)context {
NSMutableDictionary *contextValue = [self valueForContext:context];
if (contextValue == nil) {
contextValue = [NSMutableDictionary dictionary];
[self setValue:contextValue forContext:context];
}
[self assertStorageForContext:context isKindOfClass:[NSDictionary class]];
[contextValue setValue:mapping forKey:keyPath];
}
- (void)removeMappingForKeyPath:(NSString *)keyPath context:(RKObjectMappingProviderContext)context {
NSMutableDictionary *contextValue = [self valueForContext:context];
[self assertStorageForContext:context isKindOfClass:[NSDictionary class]];
[contextValue removeObjectForKey:keyPath];
}
#pragma mark - Aliases
+ (RKObjectMappingProvider *)objectMappingProvider {
return [self mappingProvider];
}
- (RKObjectMapping *)mappingForKeyPath:(NSString *)keyPath {
return (RKObjectMapping *) [self objectMappingForKeyPath:keyPath];
}
- (void)setMapping:(RKObjectMapping *)mapping forKeyPath:(NSString *)keyPath {
[self setObjectMapping:mapping forKeyPath:keyPath];
}
- (NSDictionary *)mappingsByKeyPath {
return [self objectMappingsByKeyPath];
}
- (void)registerMapping:(RKObjectMapping *)objectMapping withRootKeyPath:(NSString *)keyPath {
return [self registerObjectMapping:objectMapping withRootKeyPath:keyPath];
}
- (void)removeMappingForKeyPath:(NSString *)keyPath {
[self removeObjectMappingForKeyPath:keyPath];
}
@end