Index: src/OFMutableURI.h ================================================================== --- src/OFMutableURI.h +++ src/OFMutableURI.h @@ -144,24 +144,24 @@ */ @property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *percentEncodedQuery; /** - * @brief The query part of the URI as a dictionary. + * @brief The query part of the URI as an array. * * For example, a query like `key1=value1&key2=value2` would correspond to the - * following dictionary: + * following array: * - * @{ - * @"key1": @"value1", - * @"key2": @"value2" - * } + * @[ + * [OFPair pairWithFirstObject: @"key1" secondObject: @"value1"], + * [OFPair pairWithFirstObject: @"key2" secondObject: @"value2"], + * ] * * @throw OFInvalidFormatException The query is not in the correct format */ @property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) - OFDictionary OF_GENERIC(OFString *, OFString *) *queryDictionary; + OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *queryItems; /** * @brief The fragment part of the URI. */ @property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *fragment; Index: src/OFMutableURI.m ================================================================== --- src/OFMutableURI.m +++ src/OFMutableURI.m @@ -20,19 +20,20 @@ #import "OFDictionary.h" #ifdef OF_HAVE_FILES # import "OFFileManager.h" #endif #import "OFNumber.h" +#import "OFPair.h" #import "OFString.h" #import "OFInvalidFormatException.h" @implementation OFMutableURI @dynamic scheme, percentEncodedScheme, host, percentEncodedHost, port, user; @dynamic percentEncodedUser, password, percentEncodedPassword, path; @dynamic percentEncodedPath, pathComponents, query, percentEncodedQuery; -@dynamic queryDictionary, fragment, percentEncodedFragment; +@dynamic queryItems, fragment, percentEncodedFragment; + (instancetype)URI { return [[[self alloc] init] autorelease]; } @@ -233,43 +234,41 @@ old = _percentEncodedQuery; _percentEncodedQuery = [percentEncodedQuery copy]; [old release]; } -- (void)setQueryDictionary: - (OFDictionary OF_GENERIC(OFString *, OFString *) *)dictionary +- (void)setQueryItems: + (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *) + queryItems { void *pool; OFMutableString *percentEncodedQuery; - OFEnumerator OF_GENERIC(OFString *) *keyEnumerator, *objectEnumerator; OFCharacterSet *characterSet; - OFString *key, *object, *old; + OFString *old; - if (dictionary == nil) { + if (queryItems == nil) { [_percentEncodedQuery release]; _percentEncodedQuery = nil; return; } pool = objc_autoreleasePoolPush(); percentEncodedQuery = [OFMutableString string]; - keyEnumerator = [dictionary keyEnumerator]; - objectEnumerator = [dictionary objectEnumerator]; characterSet = [OFCharacterSet URIQueryKeyValueAllowedCharacterSet]; - while ((key = [keyEnumerator nextObject]) != nil && - (object = [objectEnumerator nextObject]) != nil) { - key = [key stringByAddingPercentEncodingWithAllowedCharacters: + for (OFPair OF_GENERIC(OFString *, OFString *) *item in queryItems) { + OFString *key = [item.firstObject + stringByAddingPercentEncodingWithAllowedCharacters: characterSet]; - object = [object + OFString *value = [item.secondObject stringByAddingPercentEncodingWithAllowedCharacters: characterSet]; if (percentEncodedQuery.length > 0) [percentEncodedQuery appendString: @"&"]; - [percentEncodedQuery appendFormat: @"%@=%@", key, object]; + [percentEncodedQuery appendFormat: @"%@=%@", key, value]; } old = _percentEncodedQuery; _percentEncodedQuery = [percentEncodedQuery copy]; [old release]; Index: src/OFURI.h ================================================================== --- src/OFURI.h +++ src/OFURI.h @@ -20,10 +20,11 @@ OF_ASSUME_NONNULL_BEGIN @class OFArray OF_GENERIC(ObjectType); @class OFDictionary OF_GENERIC(KeyType, ObjectType); @class OFNumber; +@class OFPair OF_GENERIC(FirstType, SecondType); @class OFString; /** * @class OFURI OFURI.h ObjFW/OFURI.h * @@ -128,24 +129,24 @@ */ @property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *percentEncodedQuery; /** - * @brief The query part of the URI as a dictionary. + * @brief The query part of the URI as an array. * * For example, a query like `key1=value1&key2=value2` would correspond to the - * following dictionary: + * following array: * - * @{ - * @"key1": @"value1", - * @"key2": @"value2" - * } + * @[ + * [OFPair pairWithFirstObject: @"key1" secondObject: @"value1"], + * [OFPair pairWithFirstObject: @"key2" secondObject: @"value2"], + * ] * * @throw OFInvalidFormatException The query is not in the correct format */ @property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) - OFDictionary OF_GENERIC(OFString *, OFString *) *queryDictionary; + OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *queryItems; /** * @brief The fragment part of the URI. */ @property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *fragment; Index: src/OFURI.m ================================================================== --- src/OFURI.m +++ src/OFURI.m @@ -25,10 +25,11 @@ # import "OFFileManager.h" # import "OFFileURIHandler.h" #endif #import "OFNumber.h" #import "OFOnce.h" +#import "OFPair.h" #import "OFString.h" #import "OFXMLElement.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" @@ -1039,22 +1040,23 @@ - (OFString *)percentEncodedQuery { return _percentEncodedQuery; } -- (OFDictionary OF_GENERIC(OFString *, OFString *) *)queryDictionary +- (OFArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) *)queryItems { void *pool; OFArray OF_GENERIC(OFString *) *pairs; - OFMutableDictionary OF_GENERIC(OFString *, OFString *) *ret; + OFMutableArray OF_GENERIC(OFPair OF_GENERIC(OFString *, OFString *) *) + *ret; if (_percentEncodedQuery == nil) return nil; pool = objc_autoreleasePoolPush(); pairs = [_percentEncodedQuery componentsSeparatedByString: @"&"]; - ret = [OFMutableDictionary dictionaryWithCapacity: pairs.count]; + ret = [OFMutableArray arrayWithCapacity: pairs.count]; for (OFString *pair in pairs) { OFArray *parts = [pair componentsSeparatedByString: @"="]; OFString *name, *value; @@ -1064,11 +1066,12 @@ name = [[parts objectAtIndex: 0] stringByRemovingPercentEncoding]; value = [[parts objectAtIndex: 1] stringByRemovingPercentEncoding]; - [ret setObject: value forKey: name]; + [ret addObject: [OFPair pairWithFirstObject: name + secondObject: value]]; } [ret makeImmutable]; [ret retain]; Index: tests/OFURITests.m ================================================================== --- tests/OFURITests.m +++ tests/OFURITests.m @@ -171,14 +171,14 @@ [[[OFURI URIWithString: @"http://host/"] lastPathComponent] isEqual: @"/"] && [URI5.lastPathComponent isEqual: @"foo/bar"]) TEST(@"-[query]", [URI1.query isEqual: @"que#ry=1&f&oo=b=ar"] && URI4.query == nil) - TEST(@"-[queryDictionary]", - [URI1.queryDictionary isEqual: - [OFDictionary dictionaryWithKeysAndObjects: - @"que#ry", @"1", @"f&oo", @"b=ar", nil]]); + TEST(@"-[queryItems]", + [URI1.queryItems isEqual: [OFArray arrayWithObjects: + [OFPair pairWithFirstObject: @"que#ry" secondObject: @"1"], + [OFPair pairWithFirstObject: @"f&oo" secondObject: @"b=ar"], nil]]); TEST(@"-[fragment]", [URI1.fragment isEqual: @"frag#ment"] && URI4.fragment == nil) TEST(@"-[copy]", R(URI4 = [[URI1 copy] autorelease])) @@ -287,15 +287,15 @@ EXPECT_EXCEPTION( @"-[setPercentEncodedQuery:] with invalid characters fails", OFInvalidFormatException, mutableURI.percentEncodedQuery = @"`") - TEST(@"-[setQueryDictionary:]", - (mutableURI.queryDictionary = - [OFDictionary dictionaryWithKeysAndObjects: - @"foo&bar", @"baz=qux", @"f=oobar", @"b&azqux", nil]) && - [mutableURI.percentEncodedQuery isEqual: + TEST(@"-[setQueryItems:]", + (mutableURI.queryItems = [OFArray arrayWithObjects: + [OFPair pairWithFirstObject: @"foo&bar" secondObject: @"baz=qux"], + [OFPair pairWithFirstObject: @"f=oobar" secondObject: @"b&azqux"], + nil]) && [mutableURI.percentEncodedQuery isEqual: @"foo%26bar=baz%3Dqux&f%3Doobar=b%26azqux"]) TEST(@"-[setFragment:]", (mutableURI.fragment = @"frag/ment?#") && [mutableURI.percentEncodedFragment isEqual: @"frag/ment?%23"])