aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2009-07-29 02:00:24 +0000
committerGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2009-07-29 02:00:24 +0000
commiteff8351b590bcf190b175e2add2ab87c8e7204ec (patch)
treebf73a229e0bf1d397d93461fea55e076c5fa87a4
parentb65c5509d204b37eb79af1307f04a048806def48 (diff)
[Author: iwade]
Add gtm_dictionaryWithHttpArgumentsString to NSDictionary+URLArguments. R=thomasvl APPROVED=thomasvl DELTA=73 (72 added, 0 deleted, 1 changed)
-rw-r--r--Foundation/GTMNSDictionary+URLArguments.h8
-rw-r--r--Foundation/GTMNSDictionary+URLArguments.m31
-rw-r--r--Foundation/GTMNSDictionary+URLArgumentsTest.m33
-rw-r--r--ReleaseNotes.txt2
4 files changed, 73 insertions, 1 deletions
diff --git a/Foundation/GTMNSDictionary+URLArguments.h b/Foundation/GTMNSDictionary+URLArguments.h
index 4bc896d..b094411 100644
--- a/Foundation/GTMNSDictionary+URLArguments.h
+++ b/Foundation/GTMNSDictionary+URLArguments.h
@@ -21,8 +21,14 @@
/// Utility for building a URL or POST argument string.
@interface NSDictionary (GTMNSDictionaryURLArgumentsAdditions)
+/// Returns a dictionary of the decoded key-value pairs in a http arguments
+/// string of the form key1=value1&key2=value2&...&keyN=valueN.
+/// Keys and values will be unescaped automatically.
+/// Only the first value for a repeated key is returned.
++ (NSDictionary *)gtm_dictionaryWithHttpArgumentsString:(NSString *)argString;
+
/// Gets a string representation of the dictionary in the form
-/// key1=value1&key2&value2&...&keyN=valueN, suitable for use as either
+/// key1=value1&key2=value2&...&keyN=valueN, suitable for use as either
/// URL arguments (after a '?') or POST body. Keys and values will be escaped
/// automatically, so should be unescaped in the dictionary.
- (NSString *)gtm_httpArgumentsString;
diff --git a/Foundation/GTMNSDictionary+URLArguments.m b/Foundation/GTMNSDictionary+URLArguments.m
index 89610e4..4799b2d 100644
--- a/Foundation/GTMNSDictionary+URLArguments.m
+++ b/Foundation/GTMNSDictionary+URLArguments.m
@@ -24,6 +24,37 @@
@implementation NSDictionary (GTMNSDictionaryURLArgumentsAdditions)
GTM_METHOD_CHECK(NSString, gtm_stringByEscapingForURLArgument);
+GTM_METHOD_CHECK(NSString, gtm_stringByUnescapingFromURLArgument);
+
++ (NSDictionary *)gtm_dictionaryWithHttpArgumentsString:(NSString *)argString {
+ NSMutableDictionary* ret = [NSMutableDictionary dictionary];
+ NSArray* components = [argString componentsSeparatedByString:@"&"];
+ NSString* component;
+ // Use reverse order so that the first occurrence of a key replaces
+ // those subsequent.
+ GTM_FOREACH_ENUMEREE(component, [components reverseObjectEnumerator]) {
+ if ([component length] == 0)
+ continue;
+ NSRange pos = [component rangeOfString:@"="];
+ NSString *key;
+ NSString *val;
+ if (pos.location == NSNotFound) {
+ key = [component gtm_stringByUnescapingFromURLArgument];
+ val = @"";
+ } else {
+ key = [[component substringToIndex:pos.location]
+ gtm_stringByUnescapingFromURLArgument];
+ val = [[component substringFromIndex:pos.location + pos.length]
+ gtm_stringByUnescapingFromURLArgument];
+ }
+ // gtm_stringByUnescapingFromURLArgument returns nil on invalid UTF8
+ // and NSMutableDictionary raises an exception when passed nil values.
+ if (!key) key = @"";
+ if (!val) val = @"";
+ [ret setObject:val forKey:key];
+ }
+ return ret;
+}
- (NSString *)gtm_httpArgumentsString {
NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:[self count]];
diff --git a/Foundation/GTMNSDictionary+URLArgumentsTest.m b/Foundation/GTMNSDictionary+URLArgumentsTest.m
index f01519e..8ec9520 100644
--- a/Foundation/GTMNSDictionary+URLArgumentsTest.m
+++ b/Foundation/GTMNSDictionary+URLArgumentsTest.m
@@ -25,6 +25,39 @@
@implementation GTMNSDictionary_URLArgumentsTest
+- (void)testFromArgumentsString {
+ STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@""],
+ [NSDictionary dictionary],
+ @"- empty arguments string should give an empty dictionary");
+ STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"a"],
+ [NSDictionary dictionaryWithObject:@"" forKey:@"a"],
+ @"- missing '=' should result in an empty string value");
+ STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"a="],
+ [NSDictionary dictionaryWithObject:@"" forKey:@"a"],
+ @"- no value");
+ STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"&a=1"],
+ [NSDictionary dictionaryWithObject:@"1" forKey:@"a"],
+ @"- empty segment should be skipped");
+ STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"abc=123"],
+ [NSDictionary dictionaryWithObject:@"123" forKey:@"abc"],
+ @"- simple one-pair dictionary should work");
+ STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"a=1&a=2&a=3"],
+ [NSDictionary dictionaryWithObject:@"1" forKey:@"a"],
+ @"- only first occurrence of a key is returned");
+ NSString* complex = @"a%2Bb=specialkey&complex=1%2B1%21%3D3%20%26%202%2A6%2F3%3D4&c";
+ NSDictionary* result = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"1+1!=3 & 2*6/3=4", @"complex",
+ @"specialkey", @"a+b",
+ @"", @"c",
+ nil];
+ STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:complex],
+ result,
+ @"- keys and values should be unescaped correctly");
+ STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"a=%FC"],
+ [NSDictionary dictionaryWithObject:@"" forKey:@"a"],
+ @"- invalid UTF8 characters result in an empty value, not a crash");
+}
+
- (void)testArgumentsString {
STAssertEqualObjects([[NSDictionary dictionary] gtm_httpArgumentsString], @"",
@"- empty dictionary should give an empty string");
diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt
index d2b55ea..aa7083a 100644
--- a/ReleaseNotes.txt
+++ b/ReleaseNotes.txt
@@ -314,6 +314,8 @@ Changes since 1.5.1
- Added NSMatrix and NSCell to GTMLocalizer support.
+- Added gtm_dictionaryWithHttpArgumentsString to NSDictionary+URLArguments.
+
Release 1.5.1
Changes since 1.5.0