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 22package com.github.javaparser.ast; 23 24import com.github.javaparser.Position; 25import com.github.javaparser.Range; 26import com.github.javaparser.ast.comments.BlockComment; 27import com.github.javaparser.ast.comments.Comment; 28import com.github.javaparser.ast.comments.LineComment; 29import com.github.javaparser.ast.visitor.*; 30 31import java.util.*; 32 33/** 34 * Abstract class for all nodes of the AST. 35 * 36 * Each Node can have one associated comment which describe it and 37 * a number of "orphan comments" which it contains but are not specifically 38 * associated to any element. 39 * 40 * @author Julio Vilmar Gesser 41 */ 42public abstract class Node implements Cloneable { 43 /** 44 * This can be used to sort nodes on position. 45 */ 46 public static Comparator<Node> NODE_BY_BEGIN_POSITION = (a, b) -> a.getBegin().compareTo(b.getBegin()); 47 48 private Range range; 49 50 private Node parentNode; 51 52 private List<Node> childrenNodes = new LinkedList<>(); 53 private List<Comment> orphanComments = new LinkedList<>(); 54 55 private IdentityHashMap<UserDataKey<?>, Object> userData = null; 56 57 private Comment comment; 58 59 public Node() { 60 this(Range.UNKNOWN); 61 } 62 63 public Node(Range range) { 64 this.range = range; 65 } 66 67 /** 68 * Accept method for visitor support. 69 * 70 * @param <R> 71 * the type the return value of the visitor 72 * @param <A> 73 * the type the argument passed to the visitor 74 * @param v 75 * the visitor implementation 76 * @param arg 77 * the argument passed to the visitor 78 * @return the result of the visit 79 */ 80 public abstract <R, A> R accept(GenericVisitor<R, A> v, A arg); 81 82 /** 83 * Accept method for visitor support. 84 * 85 * @param <A> 86 * the type the argument passed for the visitor 87 * @param v 88 * the visitor implementation 89 * @param arg 90 * any value relevant for the visitor 91 */ 92 public abstract <A> void accept(VoidVisitor<A> v, A arg); 93 94 /** 95 * This is a comment associated with this node. 96 * 97 * @return comment property 98 */ 99 public final Comment getComment() { 100 return comment; 101 } 102 103 /** 104 * The begin position of this node in the source file. 105 */ 106 public Position getBegin() { 107 return range.begin; 108 } 109 110 /** 111 * The end position of this node in the source file. 112 */ 113 public Position getEnd() { 114 return range.end; 115 } 116 117 /** 118 * Sets the begin position of this node in the source file. 119 */ 120 public Node setBegin(Position begin) { 121 range = range.withBegin(begin); 122 return this; 123 } 124 125 /** 126 * Sets the end position of this node in the source file. 127 */ 128 public Node setEnd(Position end) { 129 range = range.withEnd(end); 130 return this; 131 } 132 133 /** 134 * @return the range of characters in the source code that this node covers. 135 */ 136 public Range getRange() { 137 return range; 138 } 139 140 /** 141 * @param range the range of characters in the source code that this node covers. 142 */ 143 public Node setRange(Range range) { 144 this.range = range; 145 return this; 146 } 147 148 /** 149 * Use this to store additional information to this node. 150 * 151 * @param comment to be set 152 */ 153 public final Node setComment(final Comment comment) { 154 if (comment != null && (this instanceof Comment)) { 155 throw new RuntimeException("A comment can not be commented"); 156 } 157 if (this.comment != null) { 158 this.comment.setCommentedNode(null); 159 } 160 this.comment = comment; 161 if (comment != null) { 162 this.comment.setCommentedNode(this); 163 } 164 return this; 165 } 166 167 168 169 /** 170 * Use this to store additional information to this node. 171 * 172 * @param comment to be set 173 */ 174 public final Node setLineComment(String comment) { 175 return setComment(new LineComment(comment)); 176 } 177 178 /** 179 * Use this to store additional information to this node. 180 * 181 * @param comment to be set 182 */ 183 public final Node setBlockComment(String comment) { 184 return setComment(new BlockComment(comment)); 185 } 186 187 /** 188 * Return the String representation of this node. 189 * 190 * @return the String representation of this node 191 */ 192 @Override 193 public final String toString() { 194 final DumpVisitor visitor = new DumpVisitor(); 195 accept(visitor, null); 196 return visitor.getSource(); 197 } 198 199 public final String toStringWithoutComments() { 200 final DumpVisitor visitor = new DumpVisitor(false); 201 accept(visitor, null); 202 return visitor.getSource(); 203 } 204 205 @Override 206 public final int hashCode() { 207 return toString().hashCode(); 208 } 209 210 @Override 211 public boolean equals(final Object obj) { 212 if (obj == null || !(obj instanceof Node)) { 213 return false; 214 } 215 return EqualsVisitor.equals(this, (Node) obj); 216 } 217 218 @Override 219 public Node clone() { 220 return this.accept(new CloneVisitor(), null); 221 } 222 223 public Node getParentNode() { 224 return parentNode; 225 } 226 227 @SuppressWarnings("unchecked") 228 public <T> T getParentNodeOfType(Class<T> classType) { 229 Node parent = parentNode; 230 while (parent != null) { 231 if (classType.isAssignableFrom(parent.getClass())) 232 return (T) parent; 233 parent = parent.parentNode; 234 } 235 return null; 236 } 237 238 public List<Node> getChildrenNodes() { 239 return childrenNodes; 240 } 241 242 public boolean contains(Node other) { 243 return range.contains(other.range); 244 } 245 246 public void addOrphanComment(Comment comment) { 247 orphanComments.add(comment); 248 comment.setParentNode(this); 249 } 250 251 /** 252 * This is a list of Comment which are inside the node and are not associated 253 * with any meaningful AST Node. 254 * 255 * For example, comments at the end of methods (immediately before the parenthesis) 256 * or at the end of CompilationUnit are orphan comments. 257 * 258 * When more than one comment preceeds a statement, the one immediately preceding it 259 * it is associated with the statements, while the others are orphans. 260 * 261 * @return all comments that cannot be attributed to a concept 262 */ 263 public List<Comment> getOrphanComments() { 264 return orphanComments; 265 } 266 267 /** 268 * This is the list of Comment which are contained in the Node either because 269 * they are properly associated to one of its children or because they are floating 270 * around inside the Node 271 * 272 * @return all Comments within the node as a list 273 */ 274 public List<Comment> getAllContainedComments() { 275 List<Comment> comments = new LinkedList<>(); 276 comments.addAll(getOrphanComments()); 277 278 for (Node child : getChildrenNodes()) { 279 if (child.getComment() != null) { 280 comments.add(child.getComment()); 281 } 282 comments.addAll(child.getAllContainedComments()); 283 } 284 285 return comments; 286 } 287 288 /** 289 * Assign a new parent to this node, removing it 290 * from the list of children of the previous parent, if any. 291 * 292 * @param parentNode node to be set as parent 293 */ 294 public void setParentNode(Node parentNode) { 295 // remove from old parent, if any 296 if (this.parentNode != null) { 297 this.parentNode.childrenNodes.remove(this); 298 } 299 this.parentNode = parentNode; 300 // add to new parent, if any 301 if (this.parentNode != null) { 302 this.parentNode.childrenNodes.add(this); 303 } 304 } 305 306 protected void setAsParentNodeOf(List<? extends Node> childNodes) { 307 if (childNodes != null) { 308 for (Node current : childNodes) { 309 current.setParentNode(this); 310 } 311 } 312 } 313 314 protected void setAsParentNodeOf(Node childNode) { 315 if (childNode != null) { 316 childNode.setParentNode(this); 317 } 318 } 319 320 public static final int ABSOLUTE_BEGIN_LINE = -1; 321 public static final int ABSOLUTE_END_LINE = -2; 322 323 public boolean isPositionedAfter(Position position) { 324 return range.isAfter(position); 325 } 326 327 public boolean isPositionedBefore(Position position) { 328 return range.isBefore(position); 329 } 330 331 public boolean hasComment() { 332 return comment != null; 333 } 334 335 public void tryAddImportToParentCompilationUnit(Class<?> clazz) { 336 CompilationUnit parentNode = getParentNodeOfType(CompilationUnit.class); 337 if (parentNode != null) { 338 parentNode.addImport(clazz); 339 } 340 } 341 342 /** 343 * Recursively finds all nodes of a certain type. 344 * 345 * @param clazz the type of node to find. 346 */ 347 public <N extends Node> List<N> getNodesByType(Class<N> clazz) { 348 List<N> nodes = new ArrayList<>(); 349 for (Node child : getChildrenNodes()) { 350 if (clazz.isInstance(child)) { 351 nodes.add(clazz.cast(child)); 352 } 353 nodes.addAll(child.getNodesByType(clazz)); 354 } 355 return nodes; 356 } 357 358 /** 359 * Gets user data for this component using the given key. 360 * 361 * @param <M> 362 * The type of the user data. 363 * 364 * @param key 365 * The key for the data 366 * @return The user data or null of no user data was found for the given key 367 * @see UserDataKey 368 */ 369 public <M> M getUserData(final UserDataKey<M> key) { 370 if (userData == null) { 371 return null; 372 } 373 return (M) userData.get(key); 374 } 375 376 /** 377 * Sets user data for this component using the given key. 378 * For information on creating UserDataKey, see {@link UserDataKey}. 379 * 380 * @param <M> 381 * The type of user data 382 * 383 * @param key 384 * The singleton key for the user data 385 * @param object 386 * The user data object 387 * @throws IllegalArgumentException 388 * @see UserDataKey 389 */ 390 public <M> void setUserData(UserDataKey<M> key, M object) { 391 if (userData == null) { 392 userData = new IdentityHashMap<>(); 393 } 394 userData.put(key, object); 395 } 396} 397