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