diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 909fc1f..a852c15 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 623424861AB467B200726190 /* HTMLOrderedDictionary.h */; }; + 623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 623424871AB467B200726190 /* HTMLOrderedDictionary.m */; }; + 6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 623424871AB467B200726190 /* HTMLOrderedDictionary.m */; }; 6234584E1A9D2FA4009BD491 /* HTMLNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234584C1A9D2FA4009BD491 /* HTMLNode.h */; }; 6234584F1A9D2FA4009BD491 /* HTMLNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234584D1A9D2FA4009BD491 /* HTMLNode.m */; }; 623458501A9D2FA4009BD491 /* HTMLNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234584D1A9D2FA4009BD491 /* HTMLNode.m */; }; @@ -123,10 +126,13 @@ /* Begin PBXFileReference section */ 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLElementTypes.h; sourceTree = ""; }; + 623424861AB467B200726190 /* HTMLOrderedDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLOrderedDictionary.h; sourceTree = ""; }; + 623424871AB467B200726190 /* HTMLOrderedDictionary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLOrderedDictionary.m; sourceTree = ""; }; 6234584C1A9D2FA4009BD491 /* HTMLNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNode.h; sourceTree = ""; }; 6234584D1A9D2FA4009BD491 /* HTMLNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNode.m; sourceTree = ""; }; 6234BEEC1AABBF1400DEB15F /* HTMLStackOfOpenElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLStackOfOpenElements.h; sourceTree = ""; }; 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLStackOfOpenElements.m; sourceTree = ""; }; + 6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLElementAdjustment.h; sourceTree = ""; }; 6235CE991AA509430026937B /* NSString+HTMLKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+HTMLKit.h"; sourceTree = ""; }; 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+HTMLKit.m"; sourceTree = ""; }; 6235CE9E1AA5170A0026937B /* HTMLMarker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLMarker.h; sourceTree = ""; }; @@ -309,9 +315,12 @@ 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */, 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */, 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */, + 6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */, 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */, 6235CE9E1AA5170A0026937B /* HTMLMarker.h */, 6235CE9F1AA5170A0026937B /* HTMLMarker.m */, + 623424861AB467B200726190 /* HTMLOrderedDictionary.h */, + 623424871AB467B200726190 /* HTMLOrderedDictionary.m */, 6235CE991AA509430026937B /* NSString+HTMLKit.h */, 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */, 623719441AA1472B002E03C8 /* Nodes */, @@ -404,6 +413,7 @@ 623857911A9E772B003A45D9 /* HTMLDocument.h in Headers */, 624493A619CCC54100BCDDF4 /* HTMLTokenizer.h in Headers */, 62AE593A19F97E1C0043F069 /* HTMLDOCTYPEToken.h in Headers */, + 623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */, 625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */, 625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */, 6238579B1A9E8934003A45D9 /* HTMLComment.h in Headers */, @@ -567,6 +577,7 @@ 6234BEF01AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */, 624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */, 6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */, + 6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */, 62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */, 62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */, 62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, @@ -604,6 +615,7 @@ 6234BEEF1AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */, 624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */, 6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */, + 623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */, 62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */, 62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */, 62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, diff --git a/HTMLKit/HTMLElementAdjustment.h b/HTMLKit/HTMLElementAdjustment.h new file mode 100644 index 0000000..138f30d --- /dev/null +++ b/HTMLKit/HTMLElementAdjustment.h @@ -0,0 +1,13 @@ +// +// HTMLElementAdjustment.h +// HTMLKit +// +// Created by Iska on 14/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#ifndef HTMLKit_HTMLElementAdjustment_h +#define HTMLKit_HTMLElementAdjustment_h + + +#endif diff --git a/HTMLKit/HTMLOrderedDictionary.h b/HTMLKit/HTMLOrderedDictionary.h new file mode 100644 index 0000000..d936243 --- /dev/null +++ b/HTMLKit/HTMLOrderedDictionary.h @@ -0,0 +1,25 @@ +// +// HTMLOrderedDictionary.h +// HTMLKit +// +// Created by Iska on 14/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import + +@interface HTMLOrderedDictionary : NSMutableDictionary + +- (id)objectAtIndex:(NSUInteger)index; +- (void)setObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)index; +- (void)removeObjectAtIndex:(NSUInteger)index; +- (void)replaceKeyValueAtIndex:(NSUInteger)index withObject:(id)anObject andKey:(id)aKey; +- (void)replaceKey:(id)aKey withKey:(id)newKey; +- (NSUInteger)indexOfKey:(id)aKey; + +- (id)objectAtIndexedSubscript:(NSUInteger)index; +- (void)setObject:(id)obj forIndexedSubscript:(NSUInteger)index; + +- (NSEnumerator *)reverseKeyEnumerator; + +@end diff --git a/HTMLKit/HTMLOrderedDictionary.m b/HTMLKit/HTMLOrderedDictionary.m new file mode 100644 index 0000000..0354201 --- /dev/null +++ b/HTMLKit/HTMLOrderedDictionary.m @@ -0,0 +1,172 @@ +// +// HTMLOrderedDictionary.m +// HTMLKit +// +// Created by Iska on 14/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLOrderedDictionary.h" + +@interface HTMLOrderedDictionary () +{ + NSMutableDictionary *_dictionary; + NSMutableArray *_keys; +} +@end + +@implementation HTMLOrderedDictionary + +#pragma mark - Init + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" +- (instancetype)init +{ + return [self initWithCapacity:0]; +} +#pragma clang diagnostic pop + +- (instancetype)initWithCapacity:(NSUInteger)capacity +{ + self = [super init]; + if (self) { + _dictionary = [[NSMutableDictionary alloc] initWithCapacity:capacity]; + _keys = [[NSMutableArray alloc] initWithCapacity:capacity]; + } + return self; +} + +- (instancetype)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys +{ + self = [self initWithCapacity:objects.count]; + if (self) { + [objects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + _dictionary[keys[idx]] = obj; + }]; + } + return self; +} + +#pragma mark - Access + +- (id)objectForKey:(id)aKey +{ + return _dictionary[aKey]; +} + +- (void)setObject:(id)anObject forKey:(id)aKey +{ + if (_dictionary[aKey] != nil) { + [_keys addObject:aKey]; + } + _dictionary[aKey] = anObject; +} + +- (void)removeObjectForKey:(id)aKey +{ + [_keys removeObject:aKey]; + [_dictionary removeObjectForKey:aKey]; +} + +- (NSUInteger)count +{ + return _keys.count; +} + +#pragma mark - Indexed Access + +- (id)objectAtIndex:(NSUInteger)index +{ + if (index >= _keys.count) { + return nil; + } + return _dictionary[_keys[index]]; +} + +- (void)setObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)index +{ + if (index >= _keys.count) { + return; + } + [_keys replaceObjectAtIndex:index withObject:aKey]; + _dictionary[aKey] = anObject; +} + +- (void)removeObjectAtIndex:(NSUInteger)index +{ + if (index >= _keys.count) { + return; + } + if (_dictionary[_keys[index]]){ + [_dictionary removeObjectForKey:_keys[index]]; + [_keys removeObjectAtIndex:index]; + } +} + +- (void)replaceKeyValueAtIndex:(NSUInteger)index withObject:(id)anObject andKey:(id)aKey +{ + if (index >= _keys.count) { + return; + } + [_dictionary removeObjectForKey:_keys[index]]; + [_keys replaceObjectAtIndex:index withObject:aKey]; + _dictionary[aKey] = anObject; +} + +- (void)replaceKey:(id)aKey withKey:(id)newKey +{ + id value = _dictionary[aKey]; + if (value != nil) { + NSUInteger index = [_keys indexOfObject:aKey]; + [_keys replaceObjectAtIndex:index withObject:newKey]; + [_dictionary removeObjectForKey:aKey]; + _dictionary[newKey] = value; + } +} + +- (NSUInteger)indexOfKey:(id)aKey +{ + return [_keys indexOfObject:aKey]; +} + +#pragma mark - Subscript + +- (id)objectAtIndexedSubscript:(NSUInteger)index +{ + if (index >= _keys.count) { + return nil; + } + return _dictionary[_keys[index]]; +} + +- (void)setObject:(id)obj forIndexedSubscript:(NSUInteger)index +{ + if (index < _keys.count) { + _dictionary[_keys[index]] = obj; + } +} + +- (void)setObject:(id)obj forKeyedSubscript:(id)key +{ + [self setObject:obj forKey:key]; +} + +#pragma mark - Enumeration + +- (NSEnumerator *)keyEnumerator +{ + return _keys.objectEnumerator; +} + +- (NSEnumerator *)reverseKeyEnumerator +{ + return _keys.reverseObjectEnumerator; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id [])buffer count:(NSUInteger)len +{ + return [_keys countByEnumeratingWithState:state objects:buffer count:len]; +} + +@end diff --git a/HTMLKit/HTMLTagToken.h b/HTMLKit/HTMLTagToken.h index 7b9987f..2107f58 100644 --- a/HTMLKit/HTMLTagToken.h +++ b/HTMLKit/HTMLTagToken.h @@ -8,12 +8,12 @@ #import #import "HTMLToken.h" +#import "HTMLOrderedDictionary.h" @interface HTMLTagToken : HTMLToken @property (nonatomic, copy) NSString *tagName; -#warning Implement Ordered Dictionary -@property (nonatomic, strong) NSMutableDictionary *attributes; +@property (nonatomic, strong) HTMLOrderedDictionary *attributes; @property (nonatomic, assign, getter = isSelfClosing) BOOL selfClosing; - (instancetype)initWithTagName:(NSString *)tagName; diff --git a/HTMLKit/HTMLTagToken.m b/HTMLKit/HTMLTagToken.m index 39eee74..31be545 100644 --- a/HTMLKit/HTMLTagToken.m +++ b/HTMLKit/HTMLTagToken.m @@ -11,8 +11,7 @@ @interface HTMLTagToken () { NSMutableString *_tagName; -#warning Implement Ordered Dictionary - NSMutableDictionary *_attributes; + HTMLOrderedDictionary *_attributes; } @end @@ -30,8 +29,8 @@ self = [super init]; if (self) { _tagName = [tagName mutableCopy]; - _attributes = [NSMutableDictionary new]; if (attributes != nil) { + _attributes = [HTMLOrderedDictionary new]; [_attributes addEntriesFromDictionary:attributes]; } } @@ -48,6 +47,7 @@ @end +#pragma mark - Start Tag Token @implementation HTMLStartTagToken @@ -65,8 +65,6 @@ return self; } -#pragma mark - NSObject - - (BOOL)isEqual:(id)other { if ([other isKindOfClass:[self class]]) { @@ -90,6 +88,8 @@ @end +#pragma mark - End Tag Token + @implementation HTMLEndTagToken - (instancetype)initWithTagName:(NSString *)tagName @@ -106,8 +106,6 @@ return self; } -#pragma mark - NSObject - - (BOOL)isEqual:(id)other { if ([other isKindOfClass:[self class]]) { diff --git a/HTMLKit/HTMLTokenizer.m b/HTMLKit/HTMLTokenizer.m index ef41dad..a4c4df4 100644 --- a/HTMLKit/HTMLTokenizer.m +++ b/HTMLKit/HTMLTokenizer.m @@ -241,7 +241,7 @@ { if (_currentAttributeName != nil) { if (_currentTagToken.attributes == nil) { - _currentTagToken.attributes = [NSMutableDictionary new]; + _currentTagToken.attributes = [HTMLOrderedDictionary new]; } if (_currentTagToken.attributes[_currentAttributeName] != nil) { diff --git a/HTMLKitTests/HTML5LibTest.m b/HTMLKitTests/HTML5LibTest.m index 2e6eff1..da12b7d 100644 --- a/HTMLKitTests/HTML5LibTest.m +++ b/HTMLKitTests/HTML5LibTest.m @@ -107,7 +107,7 @@ HTMLStartTagToken *token = [[HTMLStartTagToken alloc] initWithTagName:data]; NSDictionary *attributes = output[2]; if (attributes && attributes.allKeys.count > 0) { - token.attributes = [NSMutableDictionary new]; + token.attributes = [HTMLOrderedDictionary new]; } for (NSString *name in attributes) { NSString *value = [attributes objectForKey:name]; diff --git a/HTMLKitTests/HTMLTokenizerTests.m b/HTMLKitTests/HTMLTokenizerTests.m index fa9ddd9..d5a369b 100644 --- a/HTMLKitTests/HTMLTokenizerTests.m +++ b/HTMLKitTests/HTMLTokenizerTests.m @@ -58,7 +58,7 @@ static NSString * const TOKENIZER = @"tokenizer"; NSArray *expectedTokens = test.output; NSArray *tokens = [tokenizer allTokens]; - XCTAssertEqualObjects(tokens, expectedTokens, @"%@", test.description); + XCTAssertEqualObjects(tokens, expectedTokens, @"%@", test.title); } } }