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 package com.github.javaparser.ast; 22 23 import com.github.javaparser.*; 24 import com.github.javaparser.ast.body.AnnotationDeclaration; 25 import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; 26 import com.github.javaparser.ast.body.EnumDeclaration; 27 import com.github.javaparser.ast.body.TypeDeclaration; 28 import com.github.javaparser.ast.comments.Comment; 29 import com.github.javaparser.ast.comments.JavadocComment; 30 import com.github.javaparser.ast.expr.Name; 31 import com.github.javaparser.ast.modules.ModuleDeclaration; 32 import com.github.javaparser.ast.nodeTypes.NodeWithName; 33 import com.github.javaparser.ast.observer.ObservableProperty; 34 import com.github.javaparser.ast.visitor.CloneVisitor; 35 import com.github.javaparser.ast.visitor.GenericVisitor; 36 import com.github.javaparser.ast.visitor.VoidVisitor; 37 import com.github.javaparser.metamodel.CompilationUnitMetaModel; 38 import com.github.javaparser.metamodel.InternalProperty; 39 import com.github.javaparser.metamodel.JavaParserMetaModel; 40 import com.github.javaparser.metamodel.OptionalProperty; 41 import com.github.javaparser.printer.PrettyPrinter; 42 import com.github.javaparser.utils.ClassUtils; 43 import com.github.javaparser.utils.CodeGenerationUtils; 44 import com.github.javaparser.utils.Utils; 45 import java.io.IOException; 46 import java.nio.charset.Charset; 47 import java.nio.file.Files; 48 import java.nio.file.Path; 49 import java.nio.file.Paths; 50 import java.util.List; 51 import java.util.Optional; 52 import java.util.function.Function; 53 import static com.github.javaparser.JavaToken.Kind.EOF; 54 import static com.github.javaparser.Providers.UTF8; 55 import static com.github.javaparser.Providers.provider; 56 import static com.github.javaparser.Range.range; 57 import static com.github.javaparser.StaticJavaParser.parseImport; 58 import static com.github.javaparser.StaticJavaParser.parseName; 59 import static com.github.javaparser.ast.Modifier.createModifierList; 60 import static com.github.javaparser.utils.CodeGenerationUtils.subtractPaths; 61 import static com.github.javaparser.utils.Utils.assertNotNull; 62 import com.github.javaparser.ast.Node; 63 import com.github.javaparser.TokenRange; 64 import com.github.javaparser.ast.Generated; 65 66 /** 67 * <p> 68 * This class represents the entire compilation unit. Each java file denotes a 69 * compilation unit. 70 * </p> 71 * A compilation unit start with an optional package declaration, 72 * followed by zero or more import declarations, 73 * followed by zero or more type declarations. 74 * 75 * @author Julio Vilmar Gesser 76 * @see PackageDeclaration 77 * @see ImportDeclaration 78 * @see TypeDeclaration 79 * @see Storage 80 */ 81 public class CompilationUnit extends Node { 82 83 @OptionalProperty 84 private PackageDeclaration packageDeclaration; 85 86 private NodeList<ImportDeclaration> imports; 87 88 private NodeList<TypeDeclaration<?>> types; 89 90 @OptionalProperty 91 private ModuleDeclaration module; 92 93 @InternalProperty 94 private Storage storage; 95 CompilationUnit()96 public CompilationUnit() { 97 this(null, null, new NodeList<>(), new NodeList<>(), null); 98 } 99 CompilationUnit(String packageDeclaration)100 public CompilationUnit(String packageDeclaration) { 101 this(null, new PackageDeclaration(new Name(packageDeclaration)), new NodeList<>(), new NodeList<>(), null); 102 } 103 104 @AllFieldsConstructor CompilationUnit(PackageDeclaration packageDeclaration, NodeList<ImportDeclaration> imports, NodeList<TypeDeclaration<?>> types, ModuleDeclaration module)105 public CompilationUnit(PackageDeclaration packageDeclaration, NodeList<ImportDeclaration> imports, NodeList<TypeDeclaration<?>> types, ModuleDeclaration module) { 106 this(null, packageDeclaration, imports, types, module); 107 } 108 109 /** 110 * This constructor is used by the parser and is considered private. 111 */ 112 @Generated("com.github.javaparser.generator.core.node.MainConstructorGenerator") CompilationUnit(TokenRange tokenRange, PackageDeclaration packageDeclaration, NodeList<ImportDeclaration> imports, NodeList<TypeDeclaration<?>> types, ModuleDeclaration module)113 public CompilationUnit(TokenRange tokenRange, PackageDeclaration packageDeclaration, NodeList<ImportDeclaration> imports, NodeList<TypeDeclaration<?>> types, ModuleDeclaration module) { 114 super(tokenRange); 115 setPackageDeclaration(packageDeclaration); 116 setImports(imports); 117 setTypes(types); 118 setModule(module); 119 customInitialization(); 120 } 121 122 @Override 123 @Generated("com.github.javaparser.generator.core.node.AcceptGenerator") accept(final GenericVisitor<R, A> v, final A arg)124 public <R, A> R accept(final GenericVisitor<R, A> v, final A arg) { 125 return v.visit(this, arg); 126 } 127 128 @Override 129 @Generated("com.github.javaparser.generator.core.node.AcceptGenerator") accept(final VoidVisitor<A> v, final A arg)130 public <A> void accept(final VoidVisitor<A> v, final A arg) { 131 v.visit(this, arg); 132 } 133 134 /** 135 * Return a list containing all comments declared in this compilation unit. 136 * Including javadocs, line comments and block comments of all types, 137 * inner-classes and other members.<br> 138 * If there is no comment, an empty list is returned. 139 * 140 * @return list with all comments of this compilation unit. 141 * @see JavadocComment 142 * @see com.github.javaparser.ast.comments.LineComment 143 * @see com.github.javaparser.ast.comments.BlockComment 144 */ getComments()145 public List<Comment> getComments() { 146 return this.getAllContainedComments(); 147 } 148 149 /** 150 * Retrieves the list of imports declared in this compilation unit or 151 * <code>null</code> if there is no import. 152 * 153 * @return the list of imports or <code>none</code> if there is no import 154 */ 155 @Generated("com.github.javaparser.generator.core.node.PropertyGenerator") getImports()156 public NodeList<ImportDeclaration> getImports() { 157 return imports; 158 } 159 getImport(int i)160 public ImportDeclaration getImport(int i) { 161 return getImports().get(i); 162 } 163 164 /** 165 * Retrieves the package declaration of this compilation unit.<br> 166 * If this compilation unit has no package declaration (default package), 167 * <code>Optional.none()</code> is returned. 168 * 169 * @return the package declaration or <code>none</code> 170 */ 171 @Generated("com.github.javaparser.generator.core.node.PropertyGenerator") getPackageDeclaration()172 public Optional<PackageDeclaration> getPackageDeclaration() { 173 return Optional.ofNullable(packageDeclaration); 174 } 175 176 /** 177 * Return the list of top level types declared in this compilation unit.<br> 178 * If there are no types declared, <code>none</code> is returned. 179 * 180 * @return the list of types or <code>none</code> null if there is no type 181 * @see AnnotationDeclaration 182 * @see ClassOrInterfaceDeclaration 183 * @see EnumDeclaration 184 */ 185 @Generated("com.github.javaparser.generator.core.node.PropertyGenerator") getTypes()186 public NodeList<TypeDeclaration<?>> getTypes() { 187 return types; 188 } 189 190 /** 191 * Convenience method that wraps <code>getTypes()</code>.<br> 192 * If <code>i</code> is out of bounds, throws <code>IndexOutOfBoundsException.</code> 193 * 194 * @param i the index of the type declaration to retrieve 195 */ getType(int i)196 public TypeDeclaration<?> getType(int i) { 197 return getTypes().get(i); 198 } 199 200 /** 201 * Sets the list of imports of this compilation unit. The list is initially 202 * <code>null</code>. 203 * 204 * @param imports the list of imports 205 */ 206 @Generated("com.github.javaparser.generator.core.node.PropertyGenerator") setImports(final NodeList<ImportDeclaration> imports)207 public CompilationUnit setImports(final NodeList<ImportDeclaration> imports) { 208 assertNotNull(imports); 209 if (imports == this.imports) { 210 return (CompilationUnit) this; 211 } 212 notifyPropertyChange(ObservableProperty.IMPORTS, this.imports, imports); 213 if (this.imports != null) 214 this.imports.setParentNode(null); 215 this.imports = imports; 216 setAsParentNodeOf(imports); 217 return this; 218 } 219 setImport(int i, ImportDeclaration imports)220 public CompilationUnit setImport(int i, ImportDeclaration imports) { 221 getImports().set(i, imports); 222 return this; 223 } 224 addImport(ImportDeclaration importDeclaration)225 public CompilationUnit addImport(ImportDeclaration importDeclaration) { 226 if (getImports().stream().noneMatch(im -> im.toString().equals(importDeclaration.toString()))) { 227 getImports().add(importDeclaration); 228 } 229 return this; 230 } 231 232 /** 233 * Sets or clear the package declarations of this compilation unit. 234 * 235 * @param packageDeclaration the packageDeclaration declaration to set or <code>null</code> to default package 236 */ 237 @Generated("com.github.javaparser.generator.core.node.PropertyGenerator") setPackageDeclaration(final PackageDeclaration packageDeclaration)238 public CompilationUnit setPackageDeclaration(final PackageDeclaration packageDeclaration) { 239 if (packageDeclaration == this.packageDeclaration) { 240 return (CompilationUnit) this; 241 } 242 notifyPropertyChange(ObservableProperty.PACKAGE_DECLARATION, this.packageDeclaration, packageDeclaration); 243 if (this.packageDeclaration != null) 244 this.packageDeclaration.setParentNode(null); 245 this.packageDeclaration = packageDeclaration; 246 setAsParentNodeOf(packageDeclaration); 247 return this; 248 } 249 250 /** 251 * Sets the list of types declared in this compilation unit. 252 */ 253 @Generated("com.github.javaparser.generator.core.node.PropertyGenerator") setTypes(final NodeList<TypeDeclaration<?>> types)254 public CompilationUnit setTypes(final NodeList<TypeDeclaration<?>> types) { 255 assertNotNull(types); 256 if (types == this.types) { 257 return (CompilationUnit) this; 258 } 259 notifyPropertyChange(ObservableProperty.TYPES, this.types, types); 260 if (this.types != null) 261 this.types.setParentNode(null); 262 this.types = types; 263 setAsParentNodeOf(types); 264 return this; 265 } 266 setType(int i, TypeDeclaration<?> type)267 public CompilationUnit setType(int i, TypeDeclaration<?> type) { 268 NodeList<TypeDeclaration<?>> copy = new NodeList<>(); 269 copy.addAll(getTypes()); 270 getTypes().set(i, type); 271 notifyPropertyChange(ObservableProperty.TYPES, copy, types); 272 return this; 273 } 274 addType(TypeDeclaration<?> type)275 public CompilationUnit addType(TypeDeclaration<?> type) { 276 NodeList<TypeDeclaration<?>> copy = new NodeList<>(); 277 copy.addAll(getTypes()); 278 getTypes().add(type); 279 notifyPropertyChange(ObservableProperty.TYPES, copy, types); 280 return this; 281 } 282 283 /** 284 * sets the package declaration of this compilation unit 285 * 286 * @param name the name of the package 287 * @return this, the {@link CompilationUnit} 288 */ setPackageDeclaration(String name)289 public CompilationUnit setPackageDeclaration(String name) { 290 setPackageDeclaration(new PackageDeclaration(parseName(name))); 291 return this; 292 } 293 294 /** 295 * Add an import to the list of {@link ImportDeclaration} of this compilation unit<br> 296 * shorthand for {@link #addImport(String, boolean, boolean)} with name,false,false 297 * 298 * @param name the import name 299 * @return this, the {@link CompilationUnit} 300 */ addImport(String name)301 public CompilationUnit addImport(String name) { 302 return addImport(name, false, false); 303 } 304 305 /** 306 * Add an import to the list of {@link ImportDeclaration} of this compilation unit<br> 307 * shorthand for {@link #addImport(String)} with clazz.getName() 308 * 309 * @param clazz the class to import 310 * @return this, the {@link CompilationUnit} 311 * @throws RuntimeException if clazz is an anonymous or local class 312 */ addImport(Class<?> clazz)313 public CompilationUnit addImport(Class<?> clazz) { 314 if (clazz.isArray()) { 315 return addImport(clazz.getComponentType()); 316 } 317 if (ClassUtils.isPrimitiveOrWrapper(clazz) || "java.lang".equals(clazz.getPackage().getName())) 318 return this; 319 else if (clazz.isMemberClass()) 320 return addImport(clazz.getName().replace("$", ".")); 321 else if (clazz.isAnonymousClass() || clazz.isLocalClass()) 322 throw new RuntimeException(clazz.getName() + " is an anonymous or local class therefore it can't be added with addImport"); 323 return addImport(clazz.getName()); 324 } 325 326 /** 327 * Add an import to the list of {@link ImportDeclaration} of this compilation unit<br> 328 * <b>This method check if no import with the same name is already in the list</b> 329 * 330 * @param name the import name 331 * @param isStatic is it an "import static" 332 * @param isAsterisk does the import end with ".*" 333 * @return this, the {@link CompilationUnit} 334 */ addImport(String name, boolean isStatic, boolean isAsterisk)335 public CompilationUnit addImport(String name, boolean isStatic, boolean isAsterisk) { 336 final StringBuilder i = new StringBuilder("import "); 337 if (isStatic) { 338 i.append("static "); 339 } 340 i.append(name); 341 if (isAsterisk) { 342 i.append(".*"); 343 } 344 i.append(";"); 345 return addImport(parseImport(i.toString())); 346 } 347 348 /** 349 * Add a public class to the types of this compilation unit 350 * 351 * @param name the class name 352 * @return the newly created class 353 */ addClass(String name)354 public ClassOrInterfaceDeclaration addClass(String name) { 355 return addClass(name, Modifier.Keyword.PUBLIC); 356 } 357 358 /** 359 * Add a class to the types of this compilation unit 360 * 361 * @param name the class name 362 * @param modifiers the modifiers (like Modifier.PUBLIC) 363 * @return the newly created class 364 */ addClass(String name, Modifier.Keyword... modifiers)365 public ClassOrInterfaceDeclaration addClass(String name, Modifier.Keyword... modifiers) { 366 ClassOrInterfaceDeclaration classOrInterfaceDeclaration = new ClassOrInterfaceDeclaration(createModifierList(modifiers), false, name); 367 getTypes().add(classOrInterfaceDeclaration); 368 return classOrInterfaceDeclaration; 369 } 370 371 /** 372 * Add a public interface class to the types of this compilation unit 373 * 374 * @param name the interface name 375 * @return the newly created class 376 */ addInterface(String name)377 public ClassOrInterfaceDeclaration addInterface(String name) { 378 return addInterface(name, Modifier.Keyword.PUBLIC); 379 } 380 381 /** 382 * Add an interface to the types of this compilation unit 383 * 384 * @param name the interface name 385 * @param modifiers the modifiers (like Modifier.PUBLIC) 386 * @return the newly created class 387 */ addInterface(String name, Modifier.Keyword... modifiers)388 public ClassOrInterfaceDeclaration addInterface(String name, Modifier.Keyword... modifiers) { 389 ClassOrInterfaceDeclaration classOrInterfaceDeclaration = new ClassOrInterfaceDeclaration(createModifierList(modifiers), true, name); 390 getTypes().add(classOrInterfaceDeclaration); 391 return classOrInterfaceDeclaration; 392 } 393 394 /** 395 * Add a public enum to the types of this compilation unit 396 * 397 * @param name the enum name 398 * @return the newly created class 399 */ addEnum(String name)400 public EnumDeclaration addEnum(String name) { 401 return addEnum(name, Modifier.Keyword.PUBLIC); 402 } 403 404 /** 405 * Add an enum to the types of this compilation unit 406 * 407 * @param name the enum name 408 * @param modifiers the modifiers (like Modifier.PUBLIC) 409 * @return the newly created class 410 */ addEnum(String name, Modifier.Keyword... modifiers)411 public EnumDeclaration addEnum(String name, Modifier.Keyword... modifiers) { 412 EnumDeclaration enumDeclaration = new EnumDeclaration(createModifierList(modifiers), name); 413 getTypes().add(enumDeclaration); 414 return enumDeclaration; 415 } 416 417 /** 418 * Add a public annotation declaration to the types of this compilation unit 419 * 420 * @param name the annotation name 421 * @return the newly created class 422 */ addAnnotationDeclaration(String name)423 public AnnotationDeclaration addAnnotationDeclaration(String name) { 424 return addAnnotationDeclaration(name, Modifier.Keyword.PUBLIC); 425 } 426 427 /** 428 * Add an annotation declaration to the types of this compilation unit 429 * 430 * @param name the annotation name 431 * @param modifiers the modifiers (like Modifier.PUBLIC) 432 * @return the newly created class 433 */ addAnnotationDeclaration(String name, Modifier.Keyword... modifiers)434 public AnnotationDeclaration addAnnotationDeclaration(String name, Modifier.Keyword... modifiers) { 435 AnnotationDeclaration annotationDeclaration = new AnnotationDeclaration(createModifierList(modifiers), name); 436 getTypes().add(annotationDeclaration); 437 return annotationDeclaration; 438 } 439 440 /** 441 * Try to get a top level class declaration by its name 442 * 443 * @param className the class name (case-sensitive) 444 */ getClassByName(String className)445 public Optional<ClassOrInterfaceDeclaration> getClassByName(String className) { 446 return getTypes().stream().filter(type -> type.getNameAsString().equals(className) && type instanceof ClassOrInterfaceDeclaration && !((ClassOrInterfaceDeclaration) type).isInterface()).findFirst().map(t -> (ClassOrInterfaceDeclaration) t); 447 } 448 449 /** 450 * Try to get a top level interface declaration by its name 451 * 452 * @param interfaceName the interface name (case-sensitive) 453 */ getInterfaceByName(String interfaceName)454 public Optional<ClassOrInterfaceDeclaration> getInterfaceByName(String interfaceName) { 455 return getTypes().stream().filter(type -> type.getNameAsString().equals(interfaceName) && type instanceof ClassOrInterfaceDeclaration && ((ClassOrInterfaceDeclaration) type).isInterface()).findFirst().map(t -> (ClassOrInterfaceDeclaration) t); 456 } 457 458 /** 459 * Try to get a top level enum declaration by its name 460 * 461 * @param enumName the enum name (case-sensitive) 462 */ getEnumByName(String enumName)463 public Optional<EnumDeclaration> getEnumByName(String enumName) { 464 return getTypes().stream().filter(type -> type.getNameAsString().equals(enumName) && type instanceof EnumDeclaration).findFirst().map(t -> (EnumDeclaration) t); 465 } 466 467 /** 468 * @return the name that the primary type in this file should have, according to the filename in {@link Storage#getFileName()}. 469 * Empty if no file information is present (when this compilation unit wasn't parsed from a file.) 470 */ getPrimaryTypeName()471 public Optional<String> getPrimaryTypeName() { 472 return getStorage().map(Storage::getFileName).map(Utils::removeFileExtension); 473 } 474 475 /** 476 * @return the type whose name corresponds to the file name. 477 * Empty if no file information is present (when this compilation unit wasn't parsed from a file.) 478 * If for some strange reason there are multiple types of this name, the first one is returned. 479 */ getPrimaryType()480 public Optional<TypeDeclaration<?>> getPrimaryType() { 481 return getPrimaryTypeName().flatMap(name -> getTypes().stream().filter(t -> t.getNameAsString().equals(name)).findFirst()); 482 } 483 484 /** 485 * Try to get a top level annotation type declaration by its name 486 * 487 * @param annotationName the annotation name (case-sensitive) 488 */ getAnnotationDeclarationByName(String annotationName)489 public Optional<AnnotationDeclaration> getAnnotationDeclarationByName(String annotationName) { 490 return getTypes().stream().filter(type -> type.getNameAsString().equals(annotationName) && type instanceof AnnotationDeclaration).findFirst().map(t -> (AnnotationDeclaration) t); 491 } 492 493 @Override 494 @Generated("com.github.javaparser.generator.core.node.RemoveMethodGenerator") remove(Node node)495 public boolean remove(Node node) { 496 if (node == null) 497 return false; 498 for (int i = 0; i < imports.size(); i++) { 499 if (imports.get(i) == node) { 500 imports.remove(i); 501 return true; 502 } 503 } 504 if (module != null) { 505 if (node == module) { 506 removeModule(); 507 return true; 508 } 509 } 510 if (packageDeclaration != null) { 511 if (node == packageDeclaration) { 512 removePackageDeclaration(); 513 return true; 514 } 515 } 516 for (int i = 0; i < types.size(); i++) { 517 if (types.get(i) == node) { 518 types.remove(i); 519 return true; 520 } 521 } 522 return super.remove(node); 523 } 524 525 @Generated("com.github.javaparser.generator.core.node.RemoveMethodGenerator") removePackageDeclaration()526 public CompilationUnit removePackageDeclaration() { 527 return setPackageDeclaration((PackageDeclaration) null); 528 } 529 530 /** 531 * @return the module declared in this compilation unit. 532 */ 533 @Generated("com.github.javaparser.generator.core.node.PropertyGenerator") getModule()534 public Optional<ModuleDeclaration> getModule() { 535 return Optional.ofNullable(module); 536 } 537 538 @Generated("com.github.javaparser.generator.core.node.PropertyGenerator") setModule(final ModuleDeclaration module)539 public CompilationUnit setModule(final ModuleDeclaration module) { 540 if (module == this.module) { 541 return (CompilationUnit) this; 542 } 543 notifyPropertyChange(ObservableProperty.MODULE, this.module, module); 544 if (this.module != null) 545 this.module.setParentNode(null); 546 this.module = module; 547 setAsParentNodeOf(module); 548 return this; 549 } 550 551 @Generated("com.github.javaparser.generator.core.node.RemoveMethodGenerator") removeModule()552 public CompilationUnit removeModule() { 553 return setModule((ModuleDeclaration) null); 554 } 555 556 /** 557 * @return information about where this compilation unit was loaded from, or empty if it wasn't loaded from a file. 558 */ getStorage()559 public Optional<Storage> getStorage() { 560 return Optional.ofNullable(storage); 561 } 562 setStorage(Path path)563 public CompilationUnit setStorage(Path path) { 564 this.storage = new Storage(this, path); 565 return this; 566 } 567 setStorage(Path path, Charset charset)568 public CompilationUnit setStorage(Path path, Charset charset) { 569 this.storage = new Storage(this, path, charset); 570 return this; 571 } 572 573 /** 574 * Create (or overwrite) a module declaration in this compilation unit with name "name". 575 * 576 * @return the module 577 */ setModule(String name)578 public ModuleDeclaration setModule(String name) { 579 final ModuleDeclaration module = new ModuleDeclaration(parseName(name), false); 580 setModule(module); 581 return module; 582 } 583 584 /** 585 * Recalculates the ranges of all nodes by looking at the sizes of the tokens. 586 * This is useful when you have manually inserted or deleted tokens and still want to use the ranges. 587 */ recalculatePositions()588 public void recalculatePositions() { 589 if (!getTokenRange().isPresent()) { 590 throw new IllegalStateException("Can't recalculate positions without tokens."); 591 } 592 Position cursor = Position.HOME; 593 for (JavaToken t : getTokenRange().get()) { 594 int tokenLength = t.getKind() == EOF.getKind() ? 0 : t.getText().length() - 1; 595 t.setRange(range(cursor, cursor.right(tokenLength))); 596 if (t.getCategory().isEndOfLine()) { 597 cursor = cursor.nextLine(); 598 } else { 599 cursor = cursor.right(tokenLength + 1); 600 } 601 } 602 } 603 604 /** 605 * Information about where this compilation unit was loaded from. 606 * This class only stores the absolute location. 607 * For more flexibility use SourceRoot. 608 */ 609 public static class Storage { 610 611 private final CompilationUnit compilationUnit; 612 613 private final Path path; 614 615 private final Charset encoding; 616 Storage(CompilationUnit compilationUnit, Path path)617 private Storage(CompilationUnit compilationUnit, Path path) { 618 this.compilationUnit = compilationUnit; 619 this.path = path.toAbsolutePath(); 620 this.encoding = UTF8; 621 } 622 Storage(CompilationUnit compilationUnit, Path path, Charset encoding)623 private Storage(CompilationUnit compilationUnit, Path path, Charset encoding) { 624 this.compilationUnit = compilationUnit; 625 this.path = path.toAbsolutePath(); 626 this.encoding = encoding; 627 } 628 629 /** 630 * @return the path to the source for this CompilationUnit 631 */ getPath()632 public Path getPath() { 633 return path; 634 } 635 636 /** 637 * @return the CompilationUnit this Storage is about. 638 */ getCompilationUnit()639 public CompilationUnit getCompilationUnit() { 640 return compilationUnit; 641 } 642 643 /** 644 * @return the encoding used to read the file. 645 */ getEncoding()646 public Charset getEncoding() { 647 return encoding; 648 } 649 650 /** 651 * @return the source root directory, calculated from the path of this compiation unit, and the package 652 * declaration of this compilation unit. If the package declaration is invalid (when it does not match the end 653 * of the path) a RuntimeException is thrown. 654 */ getSourceRoot()655 public Path getSourceRoot() { 656 final Optional<String> pkgAsString = compilationUnit.getPackageDeclaration().map(NodeWithName::getNameAsString); 657 return pkgAsString.map(p -> Paths.get(CodeGenerationUtils.packageToPath(p))).map(pkg -> subtractPaths(getDirectory(), pkg)).orElse(getDirectory()); 658 } 659 getFileName()660 public String getFileName() { 661 return path.getFileName().toString(); 662 } 663 getDirectory()664 public Path getDirectory() { 665 return path.getParent(); 666 } 667 668 /** 669 * Saves the compilation unit to its original location 670 */ save()671 public void save() { 672 save(cu -> new PrettyPrinter().print(cu)); 673 } 674 675 /** 676 * Saves a compilation unit to its original location with formatting according to the function passed as a 677 * parameter. 678 * 679 * @param makeOutput a function that formats the compilation unit 680 */ save(Function<CompilationUnit, String> makeOutput)681 public void save(Function<CompilationUnit, String> makeOutput) { 682 save(makeOutput, encoding); 683 } 684 685 /** 686 * Saves a compilation unit to its original location with formatting and encoding according to the function and 687 * encoding passed as a parameter. 688 * 689 * @param makeOutput a function that formats the compilation unit 690 * @param encoding the encoding to use for the saved file 691 */ save(Function<CompilationUnit, String> makeOutput, Charset encoding)692 public void save(Function<CompilationUnit, String> makeOutput, Charset encoding) { 693 try { 694 Files.createDirectories(path.getParent()); 695 final String code = makeOutput.apply(getCompilationUnit()); 696 Files.write(path, code.getBytes(encoding)); 697 } catch (IOException e) { 698 throw new RuntimeException(e); 699 } 700 } 701 reparse(JavaParser javaParser)702 public ParseResult<CompilationUnit> reparse(JavaParser javaParser) { 703 try { 704 return javaParser.parse(ParseStart.COMPILATION_UNIT, provider(getPath())); 705 } catch (IOException e) { 706 throw new RuntimeException(e); 707 } 708 } 709 } 710 711 @Override 712 @Generated("com.github.javaparser.generator.core.node.CloneGenerator") clone()713 public CompilationUnit clone() { 714 return (CompilationUnit) accept(new CloneVisitor(), null); 715 } 716 717 @Override 718 @Generated("com.github.javaparser.generator.core.node.GetMetaModelGenerator") getMetaModel()719 public CompilationUnitMetaModel getMetaModel() { 720 return JavaParserMetaModel.compilationUnitMetaModel; 721 } 722 723 @Override 724 @Generated("com.github.javaparser.generator.core.node.ReplaceMethodGenerator") replace(Node node, Node replacementNode)725 public boolean replace(Node node, Node replacementNode) { 726 if (node == null) 727 return false; 728 for (int i = 0; i < imports.size(); i++) { 729 if (imports.get(i) == node) { 730 imports.set(i, (ImportDeclaration) replacementNode); 731 return true; 732 } 733 } 734 if (module != null) { 735 if (node == module) { 736 setModule((ModuleDeclaration) replacementNode); 737 return true; 738 } 739 } 740 if (packageDeclaration != null) { 741 if (node == packageDeclaration) { 742 setPackageDeclaration((PackageDeclaration) replacementNode); 743 return true; 744 } 745 } 746 for (int i = 0; i < types.size(); i++) { 747 if (types.get(i) == node) { 748 types.set(i, (TypeDeclaration) replacementNode); 749 return true; 750 } 751 } 752 return super.replace(node, replacementNode); 753 } 754 } 755