/* * Copyright (C) 2007-2010 JĂșlio Vilmar Gesser. * Copyright (C) 2011, 2013-2016 The JavaParser Team. * * This file is part of JavaParser. * * JavaParser can be used either under the terms of * a) the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * b) the terms of the Apache License * * You should have received a copy of both licenses in LICENCE.LGPL and * LICENCE.APACHE. Please refer to those files for details. * * JavaParser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ package com.github.javaparser.ast; import com.github.javaparser.HasParentNode; import com.github.javaparser.ast.observer.AstObserver; import com.github.javaparser.ast.observer.Observable; import com.github.javaparser.ast.visitor.GenericVisitor; import com.github.javaparser.ast.visitor.Visitable; import com.github.javaparser.ast.visitor.VoidVisitor; import com.github.javaparser.metamodel.InternalProperty; import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.Stream; /** * A list of nodes. * It usually has a parent node. * Unlike normal Nodes, this does not mean that it is a child of that parent. * Instead, this list will make every node it contains a child of its parent. * This way, a NodeList does not create an extra level inside the AST. * * @param the type of nodes contained. */ public class NodeList implements List, Iterable, HasParentNode>, Visitable, Observable { @InternalProperty private List innerList = new ArrayList<>(0); private Node parentNode; private List observers = new ArrayList<>(); public NodeList() { parentNode = null; } public NodeList(Collection n) { this.addAll(n); } @SafeVarargs public NodeList(N... n) { this.addAll(Arrays.asList(n)); } @Override public boolean add(N node) { notifyElementAdded(innerList.size(), node); own(node); return innerList.add(node); } private void own(N node) { if (node == null) { return; } setAsParentNodeOf(node); } public boolean remove(Node node) { int index = innerList.indexOf(node); if (index != -1) { notifyElementRemoved(index, node); node.setParentNode(null); } return innerList.remove(node); } public N removeFirst() { return remove(0); } public N removeLast() { return remove(innerList.size() - 1); } @SafeVarargs public static NodeList nodeList(X... nodes) { final NodeList nodeList = new NodeList<>(); Collections.addAll(nodeList, nodes); return nodeList; } public static NodeList nodeList(Collection nodes) { final NodeList nodeList = new NodeList<>(); nodeList.addAll(nodes); return nodeList; } public static NodeList nodeList(NodeList nodes) { final NodeList nodeList = new NodeList<>(); nodeList.addAll(nodes); return nodeList; } public boolean contains(N node) { return innerList.contains(node); } @Override public int size() { return innerList.size(); } @Override public N get(int i) { return innerList.get(i); } @Override public Iterator iterator() { // TODO take care of "Iterator.remove" return innerList.iterator(); } @Override public N set(int index, N element) { if (index < 0 || index >= innerList.size()) { throw new IllegalArgumentException("Illegal index. The index should be between 0 and " + innerList.size() + " excluded. It is instead " + index); } if (element == innerList.get(index)) { return element; } notifyElementReplaced(index, element); innerList.get(index).setParentNode(null); setAsParentNodeOf(element); return innerList.set(index, element); } @Override public N remove(int index) { notifyElementRemoved(index, innerList.get(index)); N remove = innerList.remove(index); if (remove != null) remove.setParentNode(null); return remove; } @Override public boolean isEmpty() { return innerList.isEmpty(); } @Override public void sort(Comparator comparator) { innerList.sort(comparator); } public void addAll(NodeList otherList) { for (N node : otherList) { add(node); } } @Override public void add(int index, N node) { notifyElementAdded(index, node); own(node); innerList.add(index, node); } /** * Inserts the node before all other nodes. */ public void addFirst(N node) { add(0, node); } /** * Inserts the node after all other nodes. (This is simply an alias for add.) */ public void addLast(N node) { add(node); } /** * Inserts the node after afterThisNode. * * @throws IllegalArgumentException when afterThisNode is not in this list. */ public NodeList addAfter(N node, N afterThisNode) { int i = indexOf(afterThisNode); if (i == -1) { throw new IllegalArgumentException("Can't find node to insert after."); } add(i + 1, node); return this; } /** * Inserts the node before beforeThisNode. * * @throws IllegalArgumentException when beforeThisNode is not in this list. */ public NodeList addBefore(N node, N beforeThisNode) { int i = indexOf(beforeThisNode); if (i == -1) { throw new IllegalArgumentException("Can't find node to insert before."); } add(i, node); return this; } @Override public Optional getParentNode() { return Optional.ofNullable(parentNode); } /** * Sets the parentNode * * @param parentNode the parentNode * @return this, the NodeList */ @Override public NodeList setParentNode(Node parentNode) { this.parentNode = parentNode; setAsParentNodeOf(innerList); return this; } @Override public Node getParentNodeForChildren() { return parentNode; } @Override public R accept(final GenericVisitor v, final A arg) { return v.visit(this, arg); } @Override public void accept(final VoidVisitor v, final A arg) { v.visit(this, arg); } /** * @see java.lang.Iterable#forEach(java.util.function.Consumer) */ @Override public void forEach(Consumer action) { innerList.forEach(action); } /** * @see java.util.List#contains(java.lang.Object) */ @Override public boolean contains(Object o) { return innerList.contains(o); } /** * @see java.util.List#toArray() */ @Override public Object[] toArray() { return innerList.toArray(); } /** * @see java.util.List#toArray(java.lang.Object[]) */ @Override public T[] toArray(T[] a) { return innerList.toArray(a); } /** * @see java.util.List#remove(java.lang.Object) */ @Override public boolean remove(Object o) { if (o instanceof Node) { return remove((Node) o); } else { return false; } } /** * @see java.util.List#containsAll(java.util.Collection) */ @Override public boolean containsAll(Collection c) { return innerList.containsAll(c); } /** * @see java.util.List#addAll(java.util.Collection) */ @Override public boolean addAll(Collection c) { c.forEach(this::add); return !c.isEmpty(); } /** * @see java.util.List#addAll(int, java.util.Collection) */ @Override public boolean addAll(int index, Collection c) { for (N e : c) { add(index++, e); } return !c.isEmpty(); } /** * @see java.util.List#removeAll(java.util.Collection) */ @Override public boolean removeAll(Collection c) { boolean changed = false; for (Object e : c) { changed = remove(e) || changed; } return changed; } /** * @see java.util.List#retainAll(java.util.Collection) */ @Override public boolean retainAll(Collection c) { boolean changed = false; for (Object e : this.stream().filter(it -> !c.contains(it)).toArray()) { if (!c.contains(e)) { changed = remove(e) || changed; } } return changed; } /** * @see java.util.List#replaceAll(java.util.function.UnaryOperator) */ @Override public void replaceAll(UnaryOperator operator) { for (int i = 0; i < this.size(); i++) { set(i, operator.apply(this.get(i))); } } /** * @see java.util.Collection#removeIf(java.util.function.Predicate) */ @Override public boolean removeIf(Predicate filter) { boolean changed = false; for (Object e : this.stream().filter(filter).toArray()) { changed = remove(e) || changed; } return changed; } /** * @see java.util.List#clear() */ @Override public void clear() { while (!isEmpty()) { remove(0); } } /** * @see java.util.List#equals(java.lang.Object) */ @Override public boolean equals(Object o) { return innerList.equals(o); } /** * @see java.util.List#hashCode() */ @Override public int hashCode() { return innerList.hashCode(); } /** * @see java.util.List#indexOf(java.lang.Object) */ @Override public int indexOf(Object o) { return innerList.indexOf(o); } /** * @see java.util.List#lastIndexOf(java.lang.Object) */ @Override public int lastIndexOf(Object o) { return innerList.lastIndexOf(o); } /** * @see java.util.List#listIterator() */ @Override public ListIterator listIterator() { return innerList.listIterator(); } /** * @see java.util.List#listIterator(int) */ @Override public ListIterator listIterator(int index) { return innerList.listIterator(index); } /** * @see java.util.Collection#parallelStream() */ @Override public Stream parallelStream() { return innerList.parallelStream(); } /** * @see java.util.List#subList(int, int) */ @Override public List subList(int fromIndex, int toIndex) { return innerList.subList(fromIndex, toIndex); } /** * @see java.util.List#spliterator() */ @Override public Spliterator spliterator() { return innerList.spliterator(); } private void notifyElementAdded(int index, Node nodeAddedOrRemoved) { this.observers.forEach(o -> o.listChange(this, AstObserver.ListChangeType.ADDITION, index, nodeAddedOrRemoved)); } private void notifyElementRemoved(int index, Node nodeAddedOrRemoved) { this.observers.forEach(o -> o.listChange(this, AstObserver.ListChangeType.REMOVAL, index, nodeAddedOrRemoved)); } private void notifyElementReplaced(int index, Node nodeAddedOrRemoved) { this.observers.forEach(o -> o.listReplacement(this, index, this.get(index), nodeAddedOrRemoved)); } @Override public void unregister(AstObserver observer) { this.observers.remove(observer); } @Override public void register(AstObserver observer) { if(!this.observers.contains(observer)) { this.observers.add(observer); } } @Override public boolean isRegistered(AstObserver observer) { return this.observers.contains(observer); } /** * Replaces the first node that is equal to "old" with "replacement". * * @return true if a replacement has happened. */ public boolean replace(N old, N replacement) { int i = indexOf(old); if (i == -1) { return false; } set(i, replacement); return true; } /** * @return the opposite of isEmpty() */ public boolean isNonEmpty() { return !isEmpty(); } public void ifNonEmpty(Consumer> consumer) { if (isNonEmpty()) consumer.accept(this); } public static Collector, NodeList> toNodeList() { return Collector.of(NodeList::new, NodeList::add, (left, right) -> { left.addAll(right); return left; }); } private void setAsParentNodeOf(List childNodes) { if (childNodes != null) { for (HasParentNode current : childNodes) { current.setParentNode(getParentNodeForChildren()); } } } private void setAsParentNodeOf(Node childNode) { if (childNode != null) { childNode.setParentNode(getParentNodeForChildren()); } } @Override public String toString() { return innerList.stream().map(Node::toString).collect(Collectors.joining(", ", "[", "]")); } }