Skip to content

Commit

Permalink
[Author: msenesi]
Browse files Browse the repository at this point in the history
Flexible builder for URLs.

R=altse,dmaclach,thomasvl
APPROVED=dmaclach

git-svn-id: http://google-toolbox-for-mac.googlecode.com/svn/trunk@521 7dc7ac4e-7543-0410-b95c-c1676fc8e2a3
  • Loading branch information
gtm.daemon committed Mar 1, 2012
1 parent 2255943 commit fc87f5c
Show file tree
Hide file tree
Showing 4 changed files with 324 additions and 0 deletions.
64 changes: 64 additions & 0 deletions Foundation/GTMURLBuilder.h
Original file line number Diff line number Diff line change
@@ -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
138 changes: 138 additions & 0 deletions Foundation/GTMURLBuilder.m
Original file line number Diff line number Diff line change
@@ -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
108 changes: 108 additions & 0 deletions Foundation/GTMURLBuilderTest.m
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions GTMiPhone.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -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>"; };
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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;
};
Expand Down Expand Up @@ -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;
};
Expand Down

0 comments on commit fc87f5c

Please sign in to comment.