aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Foundation/GTMURLBuilder.h64
-rw-r--r--Foundation/GTMURLBuilder.m138
-rw-r--r--Foundation/GTMURLBuilderTest.m108
-rw-r--r--GTMiPhone.xcodeproj/project.pbxproj14
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;
};