1 | /* |
---|---|
2 | * Copyright (C) 2007-2010 JĂșlio Vilmar Gesser. |
3 | * Copyright (C) 2011, 2013-2020 The JavaParser Team. |
4 | * |
5 | * This file is part of JavaParser. |
6 | * |
7 | * JavaParser can be used either under the terms of |
8 | * a) the GNU Lesser General Public License as published by |
9 | * the Free Software Foundation, either version 3 of the License, or |
10 | * (at your option) any later version. |
11 | * b) the terms of the Apache License |
12 | * |
13 | * You should have received a copy of both licenses in LICENCE.LGPL and |
14 | * LICENCE.APACHE. Please refer to those files for details. |
15 | * |
16 | * JavaParser is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | * GNU Lesser General Public License for more details. |
20 | */ |
21 | |
22 | package com.github.javaparser.ast; |
23 | |
24 | import com.github.javaparser.HasParentNode; |
25 | import com.github.javaparser.ast.observer.AstObserver; |
26 | import com.github.javaparser.ast.observer.Observable; |
27 | import com.github.javaparser.ast.visitor.GenericVisitor; |
28 | import com.github.javaparser.ast.visitor.Visitable; |
29 | import com.github.javaparser.ast.visitor.VoidVisitor; |
30 | import com.github.javaparser.metamodel.InternalProperty; |
31 | |
32 | import java.util.*; |
33 | import java.util.function.Consumer; |
34 | import java.util.function.Predicate; |
35 | import java.util.function.UnaryOperator; |
36 | import java.util.stream.Collector; |
37 | import java.util.stream.Collectors; |
38 | import java.util.stream.Stream; |
39 | |
40 | /** |
41 | * A list of nodes. |
42 | * It usually has a parent node. |
43 | * Unlike normal Nodes, this does not mean that it is a child of that parent. |
44 | * Instead, this list will make every node it contains a child of its parent. |
45 | * This way, a NodeList does not create an extra level inside the AST. |
46 | * |
47 | * @param <N> the type of nodes contained. |
48 | */ |
49 | public class NodeList<N extends Node> implements List<N>, Iterable<N>, HasParentNode<NodeList<N>>, Visitable, Observable { |
50 | @InternalProperty |
51 | private List<N> innerList = new ArrayList<>(0); |
52 | |
53 | private Node parentNode; |
54 | |
55 | private List<AstObserver> observers = new ArrayList<>(); |
56 | |
57 | public NodeList() { |
58 | parentNode = null; |
59 | } |
60 | |
61 | public NodeList(Collection<N> n) { |
62 | this.addAll(n); |
63 | } |
64 | |
65 | @SafeVarargs |
66 | public NodeList(N... n) { |
67 | this.addAll(Arrays.asList(n)); |
68 | } |
69 | |
70 | @Override |
71 | public boolean add(N node) { |
72 | notifyElementAdded(innerList.size(), node); |
73 | own(node); |
74 | return innerList.add(node); |
75 | } |
76 | |
77 | private void own(N node) { |
78 | if (node == null) { |
79 | return; |
80 | } |
81 | setAsParentNodeOf(node); |
82 | } |
83 | |
84 | public boolean remove(Node node) { |
85 | int index = innerList.indexOf(node); |
86 | if (index != -1) { |
87 | notifyElementRemoved(index, node); |
88 | node.setParentNode(null); |
89 | } |
90 | return innerList.remove(node); |
91 | } |
92 | |
93 | public N removeFirst() { |
94 | return remove(0); |
95 | } |
96 | |
97 | public N removeLast() { |
98 | return remove(innerList.size() - 1); |
99 | } |
100 | |
101 | @SafeVarargs |
102 | public static <X extends Node> NodeList<X> nodeList(X... nodes) { |
103 | final NodeList<X> nodeList = new NodeList<>(); |
104 | Collections.addAll(nodeList, nodes); |
105 | return nodeList; |
106 | } |
107 | |
108 | public static <X extends Node> NodeList<X> nodeList(Collection<X> nodes) { |
109 | final NodeList<X> nodeList = new NodeList<>(); |
110 | nodeList.addAll(nodes); |
111 | return nodeList; |
112 | } |
113 | |
114 | public static <X extends Node> NodeList<X> nodeList(NodeList<X> nodes) { |
115 | final NodeList<X> nodeList = new NodeList<>(); |
116 | nodeList.addAll(nodes); |
117 | return nodeList; |
118 | } |
119 | |
120 | public boolean contains(N node) { |
121 | return innerList.contains(node); |
122 | } |
123 | |
124 | @Override |
125 | public int size() { |
126 | return innerList.size(); |
127 | } |
128 | |
129 | @Override |
130 | public N get(int i) { |
131 | return innerList.get(i); |
132 | } |
133 | |
134 | @Override |
135 | public Iterator<N> iterator() { |
136 | // TODO take care of "Iterator.remove" |
137 | return innerList.iterator(); |
138 | } |
139 | |
140 | @Override |
141 | public N set(int index, N element) { |
142 | if (index < 0 || index >= innerList.size()) { |
143 | throw new IllegalArgumentException("Illegal index. The index should be between 0 and " + innerList.size() |
144 | + " excluded. It is instead " + index); |
145 | } |
146 | if (element == innerList.get(index)) { |
147 | return element; |
148 | } |
149 | notifyElementReplaced(index, element); |
150 | innerList.get(index).setParentNode(null); |
151 | setAsParentNodeOf(element); |
152 | return innerList.set(index, element); |
153 | } |
154 | |
155 | @Override |
156 | public N remove(int index) { |
157 | notifyElementRemoved(index, innerList.get(index)); |
158 | N remove = innerList.remove(index); |
159 | if (remove != null) |
160 | remove.setParentNode(null); |
161 | return remove; |
162 | } |
163 | |
164 | @Override |
165 | public boolean isEmpty() { |
166 | return innerList.isEmpty(); |
167 | } |
168 | |
169 | @Override |
170 | public void sort(Comparator<? super N> comparator) { |
171 | innerList.sort(comparator); |
172 | } |
173 | |
174 | public void addAll(NodeList<N> otherList) { |
175 | for (N node : otherList) { |
176 | add(node); |
177 | } |
178 | } |
179 | |
180 | @Override |
181 | public void add(int index, N node) { |
182 | notifyElementAdded(index, node); |
183 | own(node); |
184 | innerList.add(index, node); |
185 | } |
186 | |
187 | /** |
188 | * Inserts the node before all other nodes. |
189 | */ |
190 | public NodeList<N> addFirst(N node) { |
191 | add(0, node); |
192 | return this; |
193 | } |
194 | |
195 | /** |
196 | * Inserts the node after all other nodes. (This is simply an alias for add.) |
197 | */ |
198 | public NodeList<N> addLast(N node) { |
199 | add(node); |
200 | return this; |
201 | } |
202 | |
203 | /** |
204 | * Inserts the node after afterThisNode. |
205 | * |
206 | * @throws IllegalArgumentException when afterThisNode is not in this list. |
207 | */ |
208 | public NodeList<N> addAfter(N node, N afterThisNode) { |
209 | int i = indexOf(afterThisNode); |
210 | if (i == -1) { |
211 | throw new IllegalArgumentException("Can't find node to insert after."); |
212 | } |
213 | add(i + 1, node); |
214 | return this; |
215 | } |
216 | |
217 | /** |
218 | * Inserts the node before beforeThisNode. |
219 | * |
220 | * @throws IllegalArgumentException when beforeThisNode is not in this list. |
221 | */ |
222 | public NodeList<N> addBefore(N node, N beforeThisNode) { |
223 | int i = indexOf(beforeThisNode); |
224 | if (i == -1) { |
225 | throw new IllegalArgumentException("Can't find node to insert before."); |
226 | } |
227 | add(i, node); |
228 | return this; |
229 | } |
230 | |
231 | |
232 | /** |
233 | * @return the first node, or empty if the list is empty. |
234 | */ |
235 | public Optional<N> getFirst() { |
236 | if (isEmpty()) { |
237 | return Optional.empty(); |
238 | } |
239 | return Optional.of(get(0)); |
240 | } |
241 | |
242 | /** |
243 | * @return the last node, or empty if the list is empty. |
244 | */ |
245 | public Optional<N> getLast() { |
246 | if (isEmpty()) { |
247 | return Optional.empty(); |
248 | } |
249 | return Optional.of(get(size() - 1)); |
250 | } |
251 | |
252 | @Override |
253 | public Optional<Node> getParentNode() { |
254 | return Optional.ofNullable(parentNode); |
255 | } |
256 | |
257 | /** |
258 | * Sets the parentNode |
259 | * |
260 | * @param parentNode the parentNode |
261 | * @return this, the NodeList |
262 | */ |
263 | @Override |
264 | public NodeList<N> setParentNode(Node parentNode) { |
265 | this.parentNode = parentNode; |
266 | setAsParentNodeOf(innerList); |
267 | return this; |
268 | } |
269 | |
270 | @Override |
271 | public Node getParentNodeForChildren() { |
272 | return parentNode; |
273 | } |
274 | |
275 | @Override |
276 | public <R, A> R accept(final GenericVisitor<R, A> v, final A arg) { |
277 | return v.visit(this, arg); |
278 | } |
279 | |
280 | @Override |
281 | public <A> void accept(final VoidVisitor<A> v, final A arg) { |
282 | v.visit(this, arg); |
283 | } |
284 | |
285 | /** |
286 | * @see java.lang.Iterable#forEach(java.util.function.Consumer) |
287 | */ |
288 | @Override |
289 | public void forEach(Consumer<? super N> action) { |
290 | innerList.forEach(action); |
291 | } |
292 | |
293 | /** |
294 | * @see java.util.List#contains(java.lang.Object) |
295 | */ |
296 | @Override |
297 | public boolean contains(Object o) { |
298 | return innerList.contains(o); |
299 | } |
300 | |
301 | /** |
302 | * @see java.util.List#toArray() |
303 | */ |
304 | @Override |
305 | public Object[] toArray() { |
306 | return innerList.toArray(); |
307 | } |
308 | |
309 | /** |
310 | * @see java.util.List#toArray(java.lang.Object[]) |
311 | */ |
312 | @Override |
313 | public <T> T[] toArray(T[] a) { |
314 | return innerList.toArray(a); |
315 | } |
316 | |
317 | /** |
318 | * @see java.util.List#remove(java.lang.Object) |
319 | */ |
320 | @Override |
321 | public boolean remove(Object o) { |
322 | if (o instanceof Node) { |
323 | return remove((Node) o); |
324 | } else { |
325 | return false; |
326 | } |
327 | } |
328 | |
329 | /** |
330 | * @see java.util.List#containsAll(java.util.Collection) |
331 | */ |
332 | @Override |
333 | public boolean containsAll(Collection<?> c) { |
334 | return innerList.containsAll(c); |
335 | } |
336 | |
337 | /** |
338 | * @see java.util.List#addAll(java.util.Collection) |
339 | */ |
340 | @Override |
341 | public boolean addAll(Collection<? extends N> c) { |
342 | c.forEach(this::add); |
343 | return !c.isEmpty(); |
344 | } |
345 | |
346 | /** |
347 | * @see java.util.List#addAll(int, java.util.Collection) |
348 | */ |
349 | @Override |
350 | public boolean addAll(int index, Collection<? extends N> c) { |
351 | for (N e : c) { |
352 | add(index++, e); |
353 | } |
354 | return !c.isEmpty(); |
355 | } |
356 | |
357 | /** |
358 | * @see java.util.List#removeAll(java.util.Collection) |
359 | */ |
360 | @Override |
361 | public boolean removeAll(Collection<?> c) { |
362 | boolean changed = false; |
363 | for (Object e : c) { |
364 | changed = remove(e) || changed; |
365 | } |
366 | return changed; |
367 | } |
368 | |
369 | /** |
370 | * @see java.util.List#retainAll(java.util.Collection) |
371 | */ |
372 | @Override |
373 | public boolean retainAll(Collection<?> c) { |
374 | boolean changed = false; |
375 | for (Object e : this.stream().filter(it -> !c.contains(it)).toArray()) { |
376 | if (!c.contains(e)) { |
377 | changed = remove(e) || changed; |
378 | } |
379 | } |
380 | return changed; |
381 | } |
382 | |
383 | /** |
384 | * @see java.util.List#replaceAll(java.util.function.UnaryOperator) |
385 | */ |
386 | @Override |
387 | public void replaceAll(UnaryOperator<N> operator) { |
388 | for (int i = 0; i < this.size(); i++) { |
389 | set(i, operator.apply(this.get(i))); |
390 | } |
391 | } |
392 | |
393 | /** |
394 | * @see java.util.Collection#removeIf(java.util.function.Predicate) |
395 | */ |
396 | @Override |
397 | public boolean removeIf(Predicate<? super N> filter) { |
398 | boolean changed = false; |
399 | for (Object e : this.stream().filter(filter).toArray()) { |
400 | changed = remove(e) || changed; |
401 | } |
402 | return changed; |
403 | } |
404 | |
405 | /** |
406 | * @see java.util.List#clear() |
407 | */ |
408 | @Override |
409 | public void clear() { |
410 | while (!isEmpty()) { |
411 | remove(0); |
412 | } |
413 | } |
414 | |
415 | /** |
416 | * @see java.util.List#equals(java.lang.Object) |
417 | */ |
418 | @Override |
419 | public boolean equals(Object o) { |
420 | return innerList.equals(o); |
421 | } |
422 | |
423 | /** |
424 | * @see java.util.List#hashCode() |
425 | */ |
426 | @Override |
427 | public int hashCode() { |
428 | return innerList.hashCode(); |
429 | } |
430 | |
431 | /** |
432 | * @see java.util.List#indexOf(java.lang.Object) |
433 | */ |
434 | @Override |
435 | public int indexOf(Object o) { |
436 | return innerList.indexOf(o); |
437 | } |
438 | |
439 | /** |
440 | * @see java.util.List#lastIndexOf(java.lang.Object) |
441 | */ |
442 | @Override |
443 | public int lastIndexOf(Object o) { |
444 | return innerList.lastIndexOf(o); |
445 | } |
446 | |
447 | /** |
448 | * @see java.util.List#listIterator() |
449 | */ |
450 | @Override |
451 | public ListIterator<N> listIterator() { |
452 | return innerList.listIterator(); |
453 | } |
454 | |
455 | /** |
456 | * @see java.util.List#listIterator(int) |
457 | */ |
458 | @Override |
459 | public ListIterator<N> listIterator(int index) { |
460 | return innerList.listIterator(index); |
461 | } |
462 | |
463 | /** |
464 | * @see java.util.Collection#parallelStream() |
465 | */ |
466 | @Override |
467 | public Stream<N> parallelStream() { |
468 | return innerList.parallelStream(); |
469 | } |
470 | |
471 | /** |
472 | * @see java.util.List#subList(int, int) |
473 | */ |
474 | @Override |
475 | public List<N> subList(int fromIndex, int toIndex) { |
476 | return innerList.subList(fromIndex, toIndex); |
477 | } |
478 | |
479 | /** |
480 | * @see java.util.List#spliterator() |
481 | */ |
482 | @Override |
483 | public Spliterator<N> spliterator() { |
484 | return innerList.spliterator(); |
485 | } |
486 | |
487 | private void notifyElementAdded(int index, Node nodeAddedOrRemoved) { |
488 | this.observers.forEach(o -> o.listChange(this, AstObserver.ListChangeType.ADDITION, index, nodeAddedOrRemoved)); |
489 | } |
490 | |
491 | private void notifyElementRemoved(int index, Node nodeAddedOrRemoved) { |
492 | this.observers.forEach(o -> o.listChange(this, AstObserver.ListChangeType.REMOVAL, index, nodeAddedOrRemoved)); |
493 | } |
494 | |
495 | private void notifyElementReplaced(int index, Node nodeAddedOrRemoved) { |
496 | this.observers.forEach(o -> o.listReplacement(this, index, this.get(index), nodeAddedOrRemoved)); |
497 | } |
498 | |
499 | @Override |
500 | public void unregister(AstObserver observer) { |
501 | this.observers.remove(observer); |
502 | } |
503 | |
504 | @Override |
505 | public void register(AstObserver observer) { |
506 | if (!this.observers.contains(observer)) { |
507 | this.observers.add(observer); |
508 | } |
509 | } |
510 | |
511 | @Override |
512 | public boolean isRegistered(AstObserver observer) { |
513 | return this.observers.contains(observer); |
514 | } |
515 | |
516 | /** |
517 | * Replaces the first node that is equal to "old" with "replacement". |
518 | * |
519 | * @return true if a replacement has happened. |
520 | */ |
521 | public boolean replace(N old, N replacement) { |
522 | int i = indexOf(old); |
523 | if (i == -1) { |
524 | return false; |
525 | } |
526 | set(i, replacement); |
527 | return true; |
528 | } |
529 | |
530 | /** |
531 | * @return the opposite of isEmpty() |
532 | */ |
533 | public boolean isNonEmpty() { |
534 | return !isEmpty(); |
535 | } |
536 | |
537 | public void ifNonEmpty(Consumer<? super NodeList<N>> consumer) { |
538 | if (isNonEmpty()) |
539 | consumer.accept(this); |
540 | } |
541 | |
542 | public static <T extends Node> Collector<T, NodeList<T>, NodeList<T>> toNodeList() { |
543 | return Collector.of(NodeList::new, NodeList::add, (left, right) -> { |
544 | left.addAll(right); |
545 | return left; |
546 | }); |
547 | } |
548 | |
549 | private void setAsParentNodeOf(List<? extends Node> childNodes) { |
550 | if (childNodes != null) { |
551 | for (HasParentNode current : childNodes) { |
552 | current.setParentNode(getParentNodeForChildren()); |
553 | } |
554 | } |
555 | } |
556 | |
557 | private void setAsParentNodeOf(Node childNode) { |
558 | if (childNode != null) { |
559 | childNode.setParentNode(getParentNodeForChildren()); |
560 | } |
561 | } |
562 | |
563 | @Override |
564 | public String toString() { |
565 | return innerList.stream().map(Node::toString).collect(Collectors.joining(", ", "[", "]")); |
566 | } |
567 | } |
568 |
Members