Skip to content

Commit 8bcdec1

Browse files
committed
Fixed all the tests and use a DelegatingList for the NodeList.
1 parent 58f71d2 commit 8bcdec1

File tree

6 files changed

+175
-135
lines changed

6 files changed

+175
-135
lines changed

lib/xml/nodes/node.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,6 @@ abstract class XmlNode extends Object with XmlVisitable, XmlWritable, XmlOwned {
8383
return null;
8484
}
8585

86-
/// Return a copy of this node.
87-
XmlNode clone() => const XmlTransformer().visit(this);
86+
/// Return a copy of this node and its subtree.
87+
XmlNode copy() => const XmlTransformer().visit(this);
8888
}

lib/xml/utils/errors.dart

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,31 @@ library xml.utils.errors;
22

33
import 'package:xml/xml/nodes/node.dart' show XmlNode;
44
import 'package:xml/xml/utils/node_type.dart' show XmlNodeType;
5+
import 'package:xml/xml/utils/owned.dart' show XmlOwned;
56

67
class XmlNodeTypeError extends ArgumentError {
8+
/// Ensure that [node] is not null.
9+
static void checkNotNull(XmlNode node) {
10+
if (node == null) {
11+
throw new XmlNodeTypeError('Node must not be null.');
12+
}
13+
}
714

8-
static void checkValidType(XmlNode node, Iterable<XmlNodeType> validTypes) {
9-
if (!validTypes.contains(node.nodeType)) {
10-
throw new XmlNodeTypeError('Expected node of type: $validTypes');
15+
/// Ensure that [node] is of one of the provided [types].
16+
static void checkValidType(XmlNode node, Iterable<XmlNodeType> types) {
17+
if (!types.contains(node.nodeType)) {
18+
throw new XmlNodeTypeError('Expected node of type: $types');
1119
}
1220
}
1321

1422
XmlNodeTypeError(String message) : super(message);
1523
}
1624

1725
class XmlParentError extends ArgumentError {
18-
19-
static void checkAttached(XmlNode parent, XmlNode child) {
20-
if (child.parent == parent) {
21-
throw new XmlParentError('Nodes does not have a parent: $child');
22-
}
23-
}
24-
25-
static void checkDetached(XmlNode node) {
26-
if (node.hasParent) {
27-
throw new XmlParentError('Nodes does not have a parent: $node');
26+
/// Ensure that [owned] has no parent.
27+
static void checkNoParent(XmlOwned owned) {
28+
if (owned.hasParent) {
29+
throw new XmlParentError('Node already has a parent, copy or remove it first: $owned');
2830
}
2931
}
3032

lib/xml/utils/node_list.dart

Lines changed: 114 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,185 +1,190 @@
11
library xml.utils.node_list;
22

3-
import 'dart:collection';
3+
import 'package:collection/collection.dart' show DelegatingList;
44

55
import 'package:xml/xml/nodes/node.dart' show XmlNode;
66
import 'package:xml/xml/utils/owned.dart' show XmlOwned;
77
import 'package:xml/xml/utils/node_type.dart' show XmlNodeType;
8-
import 'package:xml/xml/utils/errors.dart' show XmlNodeTypeError;
8+
import 'package:xml/xml/utils/errors.dart' show XmlNodeTypeError, XmlParentError;
99

1010
/// Mutable list of XmlNodes, manages the parenting of the nodes.
11-
class XmlNodeList<E extends XmlNode> extends ListBase<E> with XmlOwned {
12-
/// The list of nodes.
13-
final List<XmlNode> _nodes = [];
14-
11+
class XmlNodeList<E extends XmlNode> extends DelegatingList<E> with XmlOwned {
1512
/// The shared list of supported node types.
1613
final Set<XmlNodeType> _validNodeTypes;
1714

18-
XmlNodeList(this._validNodeTypes);
19-
20-
@override
21-
int get length => _nodes.length;
15+
XmlNodeList(this._validNodeTypes) : super(<E>[]);
2216

2317
@override
24-
set length(int length) => _nodes.length = length;
18+
void operator []=(int index, E node) {
19+
RangeError.checkValidIndex(index, this);
20+
XmlNodeTypeError.checkValidType(node, _validNodeTypes);
21+
XmlParentError.checkNoParent(node);
22+
this[index].detachParent(parent);
23+
super[index] = node;
24+
node.attachParent(parent);
25+
}
2526

2627
@override
27-
E operator [](int index) => _nodes[index];
28+
set length(int length) => throw new UnsupportedError('Unsupported length change of node list.');
2829

2930
@override
30-
void operator []=(int index, E childNode) {
31-
RangeError.checkValidIndex(index, _nodes);
32-
XmlNodeTypeError.checkValidType(childNode, _validNodeTypes);
33-
if (childNode.hasParent) {
34-
if (childNode.parent == parent) {
35-
var oldIndex = _nodes.indexOf(childNode);
36-
if (oldIndex != index) {
37-
_nodes[index] = childNode;
38-
_nodes.removeAt(oldIndex);
39-
}
40-
return;
41-
}
42-
_removeNode(childNode);
31+
void add(E node) {
32+
XmlNodeTypeError.checkNotNull(node);
33+
if (node.nodeType == XmlNodeType.DOCUMENT_FRAGMENT) {
34+
addAll(_expandFragment(node));
35+
} else {
36+
XmlNodeTypeError.checkValidType(node, _validNodeTypes);
37+
XmlParentError.checkNoParent(node);
38+
super.add(node);
39+
node.attachParent(parent);
4340
}
44-
_nodes[index].detachParent(parent);
45-
_nodes[index] = childNode;
46-
childNode.attachParent(parent);
4741
}
4842

4943
@override
50-
void add(E childNode) {
51-
if (childNode.nodeType == XmlNodeType.DOCUMENT_FRAGMENT) {
52-
addAll(childNode.children as Iterable<E>);
53-
return;
44+
void addAll(Iterable<E> nodes) {
45+
var expanded = _expandNodes(nodes);
46+
super.addAll(expanded);
47+
for (var node in expanded) {
48+
node.attachParent(parent);
5449
}
55-
XmlNodeTypeError.checkValidType(childNode, _validNodeTypes);
56-
_removeNode(childNode);
57-
_nodes.add(childNode);
58-
childNode.attachParent(parent);
5950
}
6051

6152
@override
62-
void addAll(Iterable<E> childNodes) {
63-
var copiedNodes = new List<E>.from(childNodes);
64-
for (var childNode in copiedNodes) {
65-
XmlNodeTypeError.checkValidType(childNode, _validNodeTypes);
66-
}
67-
for (var childNode in copiedNodes) {
68-
_removeNode(childNode);
69-
_nodes.add(childNode);
70-
childNode.attachParent(parent);
71-
}
72-
}
73-
74-
@override
75-
bool remove(Object childNode) {
76-
bool removed = _nodes.remove(childNode);
53+
bool remove(Object node) {
54+
bool removed = super.remove(node);
7755
if (removed) {
78-
(childNode as E).detachParent(parent);
56+
(node as E).detachParent(parent);
7957
}
8058
return removed;
8159
}
8260

8361
@override
84-
void removeWhere(bool test(E node)) => throw new UnimplementedError();
62+
void removeWhere(bool test(E element)) {
63+
super.removeWhere((node) {
64+
var remove = test(node);
65+
if (remove) {
66+
node.detachParent(parent);
67+
}
68+
return remove;
69+
});
70+
}
8571

8672
@override
87-
void retainWhere(bool test(E node)) => throw new UnimplementedError();
73+
void retainWhere(bool test(E node)) {
74+
super.retainWhere((node) {
75+
var retain = test(node);
76+
if (!retain) {
77+
node.detachParent(parent);
78+
}
79+
return retain;
80+
});
81+
}
8882

8983
@override
9084
void clear() {
91-
for (var node in _nodes) {
85+
for (var node in this) {
9286
node.detachParent(parent);
9387
}
94-
_nodes.clear();
88+
super.clear();
9589
}
9690

9791
@override
9892
E removeLast() {
99-
_nodes.last.detachParent(parent);
100-
return _nodes.removeLast();
93+
var node = super.removeLast();
94+
node.detachParent(parent);
95+
return node;
10196
}
10297

10398
@override
10499
void removeRange(int start, int end) {
105-
RangeError.checkValidRange(start, end, this.length);
100+
RangeError.checkValidRange(start, end, length);
106101
for (var i = start; i < end; i++) {
107-
_nodes[i].detachParent(parent);
102+
this[i].detachParent(parent);
108103
}
109-
_nodes.removeRange(start, end);
104+
super.removeRange(start, end);
110105
}
111106

112107
@override
113-
void fillRange(int start, int end, [E fill]) => throw new UnimplementedError();
108+
void fillRange(int start, int end, [E fill]) =>
109+
throw new UnsupportedError('Unsupported range filling of node list.');
114110

115111
@override
116-
void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) =>
117-
throw new UnimplementedError();
112+
void setRange(int start, int end, Iterable<E> nodes, [int skipCount = 0]) {
113+
RangeError.checkValidRange(start, end, length);
114+
var expanded = _expandNodes(nodes);
115+
for (var i = start; i < end; i++) {
116+
this[i].detachParent(parent);
117+
}
118+
super.setRange(start, end, expanded, skipCount);
119+
for (var i = start; i < end; i++) {
120+
this[i].attachParent(parent);
121+
}
122+
}
118123

119124
@override
120-
void replaceRange(int start, int end, Iterable<E> newContents) => throw new UnimplementedError();
125+
void replaceRange(int start, int end, Iterable<E> nodes) {
126+
RangeError.checkValidRange(start, end, length);
127+
var expanded = _expandNodes(nodes);
128+
for (var i = start; i < end; i++) {
129+
this[i].detachParent(parent);
130+
}
131+
super.replaceRange(start, end, expanded);
132+
for (var node in expanded) {
133+
node.attachParent(parent);
134+
}
135+
}
121136

122137
@override
123138
void setAll(int index, Iterable<E> iterable) => throw new UnimplementedError();
124139

125140
@override
126-
void insert(int index, E childNode) {
127-
if (childNode.nodeType == XmlNodeType.DOCUMENT_FRAGMENT) {
128-
insertAll(index, childNode.children as Iterable<E>);
129-
return;
130-
}
131-
XmlNodeTypeError.checkValidType(childNode, _validNodeTypes);
132-
if (childNode.hasParent) {
133-
if (childNode.parent == parent) {
134-
var oldIndex = _nodes.indexOf(childNode);
135-
if (oldIndex != index) {
136-
_nodes[index] = childNode;
137-
_nodes.removeAt(oldIndex < index ? oldIndex : oldIndex + 1);
138-
}
139-
return;
140-
}
141-
_removeNode(childNode);
141+
void insert(int index, E node) {
142+
XmlNodeTypeError.checkNotNull(node);
143+
if (node.nodeType == XmlNodeType.DOCUMENT_FRAGMENT) {
144+
insertAll(index, _expandFragment(node));
145+
} else {
146+
XmlNodeTypeError.checkValidType(node, _validNodeTypes);
147+
XmlParentError.checkNoParent(node);
148+
super.insert(index, node);
149+
node.attachParent(parent);
142150
}
143-
_nodes.insert(index, childNode);
144-
childNode.attachParent(parent);
145151
}
146152

147153
@override
148-
void insertAll(int index, Iterable<E> childNodes) {
149-
RangeError.checkValueInInterval(index, 0, length, 'index');
150-
var copiedNodes = new List<E>.from(childNodes);
151-
for (var childNode in copiedNodes) {
152-
XmlNodeTypeError.checkValidType(childNode, _validNodeTypes);
154+
void insertAll(int index, Iterable<E> nodes) {
155+
var expanded = _expandNodes(nodes);
156+
super.insertAll(index, expanded);
157+
for (var node in expanded) {
158+
node.attachParent(parent);
153159
}
154-
for (var childNode in copiedNodes) {
155-
if (childNode.hasParent) {
156-
if (childNode.parent == parent) {
157-
var oldIndex = _nodes.indexOf(childNode);
158-
if (oldIndex < index) {
159-
index--;
160-
}
161-
}
162-
_removeNode(childNode);
163-
}
164-
childNode.attachParent(parent);
165-
}
166-
_nodes.insertAll(index, childNodes);
167160
}
168161

169162
@override
170163
E removeAt(int index) {
171-
RangeError.checkValueInInterval(index, 0, length, 'index');
172-
_nodes[index].detachParent(parent);
173-
return removeAt(index);
164+
RangeError.checkValidIndex(index, this);
165+
this[index].detachParent(parent);
166+
return super.removeAt(index);
167+
}
168+
169+
Iterable<E> _expandFragment(E fragment) {
170+
return fragment.children.map((node) {
171+
XmlNodeTypeError.checkValidType(node, _validNodeTypes);
172+
return node.copy();
173+
});
174174
}
175175

176-
static void _removeNode(XmlNode node) {
177-
if (node.hasParent) {
178-
if (node.nodeType == XmlNodeType.ATTRIBUTE) {
179-
node.parent.attributes.remove(node);
176+
Iterable<E> _expandNodes(Iterable<E> nodes) {
177+
var expanded = <E>[];
178+
for (var node in nodes) {
179+
XmlNodeTypeError.checkNotNull(node);
180+
if (node.nodeType == XmlNodeType.DOCUMENT_FRAGMENT) {
181+
expanded.addAll(_expandFragment(node));
180182
} else {
181-
node.parent.children.remove(node);
183+
XmlNodeTypeError.checkValidType(node, _validNodeTypes);
184+
XmlParentError.checkNoParent(node);
185+
expanded.add(node);
182186
}
183187
}
188+
return expanded;
184189
}
185190
}

lib/xml/utils/owned.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
library xml.utils.child;
1+
library xml.utils.owned;
22

33
import 'package:xml/xml/nodes/node.dart' show XmlNode;
4+
import 'package:xml/xml/utils/errors.dart' show XmlParentError;
45

56
/// Mixin for objects that are a child of a different [XmlNode].
67
abstract class XmlOwned {
@@ -17,7 +18,10 @@ abstract class XmlOwned {
1718
bool get hasParent => parent != null;
1819

1920
/// Internal method to attach a child to this parent, do not call directly.
20-
void attachParent(XmlNode parent) => _parent = parent;
21+
void attachParent(XmlNode parent) {
22+
XmlParentError.checkNoParent(this);
23+
_parent = parent;
24+
}
2125

2226
/// Internal method to attach a child to this parent, do not call directly.
2327
void detachParent(XmlNode parent) => _parent = null;

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ environment:
77
sdk: '>=1.21.0 <2.0.0'
88
dependencies:
99
petitparser: ^1.5.0
10+
collection: ^1.14.0
1011
dev_dependencies:
1112
test: ^0.12.0

0 commit comments

Comments
 (0)