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