diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 0bc6af0..288d887 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -46,6 +46,11 @@ 6238C9851AB8D6330006512E /* HTMLKitExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238C9831AB8D6330006512E /* HTMLKitExceptions.h */; }; 6238C9861AB8D6330006512E /* HTMLKitExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */; }; 6238C9871AB8D6330006512E /* HTMLKitExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */; }; + 623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */; }; + 623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; }; + 623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; }; + 623916C71AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */; }; + 623916C81AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */; }; 624493A619CCC54100BCDDF4 /* HTMLTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A419CCC54100BCDDF4 /* HTMLTokenizer.h */; }; 624493A719CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */; }; 624493A819CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */; }; @@ -163,6 +168,9 @@ 6238579A1A9E8934003A45D9 /* HTMLComment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLComment.m; sourceTree = ""; }; 6238C9831AB8D6330006512E /* HTMLKitExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitExceptions.h; sourceTree = ""; }; 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitExceptions.m; sourceTree = ""; }; + 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTreeEnumerator.h; sourceTree = ""; }; + 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTreeEnumerator.m; sourceTree = ""; }; + 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodeTreeEnumratorTests.m; sourceTree = ""; }; 624493A419CCC54100BCDDF4 /* HTMLTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizer.h; sourceTree = ""; }; 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTokenizer.m; sourceTree = ""; }; 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerStates.h; sourceTree = ""; }; @@ -282,6 +290,8 @@ 6238579A1A9E8934003A45D9 /* HTMLComment.m */, 62362A3A1A9FA70400301989 /* HTMLText.h */, 62362A3B1A9FA70400301989 /* HTMLText.m */, + 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */, + 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */, ); name = Nodes; sourceTree = ""; @@ -372,6 +382,7 @@ 625A14CA19C7829400AD0C32 /* Tests */ = { isa = PBXGroup; children = ( + 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */, 6236738C1AC0CD2400FF89B3 /* Tokenizer */, 625A14CB19C7829400AD0C32 /* Supporting Files */, ); @@ -430,6 +441,7 @@ 6235CE9B1AA509430026937B /* NSString+HTMLKit.h in Headers */, 62F873EB19E088C90062683C /* HTMLParser.h in Headers */, 624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */, + 623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */, 62AE594419F992F30043F069 /* HTMLCommentToken.h in Headers */, 624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */, 6235CEA01AA5170A0026937B /* HTMLMarker.h in Headers */, @@ -611,6 +623,7 @@ 62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */, 62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */, 62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */, + 623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */, 62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, 6238C9871AB8D6330006512E /* HTMLKitExceptions.m in Sources */, 62F873ED19E088C90062683C /* HTMLParser.m in Sources */, @@ -634,6 +647,7 @@ files = ( 6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, 624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, + 623916C81AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */, 62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -652,6 +666,7 @@ 62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */, 62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */, 62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */, + 623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */, 62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, 6238C9861AB8D6330006512E /* HTMLKitExceptions.m in Sources */, 62F873EC19E088C90062683C /* HTMLParser.m in Sources */, @@ -675,6 +690,7 @@ files = ( 6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, 624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, + 623916C71AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */, 62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index 2347e43..70bbdfe 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -77,4 +77,7 @@ typedef NS_ENUM(short, HTMLNodeType) - (HTMLNode *)removeChildNodeAtIndex:(NSUInteger)index; +- (NSEnumerator *)treeEnumerator; +- (NSEnumerator *)reverseTreeEnumerator; + @end diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 8c876e8..46ff4c4 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -10,6 +10,7 @@ #import "HTMLDocument.h" #import "HTMLElement.h" #import "HTMLKitExceptions.h" +#import "HTMLNodeTreeEnumerator.h" @interface HTMLNode () { @@ -180,6 +181,18 @@ return [self removeChildNode:node]; } +#pragma mark - Enumeration + +- (NSEnumerator *)treeEnumerator +{ + return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:NO]; +} + +- (NSEnumerator *)reverseTreeEnumerator +{ + return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:YES]; +} + #pragma mark - Mutation Algorithms - (HTMLNode *)preInsertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child diff --git a/HTMLKit/HTMLNodeTreeEnumerator.h b/HTMLKit/HTMLNodeTreeEnumerator.h new file mode 100644 index 0000000..baf7243 --- /dev/null +++ b/HTMLKit/HTMLNodeTreeEnumerator.h @@ -0,0 +1,17 @@ +// +// HTMLNodeTreeEnumerator.h +// HTMLKit +// +// Created by Iska on 28/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import + +@class HTMLNode; + +@interface HTMLNodeTreeEnumerator : NSEnumerator + +- (instancetype)initWithNode:(HTMLNode *)node reverse:(BOOL)reverse; + +@end diff --git a/HTMLKit/HTMLNodeTreeEnumerator.m b/HTMLKit/HTMLNodeTreeEnumerator.m new file mode 100644 index 0000000..8f82972 --- /dev/null +++ b/HTMLKit/HTMLNodeTreeEnumerator.m @@ -0,0 +1,54 @@ +// +// HTMLNodeTreeEnumerator.m +// HTMLKit +// +// Created by Iska on 28/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNodeTreeEnumerator.h" +#import "HTMLNode.h" + +@interface HTMLNodeTreeEnumerator () +{ + BOOL _reverse; + NSMutableArray *_stack; +} +@end + +@implementation HTMLNodeTreeEnumerator + +- (instancetype)initWithNode:(HTMLNode *)node reverse:(BOOL)reverse +{ + self = [super init]; + if (self) { + _reverse = reverse; + _stack = [[NSMutableArray alloc] initWithObjects:node, nil]; + } + return self; +} + +- (id)nextObject +{ + if (_stack.count == 0) { + return nil; + } + + HTMLNode *node = _stack.lastObject; + [_stack removeLastObject]; + + NSArray *childNodes = node.childNodes.array; + if (childNodes != nil && childNodes.count > 0) { + if (childNodes.count > 1) { + NSRange range = NSMakeRange(_reverse ? 0 : 1, childNodes.count - 1); + NSArray *rest = [childNodes subarrayWithRange:range]; + + [_stack addObjectsFromArray:_reverse ? rest : rest.reverseObjectEnumerator.allObjects]; + } + [_stack addObject:_reverse ? childNodes.lastObject : childNodes.firstObject]; + } + + return node; +} + +@end diff --git a/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m b/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m new file mode 100644 index 0000000..654fbcf --- /dev/null +++ b/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m @@ -0,0 +1,148 @@ +// +// HTMLKitNodeTreeEnumratorTests.m +// HTMLKit +// +// Created by Iska on 28/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import + +#import "HTMLNodes.h" + +@interface HTMLKitNodeTreeEnumratorTests : XCTestCase + +@end + +@implementation HTMLKitNodeTreeEnumratorTests + +#pragma mark - Elements + +- (HTMLElement *)div +{ + return [[HTMLElement alloc] initWithTagName:@"div"]; +} + +- (HTMLElement *)simpleTree +{ + /* + | div + | a + | b + | c + */ + HTMLElement *div = self.div; + [div appendNode:[[HTMLElement alloc] initWithTagName:@"a"]]; + [div appendNode:[[HTMLElement alloc] initWithTagName:@"b"]]; + [div appendNode:[[HTMLElement alloc] initWithTagName:@"c"]]; + return div; +} + +- (HTMLElement *)nestedSimpleTree +{ + /* + | div + | div + | a + | b + | c + | div + | a + | b + | c + */ + HTMLElement *div = self.div; + [div appendNode:self.simpleTree]; + [div appendNode:self.simpleTree]; + return div; +} + +- (HTMLElement *)complexTree +{ + /* + | div + | div + | div + | a + | b + | c + | e + | f + | div + | a + | b + | c + */ + HTMLElement *root = self.div; + + HTMLElement *div = self.div; + [div appendNode:self.simpleTree]; + [root appendNode:div]; + + HTMLElement *e = [[HTMLElement alloc] initWithTagName:@"e"]; + [e appendNode:[[HTMLElement alloc] initWithTagName:@"f"]]; + [root appendNode:e]; + [root appendNode:self.simpleTree]; + + return root; +} + +#pragma mark - Tests + +- (void)testSingle +{ + HTMLElement *div = self.div; + NSArray *result = div.treeEnumerator.allObjects; + NSArray *expected = @[@"div"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testSimpleTree +{ + HTMLElement *tree = self.simpleTree; + NSArray *result = tree.treeEnumerator.allObjects; + NSArray *expected = @[@"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testSimpleTreeReversed +{ + HTMLElement *tree = self.simpleTree; + NSArray *result = tree.reverseTreeEnumerator.allObjects; + NSArray *expected = @[@"div", @"c", @"b", @"a"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testNestedSimpleTree +{ + HTMLElement *tree = self.nestedSimpleTree; + NSArray *result = tree.treeEnumerator.allObjects; + NSArray *expected = @[@"div", @"div", @"a", @"b", @"c", @"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testNestedSimpleTreeReversed +{ + HTMLElement *tree = self.nestedSimpleTree; + NSArray *result = tree.reverseTreeEnumerator.allObjects; + NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"div", @"c", @"b", @"a"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testComplexSimpleTree +{ + HTMLElement *tree = self.complexTree; + NSArray *result = tree.treeEnumerator.allObjects; + NSArray *expected = @[@"div", @"div",@"div", @"a", @"b", @"c", @"e", @"f", @"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testComplexSimpleTreeReversed +{ + HTMLElement *tree = self.complexTree; + NSArray *result = tree.reverseTreeEnumerator.allObjects; + NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"e", @"f", @"div", @"div", @"c", @"b", @"a"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +@end