Skip to content

Commit

Permalink
Added support for parser class specification by regular expression. c…
Browse files Browse the repository at this point in the history
…loses RestKit#489
  • Loading branch information
inquinity authored and blakewatters committed Feb 25, 2012
1 parent 53bbeca commit 99a1080
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 16 deletions.
51 changes: 44 additions & 7 deletions Code/ObjectMapping/RKParserRegistry.h
Expand Up @@ -22,39 +22,76 @@
#import "RKParser.h"

/**
The Parser Registry provides for the registration of RKParser classes
for a particular MIME Type. This enables
*/
RKParserRegistry provides for the registration of RKParser classes
that handle parsing/serializing for content by MIME Type. Registration
is configured via exact string matches (i.e. application/json) or via regular
expression.
*/
@interface RKParserRegistry : NSObject {
NSMutableDictionary *_MIMETypeToParserClasses;
NSMutableArray *_MIMETypeToParserClassesRegularExpressions;
}

/**
Return the global shared singleton registry for MIME Type to Parsers
@return The global shared RKParserRegistry instance.
*/
+ (RKParserRegistry *)sharedRegistry;

/**
Sets the global shared registry singleton to a new instance of RKParserRegistry
@param registry A new parser registry object to configure as the shared instance.
*/
+ (void)setSharedRegistry:(RKParserRegistry *)registry;

/**
Instantiate and return a Parser for the given MIME Type
Returns an instance of the RKParser conformant class registered to handle content
with the given MIME Type.
MIME Types are searched in the order in which they are registered and exact
string matches are favored over regular expressions.
@param MIMEType The MIME Type of the content to be parsed/serialized.
@return An instance of the RKParser conformant class registered to handle the given MIME Type.
*/
- (id<RKParser>)parserForMIMEType:(NSString *)MIMEType;

/**
Return the class registered for handling parser/encoder operations
for a given MIME Type
Returns an instance of the RKParser conformant class registered to handle content
with the given MIME Type.
MIME Types are searched in the order in which they are registered and exact
string matches are favored over regular expressions.
@param MIMEType The MIME Type of the content to be parsed/serialized.
@return The RKParser conformant class registered to handle the given MIME Type.
*/
- (Class<RKParser>)parserClassForMIMEType:(NSString *)MIMEType;

/**
Registers an RKParser conformant class as the handler for the specified MIME Type
Registers an RKParser conformant class as the handler for MIME Types exactly matching the
specified MIME Type string.
@param parserClass The RKParser conformant class to instantiate when parsing/serializing MIME Types matching MIMETypeExpression.
@param MIMEType A MIME Type string for which instances of parserClass should be used for parsing/serialization.
*/
- (void)setParserClass:(Class<RKParser>)parserClass forMIMEType:(NSString *)MIMEType;

#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000

/**
Registers an RKParser conformant class as the handler for MIME Types matching the
specified regular expression.
@param parserClass The RKParser conformant class to instantiate when parsing/serializing MIME Types matching MIMETypeExpression.
@param MIMETypeRegex A regular expression that matches MIME Types that should be handled by instances of parserClass.
*/
- (void)setParserClass:(Class<RKParser>)parserClass forMIMETypeRegularExpression:(NSRegularExpression *)MIMETypeRegex;

#endif

/**
Automatically configure the registry via run-time reflection of the RKParser classes
available that ship with RestKit. This happens automatically when the shared registry
Expand Down
43 changes: 34 additions & 9 deletions Code/ObjectMapping/RKParserRegistry.m
Expand Up @@ -20,11 +20,11 @@

#import "RKParserRegistry.h"

RKParserRegistry* gSharedRegistry;
RKParserRegistry *gSharedRegistry;

@implementation RKParserRegistry

+ (RKParserRegistry*)sharedRegistry {
+ (RKParserRegistry *)sharedRegistry {
if (gSharedRegistry == nil) {
gSharedRegistry = [RKParserRegistry new];
[gSharedRegistry autoconfigure];
Expand All @@ -33,7 +33,7 @@ + (RKParserRegistry*)sharedRegistry {
return gSharedRegistry;
}

+ (void)setSharedRegistry:(RKParserRegistry*)registry {
+ (void)setSharedRegistry:(RKParserRegistry *)registry {
[registry retain];
[gSharedRegistry release];
gSharedRegistry = registry;
Expand All @@ -43,25 +43,50 @@ - (id)init {
self = [super init];
if (self) {
_MIMETypeToParserClasses = [[NSMutableDictionary alloc] init];
_MIMETypeToParserClassesRegularExpressions = [[NSMutableArray alloc] init];
}

return self;
}

- (void)dealloc {
[_MIMETypeToParserClasses release];
[_MIMETypeToParserClassesRegularExpressions release];
[super dealloc];
}

- (Class<RKParser>)parserClassForMIMEType:(NSString*)MIMEType {
return [_MIMETypeToParserClasses objectForKey:MIMEType];
- (Class<RKParser>)parserClassForMIMEType:(NSString *)MIMEType {
id parserClass = [_MIMETypeToParserClasses objectForKey:MIMEType];
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
if (!parserClass)
{
for (NSArray *regexAndClass in _MIMETypeToParserClassesRegularExpressions) {
NSRegularExpression *regex = [regexAndClass objectAtIndex:0];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:MIMEType options:0 range:NSMakeRange(0, [MIMEType length])];
if (numberOfMatches) {
parserClass = [regexAndClass objectAtIndex:1];
break;
}
}
}
#endif
return parserClass;
}

- (void)setParserClass:(Class<RKParser>)parserClass forMIMEType:(NSString*)MIMEType {
- (void)setParserClass:(Class<RKParser>)parserClass forMIMEType:(NSString *)MIMEType {
[_MIMETypeToParserClasses setObject:parserClass forKey:MIMEType];
}

- (id<RKParser>)parserForMIMEType:(NSString*)MIMEType {
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000

- (void)setParserClass:(Class<RKParser>)parserClass forMIMETypeRegularExpression:(NSRegularExpression *)MIMETypeRegex {
NSArray *expressionAndClass = [NSArray arrayWithObjects:MIMETypeRegex, parserClass, nil];
[_MIMETypeToParserClassesRegularExpressions addObject:expressionAndClass];
}

#endif

- (id<RKParser>)parserForMIMEType:(NSString *)MIMEType {
Class parserClass = [self parserClassForMIMEType:MIMEType];
if (parserClass) {
return [[[parserClass alloc] init] autorelease];
Expand All @@ -74,8 +99,8 @@ - (void)autoconfigure {
Class parserClass = nil;

// JSON
NSSet* JSONParserClassNames = [NSSet setWithObjects:@"RKJSONParserJSONKit", @"RKJSONParserYAJL", @"RKJSONParserSBJSON", @"RKJSONParserNXJSON", nil];
for (NSString* parserClassName in JSONParserClassNames) {
NSSet *JSONParserClassNames = [NSSet setWithObjects:@"RKJSONParserJSONKit", @"RKJSONParserYAJL", @"RKJSONParserSBJSON", @"RKJSONParserNXJSON", nil];
for (NSString *parserClassName in JSONParserClassNames) {
parserClass = NSClassFromString(parserClassName);
if (parserClass) {
[self setParserClass:parserClass forMIMEType:RKMIMETypeJSON];
Expand Down
32 changes: 32 additions & 0 deletions Tests/Logic/ObjectMapping/RKParserRegistryTest.m
Expand Up @@ -53,4 +53,36 @@ - (void)testShouldAutoconfigureBasedOnReflection {
assertThat(parser, is(instanceOf([RKXMLParserXMLReader class])));
}

- (void)testRetrievalOfExactStringMatchForMIMEType {
RKParserRegistry* registry = [[RKParserRegistry new] autorelease];
[registry setParserClass:[RKJSONParserJSONKit class] forMIMEType:RKMIMETypeJSON];
id<RKParser> parser = [registry parserForMIMEType:RKMIMETypeJSON];
assertThat(parser, is(instanceOf([RKJSONParserJSONKit class])));
}

- (void)testRetrievalOfRegularExpressionMatchForMIMEType {
RKParserRegistry *registry = [[RKParserRegistry new] autorelease];
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"application/xml\\+\\w+" options:0 error:&error];
[registry setParserClass:[RKJSONParserJSONKit class] forMIMETypeRegularExpression:regex];
id<RKParser> parser = [registry parserForMIMEType:@"application/xml+whatever"];
assertThat(parser, is(instanceOf([RKJSONParserJSONKit class])));
}

- (void)testRetrievalOfExactStringMatchIsFavoredOverRegularExpression {
RKParserRegistry *registry = [[RKParserRegistry new] autorelease];
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"application/xml\\+\\w+" options:0 error:&error];
[registry setParserClass:[RKJSONParserJSONKit class] forMIMETypeRegularExpression:regex];
[registry setParserClass:[RKXMLParserXMLReader class] forMIMEType:@"application/xml+whatever"];

// Exact match
id<RKParser> exactParser = [registry parserForMIMEType:@"application/xml+whatever"];
assertThat(exactParser, is(instanceOf([RKXMLParserXMLReader class])));

// Fallback to regex
id<RKParser> regexParser = [registry parserForMIMEType:@"application/xml+different"];
assertThat(regexParser, is(instanceOf([RKJSONParserJSONKit class])));
}

@end

0 comments on commit 99a1080

Please sign in to comment.