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