• 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 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