diff --git a/Code/HTMLNode.h b/Code/HTMLNode.h index 90042a3..9d943d8 100644 --- a/Code/HTMLNode.h +++ b/Code/HTMLNode.h @@ -39,6 +39,16 @@ NS_ASSUME_NONNULL_BEGIN /// Convenience method that returns a mutable proxy for children. The proxy returned by -mutableChildren is much faster than the one obtained by calling -mutableOrderedSetValueForKey: yourself. @property (readonly, nonatomic) HTMLMutableOrderedSetOf(HTMLNode *) *mutableChildren; +/** + Add a child to the end of the node's set of children, removing it from its current parentNode's set of children. If the child is already in the node's set of children, nothing happens. + */ +- (void)addChild:(HTMLNode *)child; + +/** + Remove a child from the node's set of children. If the child is not in the node's set of children, nothing happens. + */ +- (void)removeChild:(HTMLNode *)child; + /** The number of nodes that have the node as their parent. diff --git a/Code/HTMLNode.m b/Code/HTMLNode.m index 8a407aa..f305234 100644 --- a/Code/HTMLNode.m +++ b/Code/HTMLNode.m @@ -93,6 +93,20 @@ - (void)removeFromParentNode return [[HTMLChildrenRelationshipProxy alloc] initWithNode:self children:_children]; } +- (void)addChild:(HTMLNode *)child +{ + NSParameterAssert(child); + + [self.mutableChildren addObject:child]; +} + +- (void)removeChild:(HTMLNode *)child +{ + NSParameterAssert(child); + + [self.mutableChildren removeObject:child]; +} + - (NSUInteger)numberOfChildren { return _children.count; @@ -110,15 +124,19 @@ - (NSUInteger)indexOfChild:(HTMLNode *)child - (void)insertObject:(HTMLNode *)node inChildrenAtIndex:(NSUInteger)index { + if ([_children containsObject:node]) { + return; + } [_children insertObject:node atIndex:index]; [node setParentNode:self updateChildren:NO]; } - (void)insertChildren:(NSArray *)array atIndexes:(NSIndexSet *)indexes { - [_children insertObjects:array atIndexes:indexes]; - for (HTMLNode *node in array) { - [node setParentNode:self updateChildren:NO]; + NSUInteger nextIndex = indexes.firstIndex; + for (HTMLNode *child in array) { + [self insertObject:child inChildrenAtIndex:nextIndex]; + nextIndex = [indexes indexGreaterThanIndex:nextIndex]; } } diff --git a/Tests/HTMLNodeTests.m b/Tests/HTMLNodeTests.m index 8ce8028..d4baba5 100644 --- a/Tests/HTMLNodeTests.m +++ b/Tests/HTMLNodeTests.m @@ -101,6 +101,34 @@ - (void)testNode XCTAssertNil(comment.document); } +- (void)testAddRemoveChild +{ + HTMLComment *comment = [HTMLComment new]; + + XCTAssertNil(comment.document); + XCTAssertTrue(_document.children.count == 0); + [_document addChild:comment]; + XCTAssertEqualObjects(comment.document, _document); + XCTAssertEqualObjects(_document.children.array, (@[ comment ])); + [_document removeChild:comment]; + XCTAssertNil(comment.document); + XCTAssertTrue(_document.children.count == 0); + + [_document removeChild:comment]; + XCTAssertTrue(_document.children.count == 0); + + HTMLElement *element = [HTMLElement new]; + [_document addChild:comment]; + [_document addChild:element]; + XCTAssertEqualObjects(_document.children.array, (@[ comment, element ])); + [_document addChild:comment]; + XCTAssertEqualObjects(_document.children.array, (@[ comment, element ])); + + HTMLTextNode *text = [HTMLTextNode new]; + [_document removeChild:text]; + XCTAssertEqualObjects(_document.children.array, (@[ comment, element ])); +} + - (void)testRemoveFromParentNode { HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p" attributes:nil];