Permalink
Browse files

Added a URL builder

  • Loading branch information...
1 parent 2c598d7 commit e418b1b46e30e0e3153f0a5480a84ebf0e0ff6cd @davedelong committed Feb 28, 2011
Showing with 248 additions and 0 deletions.
  1. +45 −0 DDURLBuilder/DDURLBuilder.h
  2. +180 −0 DDURLBuilder/DDURLBuilder.m
  3. +23 −0 DDURLBuilder/main.m
@@ -0,0 +1,45 @@
+//
+// DDURLBuilder.h
+// EmptyFoundation
+//
+// Created by Dave DeLong on 2/17/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface DDURLBuilder : NSObject {
+ @private
+
+ NSString *scheme;
+ NSString *user;
+ NSString *password;
+ NSString *host;
+ NSNumber *port;
+ NSString *path;
+ NSMutableDictionary *queryValues;
+ NSString *fragment;
+}
+
++ (id) URLBuilderWithURL:(NSURL *)url;
+- (id) initWithURL:(NSURL *)url;
+
+@property (nonatomic, retain) NSURL *URL;
+
+@property (nonatomic, retain) NSString *scheme;
+@property (nonatomic, retain) NSString *user;
+@property (nonatomic, retain) NSString *password;
+@property (nonatomic, retain) NSString *host;
+@property (nonatomic, retain) NSNumber *port;
+@property (nonatomic, retain) NSString *path;
+@property (nonatomic, retain) NSString *fragment;
+
+@property (nonatomic, assign) BOOL usesSchemeSeparators;
+
+- (NSArray *) queryValuesForKey:(NSString *)key;
+- (void) addQueryValue:(NSString *)value forKey:(NSString *)key;
+- (void) removeQueryValue:(NSString *)value forKey:(NSString *)key;
+- (void) removeQueryValuesForKey:(NSString *)key;
+
+@end
View
@@ -0,0 +1,180 @@
+//
+// DDURLBuilder.m
+// EmptyFoundation
+//
+// Created by Dave DeLong on 2/17/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "DDURLBuilder.h"
+
+NSString *ddurlbuilder_percentEncode(NSString *string) {
+ NSMutableString * output = [NSMutableString string];
+ const unsigned char * source = (const unsigned char *)[string UTF8String];
+ int sourceLen = strlen((const char *)source);
+ for (int i = 0; i < sourceLen; ++i) {
+ const unsigned char thisChar = source[i];
+ if (thisChar == ' '){
+ [output appendString:@"+"];
+ } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' ||
+ (thisChar >= 'a' && thisChar <= 'z') ||
+ (thisChar >= 'A' && thisChar <= 'Z') ||
+ (thisChar >= '0' && thisChar <= '9')) {
+ [output appendFormat:@"%c", thisChar];
+ } else {
+ [output appendFormat:@"%%%02X", thisChar];
+ }
+ }
+ return output;
+}
+
+@implementation DDURLBuilder
+
+@synthesize scheme;
+@synthesize user;
+@synthesize password;
+@synthesize host;
+@synthesize path;
+@synthesize fragment;
+@synthesize port;
+
+@synthesize usesSchemeSeparators;
+
++ (id) URLBuilderWithURL:(NSURL *)url {
+ return [[[self alloc] initWithURL:url] autorelease];
+}
+
+- (id) initWithURL:(NSURL *)url {
+ self = [super init];
+ if (self) {
+ queryValues = [[NSMutableDictionary alloc] init];
+ [self setUsesSchemeSeparators:YES];
+ if (url) {
+ [self setURL:url];
+ }
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [scheme release];
+ [user release];
+ [password release];
+ [host release];
+ [port release];
+ [path release];
+ [queryValues release];
+ [fragment release];
+ [super dealloc];
+}
+
+- (void) setURL:(NSURL *)URL {
+ [self setScheme:[URL scheme]];
+ [self setUser:[URL user]];
+ [self setPassword:[URL password]];
+ [self setPath:[URL path]];
+ [self setHost:[URL host]];
+ [self setFragment:[URL fragment]];
+ [self setPort:[URL port]];
+
+ NSString *absolute = [URL absoluteString];
+ [self setUsesSchemeSeparators:([absolute hasPrefix:[NSString stringWithFormat:@"%@://", [self scheme]]])];
+
+ [queryValues removeAllObjects];
+ NSString *query = [URL query];
+ NSArray *components = [query componentsSeparatedByString:@"&"];
+ for (NSString *component in components) {
+ NSArray *bits = [component componentsSeparatedByString:@"="];
+ if ([bits count] != 2) {
+ NSLog(@"illegal query string component: %@", component);
+ continue;
+ }
+
+ NSString *key = [[components objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ NSString *value = [[components objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ [self addQueryValue:value forKey:key];
+ }
+}
+
+- (NSURL *)URL {
+ if ([self scheme] == nil || [self host] == nil) { return nil; }
+
+ NSMutableString *url = [NSMutableString string];
+
+ [url appendFormat:@"%@:", [self scheme]];
+ if ([self usesSchemeSeparators]) {
+ [url appendString:@"//"];
+ }
+ if ([self user]) {
+ [url appendString:ddurlbuilder_percentEncode([self user])];
+ if ([self password]) {
+ [url appendFormat:@":%@", ddurlbuilder_percentEncode([self password])];
+ }
+ [url appendString:@"@"];
+ }
+
+ [url appendString:ddurlbuilder_percentEncode([self host])];
+ if ([self port]) {
+ [url appendFormat:@":%@", [self port]];
+ }
+
+
+ if ([self path]) {
+ NSArray *pathComponents = [[self path] pathComponents];
+ for (NSString *component in pathComponents) {
+ if ([component isEqualToString:@"/"]) { continue; }
+ [url appendFormat:@"/%@", ddurlbuilder_percentEncode(component)];
+ }
+ }
+
+ if ([queryValues count] > 0) {
+ NSMutableArray *components = [NSMutableArray array];
+ for (NSString *key in queryValues) {
+ NSArray *values = [queryValues objectForKey:key];
+ key = ddurlbuilder_percentEncode(key);
+ for (NSString *value in values) {
+ value = ddurlbuilder_percentEncode(value);
+ NSString *component = [NSString stringWithFormat:@"%@=%@", key, value];
+ [components addObject:component];
+ }
+ }
+ NSString *queryString = [components componentsJoinedByString:@"&"];
+ [url appendFormat:@"?%@", queryString];
+ }
+
+ if ([self fragment]) {
+ [url appendFormat:@"#%@", [self fragment]];
+ }
+
+ return [NSURL URLWithString:url];
+}
+
+- (NSArray *) queryValuesForKey:(NSString *)key {
+ if (key == nil) { return nil; }
+ return [[[queryValues objectForKey:key] copy] autorelease];
+}
+
+- (void) addQueryValue:(NSString *)value forKey:(NSString *)key {
+ if (value == nil || key == nil) { return; }
+ NSMutableArray *values = [queryValues objectForKey:key];
+ if (values == nil) {
+ values = [NSMutableArray array];
+ [queryValues setObject:values forKey:key];
+ }
+ [values addObject:value];
+}
+
+- (void) removeQueryValue:(NSString *)value forKey:(NSString *)key {
+ if (value == nil || key == nil) { return; }
+ NSMutableArray *values = [queryValues objectForKey:key];
+ if (values) {
+ [values removeObject:value];
+ }
+}
+
+- (void) removeQueryValuesForKey:(NSString *)key {
+ if (key == nil) { return; }
+ [queryValues removeObjectForKey:key];
+}
+
+@end
View
@@ -0,0 +1,23 @@
+#import <Foundation/Foundation.h>
+#import "DDURLBuilder.h"
+
+int main (int argc, const char * argv[]) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // http://www.google.com/search?client=safari&rls=en&q=what+is+the+answer+to+the+ultimate+question+of+life+the+universe+and+everything&ie=UTF-8&oe=UTF-8
+
+ DDURLBuilder *builder = [DDURLBuilder URLBuilderWithURL:nil];
+ [builder setHost:@"google.com"];
+ [builder setScheme:@"http"];
+ [builder setPath:@"search"];
+ [builder addQueryValue:@"safari" forKey:@"client"];
+ [builder addQueryValue:@"en" forKey:@"rls"];
+ [builder addQueryValue:@"what is the answer to the ultimate question of life the universe and everything" forKey:@"q"];
+ [builder addQueryValue:@"UTF-8" forKey:@"ie"];
+ [builder addQueryValue:@"UTF-8" forKey:@"oe"];
+
+ NSLog(@"%@", [builder URL]);
+
+ [pool drain];
+ return 0;
+}

0 comments on commit e418b1b

Please sign in to comment.