Add implementation for the Tree Walker

https://dom.spec.whatwg.org/#interface-treewalker
This commit is contained in:
iska
2015-06-05 22:46:41 +02:00
parent 0d7c57d755
commit 0c82f6891b
5 changed files with 278 additions and 1 deletions
+10
View File
@@ -71,6 +71,9 @@
6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; };
624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; };
624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; };
624717BC1B22009200B38302 /* HTMLTreeWalker.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717BA1B22009200B38302 /* HTMLTreeWalker.h */; };
624717BD1B22009200B38302 /* HTMLTreeWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717BB1B22009200B38302 /* HTMLTreeWalker.m */; };
624717BE1B22009200B38302 /* HTMLTreeWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717BB1B22009200B38302 /* HTMLTreeWalker.m */; };
6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */; };
6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; };
6247A9451B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; };
@@ -219,6 +222,8 @@
624717161B22333200C11912 /* HTMLNodeTraversal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTraversal.h; sourceTree = "<group>"; };
624717171B22333200C11912 /* HTMLNodeTraversal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTraversal.m; sourceTree = "<group>"; };
624717B71B21FE5400B38302 /* HTMLNodeFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeFilter.m; sourceTree = "<group>"; };
624717BA1B22009200B38302 /* HTMLTreeWalker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTreeWalker.h; sourceTree = "<group>"; };
624717BB1B22009200B38302 /* HTMLTreeWalker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTreeWalker.m; sourceTree = "<group>"; };
6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeIterator.h; sourceTree = "<group>"; };
6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeIterator.m; sourceTree = "<group>"; };
6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeFilter.h; sourceTree = "<group>"; };
@@ -345,6 +350,8 @@
623406E01ADB04F9004677A3 /* HTMLTemplate.m */,
6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */,
6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */,
624717BA1B22009200B38302 /* HTMLTreeWalker.h */,
624717BB1B22009200B38302 /* HTMLTreeWalker.m */,
624717161B22333200C11912 /* HTMLNodeTraversal.h */,
624717171B22333200C11912 /* HTMLNodeTraversal.m */,
6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */,
@@ -596,6 +603,7 @@
623857961A9E8606003A45D9 /* HTMLDocumentType.h in Headers */,
623406E61ADB05AD004677A3 /* HTMLDocumentFragment.h in Headers */,
62363C3D1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h in Headers */,
624717BC1B22009200B38302 /* HTMLTreeWalker.h in Headers */,
62362A3C1A9FA70400301989 /* HTMLText.h in Headers */,
6234584E1A9D2FA4009BD491 /* HTMLNode.h in Headers */,
);
@@ -755,6 +763,7 @@
624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */,
6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */,
624717BE1B22009200B38302 /* HTMLTreeWalker.m in Sources */,
62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */,
62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */,
62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */,
@@ -809,6 +818,7 @@
624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */,
623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */,
624717BD1B22009200B38302 /* HTMLTreeWalker.m in Sources */,
62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */,
62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */,
62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */,
+1
View File
@@ -13,4 +13,5 @@
extern HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root);
extern HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root);
extern HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root);
extern BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node);
+16 -1
View File
@@ -46,7 +46,22 @@ HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root)
return nil;
}
extern BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node)
HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root)
{
do {
if (node == root) {
return nil;
}
if (node.nextSibling != nil) {
return node.nextSibling;
}
node = node.parentNode;
} while (node != nil);
return nil;
}
BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node)
{
unsigned long nthBit = (1 << (node.nodeType - 1)) & whatToShow;
if (!nthBit) {
+29
View File
@@ -0,0 +1,29 @@
//
// HTMLTreeWalker.h
// HTMLKit
//
// Created by Iska on 05/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "HTMLNodeFilter.h"
@class HTMLNode;
@interface HTMLTreeWalker : NSObject
@property (nonatomic, strong, readonly) HTMLNode *root;
@property (nonatomic, assign, readonly) HTMLNodeFilterShowOptions whatToShow;
@property (nonatomic, strong, readonly) id<HTMLNodeFilter> filter;
@property (nonatomic, strong) HTMLNode *currentNode;
- (HTMLNode *)parentNode;
- (HTMLNode *)firstChild;
- (HTMLNode *)lastChild;
- (HTMLNode *)previousSibling;
- (HTMLNode *)nextSibling;
- (HTMLNode *)previousNode;
- (HTMLNode *)nextNode;
@end
+222
View File
@@ -0,0 +1,222 @@
//
// HTMLTreeWalker.m
// HTMLKit
//
// Created by Iska on 05/06/15.
// Copyright (c) 2015 BrainCookie. All rights reserved.
//
#import "HTMLTreeWalker.h"
#import "HTMLNode.h"
#import "HTMLNodeFilter.h"
#import "HTMLNodeTraversal.h"
typedef NS_ENUM(short, HTMLTreeWalkerChildrenType)
{
HTMLTreeWalkerChildrenTypeFirst,
HTMLTreeWalkerChildrenTypeLast
};
typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType)
{
HTMLTreeWalkerSiblingsTypeNext,
HTMLTreeWalkerSiblingsTypePrevious
};
@implementation HTMLTreeWalker
#pragma mark - Lifecycle
- (instancetype)initWithNode:(HTMLNode *)node
showOptions:(HTMLNodeFilterShowOptions)showOptions
filter:(id<HTMLNodeFilter>)filter
{
self = [super init];
if (self) {
_root = node;
_filter = filter;
_whatToShow = showOptions;
_currentNode = _root;
}
return self;
}
#pragma mark - Traversal
- (HTMLNode *)traverseChildrenOfType:(HTMLTreeWalkerChildrenType)type
{
HTMLNode *node = _currentNode;
node = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode;
while (node != nil) {
BOOL result = FilterNode(self.filter, self.whatToShow, node);
if (result) {
_currentNode = node;
return node;
}
HTMLNode *child = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode;
if (child != nil) {
node = child;
continue;
}
while (node != nil) {
HTMLNode *sibling = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.nextSibling : node.previousSibling;
if (sibling != nil) {
node = sibling;
continue;
}
HTMLNode *parent = node.parentNode;
if (parent == nil || parent == _root || parent == _currentNode) {
return nil;
}
node = parent;
}
}
return nil;
}
- (HTMLNode *)parentNode
{
HTMLNode *node = _currentNode;
while (node != nil && node != _root) {
node = node.parentNode;
if (node != nil && FilterNode(self.filter, self.whatToShow, node)) {
_currentNode = node;
return node;
}
}
return nil;
}
- (HTMLNode *)firstChild
{
return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeFirst];
}
- (HTMLNode *)lastChild
{
return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeLast];
}
- (HTMLNode *)traverseSiblingsOfType:(HTMLTreeWalkerSiblingsType)type
{
HTMLNode *node = _currentNode;
if (node == _root) {
return nil;
}
while (YES) {
HTMLNode *sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling;
while (sibling != nil) {
node = sibling;
BOOL result = FilterNode(self.filter, self.whatToShow, node);
if (result) {
_currentNode = node;
return node;
}
HTMLNode *sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.firstChiledNode : node.lastChildNode;
if (sibling == nil) {
sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling;
}
}
node = node.parentNode;
if (node == nil || node == _root) {
return nil;
}
if (FilterNode(self.filter, self.whatToShow, node)) {
return nil;
}
}
return nil;
}
- (HTMLNode *)previousSibling
{
return [self traverseSiblingsOfType:HTMLTreeWalkerSiblingsTypePrevious];
}
- (HTMLNode *)nextSibling
{
return [self traverseSiblingsOfType:HTMLTreeWalkerSiblingsTypeNext];
}
- (HTMLNode *)previousNode
{
HTMLNode *node = _currentNode;
while (node != _root) {
HTMLNode *sibling = node.previousSibling;
while (sibling != nil) {
node = sibling;
BOOL result = FilterNode(self.filter, self.whatToShow, node);
while (result && node.hasChildNodes) {
node = node.lastChildNode;
result = FilterNode(self.filter, self.whatToShow, node);
}
if (result) {
_currentNode = node;
return node;
}
sibling = node.previousSibling;
}
if (node == _root || node.parentNode == nil) {
return nil;
}
node = node.parentNode;
if (FilterNode(self.filter, self.whatToShow, node)) {
_currentNode = node;
return node;
}
}
return nil;
}
- (HTMLNode *)nextNode
{
HTMLNode *node = _currentNode;
BOOL result = YES;
while (YES) {
while (!result && node.hasChildNodes) {
node = node.firstChiledNode;
result = FilterNode(self.filter, self.whatToShow, node);
if (result) {
_currentNode = node;
return node;
}
}
HTMLNode *nextSibling;
while ((nextSibling = FollowingNodeSkippingChildren(node, _root)) != nil) {
node = nextSibling;
result = FilterNode(self.filter, self.whatToShow, node);
if (result) {
_currentNode = node;
return node;
}
break;
}
}
return nil;
}
@end