|
1 | 1 | library xml.utils.node_list;
|
2 | 2 |
|
3 |
| -import 'dart:collection'; |
| 3 | +import 'package:collection/collection.dart' show DelegatingList; |
4 | 4 |
|
5 | 5 | import 'package:xml/xml/nodes/node.dart' show XmlNode;
|
6 | 6 | import 'package:xml/xml/utils/owned.dart' show XmlOwned;
|
7 | 7 | 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; |
9 | 9 |
|
10 | 10 | /// 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 { |
15 | 12 | /// The shared list of supported node types.
|
16 | 13 | final Set<XmlNodeType> _validNodeTypes;
|
17 | 14 |
|
18 |
| - XmlNodeList(this._validNodeTypes); |
19 |
| - |
20 |
| - @override |
21 |
| - int get length => _nodes.length; |
| 15 | + XmlNodeList(this._validNodeTypes) : super(<E>[]); |
22 | 16 |
|
23 | 17 | @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 | + } |
25 | 26 |
|
26 | 27 | @override
|
27 |
| - E operator [](int index) => _nodes[index]; |
| 28 | + set length(int length) => throw new UnsupportedError('Unsupported length change of node list.'); |
28 | 29 |
|
29 | 30 | @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); |
43 | 40 | }
|
44 |
| - _nodes[index].detachParent(parent); |
45 |
| - _nodes[index] = childNode; |
46 |
| - childNode.attachParent(parent); |
47 | 41 | }
|
48 | 42 |
|
49 | 43 | @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); |
54 | 49 | }
|
55 |
| - XmlNodeTypeError.checkValidType(childNode, _validNodeTypes); |
56 |
| - _removeNode(childNode); |
57 |
| - _nodes.add(childNode); |
58 |
| - childNode.attachParent(parent); |
59 | 50 | }
|
60 | 51 |
|
61 | 52 | @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); |
77 | 55 | if (removed) {
|
78 |
| - (childNode as E).detachParent(parent); |
| 56 | + (node as E).detachParent(parent); |
79 | 57 | }
|
80 | 58 | return removed;
|
81 | 59 | }
|
82 | 60 |
|
83 | 61 | @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 | + } |
85 | 71 |
|
86 | 72 | @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 | + } |
88 | 82 |
|
89 | 83 | @override
|
90 | 84 | void clear() {
|
91 |
| - for (var node in _nodes) { |
| 85 | + for (var node in this) { |
92 | 86 | node.detachParent(parent);
|
93 | 87 | }
|
94 |
| - _nodes.clear(); |
| 88 | + super.clear(); |
95 | 89 | }
|
96 | 90 |
|
97 | 91 | @override
|
98 | 92 | E removeLast() {
|
99 |
| - _nodes.last.detachParent(parent); |
100 |
| - return _nodes.removeLast(); |
| 93 | + var node = super.removeLast(); |
| 94 | + node.detachParent(parent); |
| 95 | + return node; |
101 | 96 | }
|
102 | 97 |
|
103 | 98 | @override
|
104 | 99 | void removeRange(int start, int end) {
|
105 |
| - RangeError.checkValidRange(start, end, this.length); |
| 100 | + RangeError.checkValidRange(start, end, length); |
106 | 101 | for (var i = start; i < end; i++) {
|
107 |
| - _nodes[i].detachParent(parent); |
| 102 | + this[i].detachParent(parent); |
108 | 103 | }
|
109 |
| - _nodes.removeRange(start, end); |
| 104 | + super.removeRange(start, end); |
110 | 105 | }
|
111 | 106 |
|
112 | 107 | @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.'); |
114 | 110 |
|
115 | 111 | @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 | + } |
118 | 123 |
|
119 | 124 | @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 | + } |
121 | 136 |
|
122 | 137 | @override
|
123 | 138 | void setAll(int index, Iterable<E> iterable) => throw new UnimplementedError();
|
124 | 139 |
|
125 | 140 | @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); |
142 | 150 | }
|
143 |
| - _nodes.insert(index, childNode); |
144 |
| - childNode.attachParent(parent); |
145 | 151 | }
|
146 | 152 |
|
147 | 153 | @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); |
153 | 159 | }
|
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); |
167 | 160 | }
|
168 | 161 |
|
169 | 162 | @override
|
170 | 163 | 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 | + }); |
174 | 174 | }
|
175 | 175 |
|
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)); |
180 | 182 | } else {
|
181 |
| - node.parent.children.remove(node); |
| 183 | + XmlNodeTypeError.checkValidType(node, _validNodeTypes); |
| 184 | + XmlParentError.checkNoParent(node); |
| 185 | + expanded.add(node); |
182 | 186 | }
|
183 | 187 | }
|
| 188 | + return expanded; |
184 | 189 | }
|
185 | 190 | }
|
0 commit comments