diff options
-rw-r--r-- | Foundation/GTMURLBuilder.h | 64 | ||||
-rw-r--r-- | Foundation/GTMURLBuilder.m | 138 | ||||
-rw-r--r-- | Foundation/GTMURLBuilderTest.m | 108 | ||||
-rw-r--r-- | GTMiPhone.xcodeproj/project.pbxproj | 14 |
4 files changed, 324 insertions, 0 deletions
diff --git a/Foundation/GTMURLBuilder.h b/Foundation/GTMURLBuilder.h new file mode 100644 index 0000000..e959fa7 --- /dev/null +++ b/Foundation/GTMURLBuilder.h @@ -0,0 +1,64 @@ +// +// GTMURLBuilder.h +// +// Copyright 2012 Google Inc. +// +// 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. +// + +// +// Class for creating URLs. It handles URL encoding of parameters. +// +// Usage example: +// +// GTMURLBuilder *URLBuilder = +// [GTMURLBuilder builderWithString:@"http://www.google.com"]; +// [URLBuilder setValue:@"abc" forParameter:@"q"]; +// NSURL *URL = [URLBuilder URL]; +// + +#import <Foundation/Foundation.h> +#import "GTMDefines.h" + +@interface GTMURLBuilder : NSObject { + @private + NSMutableDictionary *params_; +} + +@property(nonatomic, readonly) NSString *baseURLString; + +// Encodes URL by percent-encoding, except reserved characters: +// ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," ++ (NSURL *)URLWithString:(NSString *)URLString; +// |URLString| is expected to be a valid URL with already escaped parameter +// values. ++ (GTMURLBuilder *)builderWithString:(NSString *)URLString; ++ (GTMURLBuilder *)builderWithURL:(NSURL *)URL; + +// |URLString| The base URL to which parameters will be appended. +// If the URL already contains parameters, they should already be encoded. +- (id)initWithString:(NSString *)URLString; +- (void)setValue:(NSString *)value forParameter:(NSString *)parameter; +- (void)setIntegerValue:(NSInteger)value forParameter:(NSString *)parameter; +- (NSString *)valueForParameter:(NSString *)parameter; +- (void)removeParameter:(NSString *)parameter; +- (void)setParameters:(NSDictionary *)parameters; +- (NSDictionary *)parameters; +- (NSURL *)URL; +- (NSString *)URLString; + +// Case-sensitive comparison of the URL. Also protocol and host are compared +// as case-sensitive strings. The order of URL parameters is ignored. +- (BOOL)isEqual:(GTMURLBuilder *)URLBuilder; + +@end diff --git a/Foundation/GTMURLBuilder.m b/Foundation/GTMURLBuilder.m new file mode 100644 index 0000000..0cfce45 --- /dev/null +++ b/Foundation/GTMURLBuilder.m @@ -0,0 +1,138 @@ +// +// GTMURLBuilder.m +// +// Copyright 2012 Google Inc. +// +// 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 "GTMURLBuilder.h" + +#import "GTMDefines.h" +#import "GTMLogger.h" +#import "GTMNSDictionary+URLArguments.h" +#import "GTMNSString+URLArguments.h" + +@implementation GTMURLBuilder + +@synthesize baseURLString = baseURLString_; + ++ (GTMURLBuilder *)builderWithString:(NSString *)URLString { + GTMURLBuilder *URLBuilder = + [[[GTMURLBuilder alloc] initWithString:URLString] autorelease]; + return URLBuilder; +} + ++ (GTMURLBuilder *)builderWithURL:(NSURL *)URL { + return [self builderWithString:[URL absoluteString]]; +} + +- (id)init { + self = [super init]; + [self release]; + _GTMDevAssert(NO, @"Invalid initialization."); + return nil; +} + +- (id)initWithString:(NSString *)URLString { + self = [super init]; + if (self) { + _GTMDevAssert(URLString, @"URL must not be nil"); + NSURL *URL = [NSURL URLWithString:URLString]; + _GTMDevAssert(URL, @"URL is invalid"); + + // NSURL does not work with ports. + baseURLString_ = [URL absoluteString]; + if ([URL path]) { + NSRange pathRange = + [baseURLString_ rangeOfString:[URL path] options:NSBackwardsSearch]; + if (pathRange.location != NSNotFound) { + baseURLString_ = [baseURLString_ substringToIndex:pathRange.location]; + } + + baseURLString_ = + [NSString stringWithFormat:@"%@%@", baseURLString_, [URL path]]; + } + [baseURLString_ retain]; + params_ = [[NSDictionary gtm_dictionaryWithHttpArgumentsString:[URL query]] + mutableCopy]; + } + return self; +} + +- (void)dealloc { + [baseURLString_ release]; + [params_ release]; + [super dealloc]; +} + +- (void)setValue:(NSString *)value forParameter:(NSString *)parameter { + [params_ setObject:value forKey:parameter]; +} + +- (void)setIntegerValue:(NSInteger)value forParameter:(NSString *)parameter { + [params_ setObject:[NSString stringWithFormat:@"%i", value] forKey:parameter]; +} + +- (NSString *)valueForParameter:(NSString *)parameter { + return [params_ objectForKey:parameter]; +} + +- (void)removeParameter:(NSString *)parameter { + [params_ removeObjectForKey:parameter]; +} + +- (void)setParameters:(NSDictionary *)parameters { + [params_ autorelease]; + params_ = [[NSDictionary dictionaryWithDictionary:parameters] mutableCopy]; +} + +- (NSDictionary *)parameters { + return params_; +} + +- (NSURL *)URL { + if (![params_ count]) { + return [NSURL URLWithString:baseURLString_]; + } else { + return [NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", + baseURLString_, [params_ gtm_httpArgumentsString]]]; + } +} + +- (NSString *)URLString { + return [[self URL] absoluteString]; +} + +- (BOOL)isEqual:(GTMURLBuilder *)URLBuilder { + if (!URLBuilder) { + return NO; + } + + if (!([[self baseURLString] isEqualToString:[URLBuilder baseURLString]])) { + return NO; + } + + if (![[self parameters] isEqualToDictionary:[URLBuilder parameters]]) { + return NO; + } + + return YES; +} + ++ (NSURL *)URLWithString:(NSString *)URLString { + return [NSURL URLWithString: + [URLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; +} + +@end diff --git a/Foundation/GTMURLBuilderTest.m b/Foundation/GTMURLBuilderTest.m new file mode 100644 index 0000000..096c09b --- /dev/null +++ b/Foundation/GTMURLBuilderTest.m @@ -0,0 +1,108 @@ +// +// GTMURLBuilderTest.m +// +// Copyright 2012 Google Inc. +// +// 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 "GTMURLBuilder.h" + +#import "GTMSenTestCase.h" + +@interface GTMURLBuilderTest : GTMTestCase +@end + +@implementation GTMURLBuilderTest + +- (void)testParseURL { + GTMURLBuilder *URLBuilder = [[[GTMURLBuilder alloc] + initWithString:@"http://google.com:8080/pathA/pathB?param=val"] + autorelease]; + STAssertEqualStrings(@"http://google.com:8080/pathA/pathB?param=val", + [URLBuilder URLString], nil); + STAssertEqualStrings(@"val", [URLBuilder valueForParameter:@"param"], nil); + URLBuilder = [GTMURLBuilder builderWithString: + @"http://google.com:8080/pathA/pathB?param=val"]; + STAssertEqualStrings(@"http://google.com:8080/pathA/pathB?param=val", + [URLBuilder URLString], nil); + STAssertEqualStrings(@"val", [URLBuilder valueForParameter:@"param"], nil); +} + +- (void)testMailToHandling { + GTMURLBuilder *URLBuilder = + [GTMURLBuilder builderWithString:@"mailto:ytmapp-ios@google.com"]; + [URLBuilder setValue:@"blah" forParameter:@"subject"]; + STAssertEqualStrings(@"mailto:ytmapp-ios@google.com?subject=blah", + [URLBuilder URLString], nil); +} + +- (void)testIsEqualTo { + GTMURLBuilder *URLBuilderA = [GTMURLBuilder + builderWithString:@"http://google.com/pathA/pathB?a=b&c=d"]; + GTMURLBuilder *URLBuilderB = + [GTMURLBuilder builderWithString:@"http://google.com/pathA/pathB"]; + [URLBuilderB setValue:@"d" forParameter:@"c"]; + [URLBuilderB setValue:@"b" forParameter:@"a"]; + STAssertTrue([URLBuilderA isEqual:URLBuilderB], nil); + [URLBuilderB setValue:@"c" forParameter:@"a"]; + STAssertFalse([URLBuilderA isEqual:URLBuilderB], nil); + [URLBuilderB setValue:@"b" forParameter:@"a"]; + [URLBuilderB setValue:@"f" forParameter:@"e"]; + STAssertFalse([URLBuilderA isEqual:URLBuilderB], nil); +} + +- (void)testURLWithString { + STAssertEqualStrings(@";/?:@&=+$,", + [[GTMURLBuilder URLWithString:@";/?:@&=+$,"] + absoluteString], + nil); + STAssertEqualStrings(@"a%20b%20&%20c%20-%20URL=http://test.com/", + [[GTMURLBuilder URLWithString: + @"a b & c - URL=http://test.com/"] absoluteString], + nil); +} + +- (void)testSetParameters { + GTMURLBuilder *URLBuilderA = + [GTMURLBuilder builderWithString:@"http://google.com/"]; + GTMURLBuilder *URLBuilderB = + [GTMURLBuilder builderWithString:@"http://google.com/?p1=x&p2=b"]; + NSDictionary *params = + [NSDictionary dictionaryWithObjectsAndKeys:@"a", @"p1", @"b", @"p2", nil]; + [URLBuilderA setParameters:params]; + [URLBuilderA setValue:@"x" forParameter:@"p1"]; + STAssertTrue([URLBuilderA isEqual:URLBuilderB], nil); +} + +- (void)testReplaceParameters { + GTMURLBuilder *URLBuilderA = + [GTMURLBuilder builderWithString:@"http://google.com/?p1=y"]; + GTMURLBuilder *URLBuilderB = + [GTMURLBuilder builderWithString:@"http://google.com/?p1=x&p2=b"]; + NSDictionary *params = + [NSDictionary dictionaryWithObjectsAndKeys:@"a", @"p1", @"b", @"p2", nil]; + [URLBuilderA setParameters:params]; + [URLBuilderA setValue:@"x" forParameter:@"p1"]; + STAssertTrue([URLBuilderA isEqual:URLBuilderB], nil); +} + +- (void)testURLPathParsing { + GTMURLBuilder *URLBuilder = + [GTMURLBuilder builderWithString:@"http://google.com/"]; + STAssertEqualStrings(@"http://google.com/", [URLBuilder URLString], nil); + URLBuilder = [GTMURLBuilder builderWithString:@"http://google.com/pA/pB"]; + STAssertEqualStrings(@"http://google.com/pA/pB", [URLBuilder URLString], nil); +} + +@end diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj index bd031dd..82586c3 100644 --- a/GTMiPhone.xcodeproj/project.pbxproj +++ b/GTMiPhone.xcodeproj/project.pbxproj @@ -118,6 +118,10 @@ 9340CFA8140550E30026DDC9 /* GTMFadeTruncatingLabelTest3.png in Resources */ = {isa = PBXBuildFile; fileRef = 9340CFA3140550E30026DDC9 /* GTMFadeTruncatingLabelTest3.png */; }; 9340CFA9140550E30026DDC9 /* GTMFadeTruncatingLabelTest4.png in Resources */ = {isa = PBXBuildFile; fileRef = 9340CFA4140550E30026DDC9 /* GTMFadeTruncatingLabelTest4.png */; }; 9340CFAA140550E30026DDC9 /* GTMFadeTruncatingLabelTest5.png in Resources */ = {isa = PBXBuildFile; fileRef = 9340CFA5140550E30026DDC9 /* GTMFadeTruncatingLabelTest5.png */; }; + BE9B794114FE9A2C004A993A /* GTMURLBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9B793F14FE99F3004A993A /* GTMURLBuilderTest.m */; }; + BE9B794214FE9A2E004A993A /* GTMURLBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9B793F14FE99F3004A993A /* GTMURLBuilderTest.m */; }; + BE9B794314FE9A3E004A993A /* GTMURLBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9B793E14FE99F3004A993A /* GTMURLBuilder.m */; }; + BE9B794414FE9A3E004A993A /* GTMURLBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9B793E14FE99F3004A993A /* GTMURLBuilder.m */; }; F417115A0ECDFF0400B9B276 /* GTMLightweightProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = F41711580ECDFF0400B9B276 /* GTMLightweightProxy.m */; }; F417115B0ECDFF0400B9B276 /* GTMLightweightProxyTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F41711590ECDFF0400B9B276 /* GTMLightweightProxyTest.m */; }; F418AF990E7558EC004FB565 /* GTMExceptionalInlines.m in Sources */ = {isa = PBXBuildFile; fileRef = F418AF940E7558DC004FB565 /* GTMExceptionalInlines.m */; }; @@ -433,6 +437,9 @@ 9340CFA3140550E30026DDC9 /* GTMFadeTruncatingLabelTest3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = GTMFadeTruncatingLabelTest3.png; path = TestData/GTMFadeTruncatingLabelTest3.png; sourceTree = "<group>"; }; 9340CFA4140550E30026DDC9 /* GTMFadeTruncatingLabelTest4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = GTMFadeTruncatingLabelTest4.png; path = TestData/GTMFadeTruncatingLabelTest4.png; sourceTree = "<group>"; }; 9340CFA5140550E30026DDC9 /* GTMFadeTruncatingLabelTest5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = GTMFadeTruncatingLabelTest5.png; path = TestData/GTMFadeTruncatingLabelTest5.png; sourceTree = "<group>"; }; + BE9B793D14FE99F3004A993A /* GTMURLBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GTMURLBuilder.h; sourceTree = "<group>"; }; + BE9B793E14FE99F3004A993A /* GTMURLBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GTMURLBuilder.m; sourceTree = "<group>"; }; + BE9B793F14FE99F3004A993A /* GTMURLBuilderTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GTMURLBuilderTest.m; sourceTree = "<group>"; }; F41711570ECDFF0400B9B276 /* GTMLightweightProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMLightweightProxy.h; sourceTree = "<group>"; }; F41711580ECDFF0400B9B276 /* GTMLightweightProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLightweightProxy.m; sourceTree = "<group>"; }; F41711590ECDFF0400B9B276 /* GTMLightweightProxyTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLightweightProxyTest.m; sourceTree = "<group>"; }; @@ -717,6 +724,9 @@ 8BC04A740DAF145200C2D1CA /* GTMSystemVersion.m */, 8BC04A6F0DAF144200C2D1CA /* GTMSystemVersion.h */, 8BC04A710DAF144700C2D1CA /* GTMSystemVersionTest.m */, + BE9B793D14FE99F3004A993A /* GTMURLBuilder.h */, + BE9B793E14FE99F3004A993A /* GTMURLBuilder.m */, + BE9B793F14FE99F3004A993A /* GTMURLBuilderTest.m */, 8B41EC0D0E0711D40040CF9F /* GTMValidatingContainers.m */, 8B41EC0E0E0711D40040CF9F /* GTMValidatingContainers.h */, 8B41EC0C0E0711D40040CF9F /* GTMValidatingContainersTest.m */, @@ -1110,6 +1120,8 @@ 9340CFA0140550CE0026DDC9 /* GTMFadeTruncatingLabelTest.m in Sources */, 169E1E2F1459AAE100E6F562 /* GTMUILocalizer.m in Sources */, 169E1E301459AAE100E6F562 /* GTMUILocalizerTest.m in Sources */, + BE9B794114FE9A2C004A993A /* GTMURLBuilderTest.m in Sources */, + BE9B794314FE9A3E004A993A /* GTMURLBuilder.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1193,6 +1205,8 @@ F4D20F1914852CA40001600C /* GTMUnitTestDevLog.m in Sources */, F4D20F1A14852CA40001600C /* GTMValidatingContainers.m in Sources */, F4D20F1B14852CA40001600C /* GTMValidatingContainersTest.m in Sources */, + BE9B794214FE9A2E004A993A /* GTMURLBuilderTest.m in Sources */, + BE9B794414FE9A3E004A993A /* GTMURLBuilder.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; |