CompilationUnit.java revision 4e845ed7ad59e4d904f164bc161d6afc1db0255b
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 */
21package com.github.javaparser.ast;
22
23import com.github.javaparser.JavaParser;
24import com.github.javaparser.Range;
25import com.github.javaparser.ast.body.AnnotationDeclaration;
26import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
27import com.github.javaparser.ast.body.EnumDeclaration;
28import com.github.javaparser.ast.body.TypeDeclaration;
29import com.github.javaparser.ast.comments.Comment;
30import com.github.javaparser.ast.comments.JavadocComment;
31import com.github.javaparser.ast.expr.Name;
32import com.github.javaparser.ast.modules.ModuleDeclaration;
33import com.github.javaparser.ast.observer.ObservableProperty;
34import com.github.javaparser.ast.visitor.CloneVisitor;
35import com.github.javaparser.ast.visitor.GenericVisitor;
36import com.github.javaparser.ast.visitor.VoidVisitor;
37import com.github.javaparser.metamodel.CompilationUnitMetaModel;
38import com.github.javaparser.metamodel.InternalProperty;
39import com.github.javaparser.metamodel.JavaParserMetaModel;
40import com.github.javaparser.utils.ClassUtils;
41import java.util.Arrays;
42import java.util.EnumSet;
43import java.util.List;
44import java.util.Optional;
45import java.util.stream.Collectors;
46import static com.github.javaparser.JavaParser.parseName;
47import static com.github.javaparser.utils.Utils.assertNotNull;
48import javax.annotation.Generated;
49
50/**
51 * <p>
52 * This class represents the entire compilation unit. Each java file denotes a
53 * compilation unit.
54 * </p>
55 * A compilation unit start with an optional package declaration,
56 * followed by zero or more import declarations,
57 * followed by zero or more type declarations.
58 *
59 * @author Julio Vilmar Gesser
60 * @see PackageDeclaration
61 * @see ImportDeclaration
62 * @see TypeDeclaration
63 */
64public final class CompilationUnit extends Node {
65
66    private PackageDeclaration packageDeclaration;
67
68    private NodeList<ImportDeclaration> imports;
69
70    private NodeList<TypeDeclaration<?>> types;
71
72    private ModuleDeclaration module;
73
74    public CompilationUnit() {
75        this(null, null, new NodeList<>(), new NodeList<>(), null);
76    }
77
78    public CompilationUnit(String packageDeclaration) {
79        this(null, new PackageDeclaration(new Name(packageDeclaration)), new NodeList<>(), new NodeList<>(), null);
80    }
81
82    @AllFieldsConstructor
83    public CompilationUnit(PackageDeclaration packageDeclaration, NodeList<ImportDeclaration> imports, NodeList<TypeDeclaration<?>> types, ModuleDeclaration module) {
84        this(null, packageDeclaration, imports, types, module);
85    }
86
87    /**This constructor is used by the parser and is considered private.*/
88    @Generated("com.github.javaparser.generator.core.node.MainConstructorGenerator")
89    public CompilationUnit(Range range, PackageDeclaration packageDeclaration, NodeList<ImportDeclaration> imports, NodeList<TypeDeclaration<?>> types, ModuleDeclaration module) {
90        super(range);
91        setPackageDeclaration(packageDeclaration);
92        setImports(imports);
93        setTypes(types);
94        setModule(module);
95        customInitialization();
96    }
97
98    @Override
99    public <R, A> R accept(GenericVisitor<R, A> v, A arg) {
100        return v.visit(this, arg);
101    }
102
103    @Override
104    public <A> void accept(VoidVisitor<A> v, A arg) {
105        v.visit(this, arg);
106    }
107
108    /**
109     * Return a list containing all comments declared in this compilation unit.
110     * Including javadocs, line comments and block comments of all types,
111     * inner-classes and other members.<br>
112     * If there is no comment, an empty list is returned.
113     *
114     * @return list with all comments of this compilation unit.
115     * @see JavadocComment
116     * @see com.github.javaparser.ast.comments.LineComment
117     * @see com.github.javaparser.ast.comments.BlockComment
118     */
119    public List<Comment> getComments() {
120        return this.getAllContainedComments();
121    }
122
123    /**
124     * Retrieves the list of imports declared in this compilation unit or
125     * <code>null</code> if there is no import.
126     *
127     * @return the list of imports or <code>none</code> if there is no import
128     */
129    public NodeList<ImportDeclaration> getImports() {
130        return imports;
131    }
132
133    public ImportDeclaration getImport(int i) {
134        return getImports().get(i);
135    }
136
137    /**
138     * Retrieves the package declaration of this compilation unit.<br>
139     * If this compilation unit has no package declaration (default package),
140     * <code>Optional.none()</code> is returned.
141     *
142     * @return the package declaration or <code>none</code>
143     */
144    public Optional<PackageDeclaration> getPackageDeclaration() {
145        return Optional.ofNullable(packageDeclaration);
146    }
147
148    /**
149     * Return the list of types declared in this compilation unit.<br>
150     * If there is no types declared, <code>none</code> is returned.
151     *
152     * @return the list of types or <code>none</code> null if there is no type
153     * @see AnnotationDeclaration
154     * @see ClassOrInterfaceDeclaration
155     * @see EnumDeclaration
156     */
157    public NodeList<TypeDeclaration<?>> getTypes() {
158        return types;
159    }
160
161    /**
162     * Convenience method that wraps <code>getTypes()</code>.<br>
163     * If <code>i</code> is out of bounds, throws <code>IndexOutOfBoundsException.</code>
164     *
165     * @param i the index of the type declaration to retrieve
166     */
167    public TypeDeclaration<?> getType(int i) {
168        return getTypes().get(i);
169    }
170
171    /**
172     * Sets the list of imports of this compilation unit. The list is initially
173     * <code>null</code>.
174     *
175     * @param imports the list of imports
176     */
177    public CompilationUnit setImports(final NodeList<ImportDeclaration> imports) {
178        assertNotNull(imports);
179        if (imports == this.imports) {
180            return (CompilationUnit) this;
181        }
182        notifyPropertyChange(ObservableProperty.IMPORTS, this.imports, imports);
183        if (this.imports != null)
184            this.imports.setParentNode(null);
185        this.imports = imports;
186        setAsParentNodeOf(imports);
187        return this;
188    }
189
190    public CompilationUnit setImport(int i, ImportDeclaration imports) {
191        getImports().set(i, imports);
192        return this;
193    }
194
195    public CompilationUnit addImport(ImportDeclaration imports) {
196        getImports().add(imports);
197        return this;
198    }
199
200    /**
201     * Sets or clear the package declarations of this compilation unit.
202     *
203     * @param packageDeclaration the packageDeclaration declaration to set or <code>null</code> to default package
204     */
205    public CompilationUnit setPackageDeclaration(final PackageDeclaration packageDeclaration) {
206        if (packageDeclaration == this.packageDeclaration) {
207            return (CompilationUnit) this;
208        }
209        notifyPropertyChange(ObservableProperty.PACKAGE_DECLARATION, this.packageDeclaration, packageDeclaration);
210        if (this.packageDeclaration != null)
211            this.packageDeclaration.setParentNode(null);
212        this.packageDeclaration = packageDeclaration;
213        setAsParentNodeOf(packageDeclaration);
214        return this;
215    }
216
217    /**
218     * Sets the list of types declared in this compilation unit.
219     */
220    public CompilationUnit setTypes(final NodeList<TypeDeclaration<?>> types) {
221        assertNotNull(types);
222        if (types == this.types) {
223            return (CompilationUnit) this;
224        }
225        notifyPropertyChange(ObservableProperty.TYPES, this.types, types);
226        if (this.types != null)
227            this.types.setParentNode(null);
228        this.types = types;
229        setAsParentNodeOf(types);
230        return this;
231    }
232
233    public CompilationUnit setType(int i, TypeDeclaration<?> type) {
234        NodeList<TypeDeclaration<?>> copy = new NodeList<>();
235        copy.addAll(getTypes());
236        getTypes().set(i, type);
237        notifyPropertyChange(ObservableProperty.TYPES, copy, types);
238        return this;
239    }
240
241    public CompilationUnit addType(TypeDeclaration<?> type) {
242        NodeList<TypeDeclaration<?>> copy = new NodeList<>();
243        copy.addAll(getTypes());
244        getTypes().add(type);
245        notifyPropertyChange(ObservableProperty.TYPES, copy, types);
246        return this;
247    }
248
249    /**
250     * sets the package declaration of this compilation unit
251     *
252     * @param name the name of the package
253     * @return this, the {@link CompilationUnit}
254     */
255    public CompilationUnit setPackageDeclaration(String name) {
256        setPackageDeclaration(new PackageDeclaration(parseName(name)));
257        return this;
258    }
259
260    /**
261     * Add an import to the list of {@link ImportDeclaration} of this compilation unit<br>
262     * shorthand for {@link #addImport(String, boolean, boolean)} with name,false,false
263     *
264     * @param name the import name
265     * @return this, the {@link CompilationUnit}
266     */
267    public CompilationUnit addImport(String name) {
268        return addImport(name, false, false);
269    }
270
271    /**
272     * Add an import to the list of {@link ImportDeclaration} of this compilation unit<br>
273     * shorthand for {@link #addImport(String)} with clazz.getName()
274     *
275     * @param clazz the class to import
276     * @return this, the {@link CompilationUnit}
277     */
278    public CompilationUnit addImport(Class<?> clazz) {
279        if (ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.getName().startsWith("java.lang"))
280            return this;
281        else if (clazz.isArray() && !ClassUtils.isPrimitiveOrWrapper(clazz.getComponentType()) && !clazz.getComponentType().getName().startsWith("java.lang"))
282            return addImport(clazz.getComponentType().getName());
283        return addImport(clazz.getName());
284    }
285
286    /**
287     * Add an import to the list of {@link ImportDeclaration} of this compilation unit<br>
288     * <b>This method check if no import with the same name is already in the list</b>
289     *
290     * @param name the import name
291     * @param isStatic is it an "import static"
292     * @param isAsterisk does the import end with ".*"
293     * @return this, the {@link CompilationUnit}
294     */
295    public CompilationUnit addImport(String name, boolean isStatic, boolean isAsterisk) {
296        final StringBuilder i = new StringBuilder("import ");
297        if (isStatic) {
298            i.append("static ");
299        }
300        i.append(name);
301        if (isAsterisk) {
302            i.append(".*");
303        }
304        i.append(";");
305        ImportDeclaration importDeclaration = JavaParser.parseImport(i.toString());
306        if (getImports().stream().anyMatch(im -> im.toString().equals(importDeclaration.toString())))
307            return this;
308        else {
309            getImports().add(importDeclaration);
310            return this;
311        }
312    }
313
314    /**
315     * Add a public class to the types of this compilation unit
316     *
317     * @param name the class name
318     * @return the newly created class
319     */
320    public ClassOrInterfaceDeclaration addClass(String name) {
321        return addClass(name, Modifier.PUBLIC);
322    }
323
324    /**
325     * Add a class to the types of this compilation unit
326     *
327     * @param name the class name
328     * @param modifiers the modifiers (like Modifier.PUBLIC)
329     * @return the newly created class
330     */
331    public ClassOrInterfaceDeclaration addClass(String name, Modifier... modifiers) {
332        ClassOrInterfaceDeclaration classOrInterfaceDeclaration = new ClassOrInterfaceDeclaration(Arrays.stream(modifiers).collect(Collectors.toCollection(() -> EnumSet.noneOf(Modifier.class))), false, name);
333        getTypes().add(classOrInterfaceDeclaration);
334        return classOrInterfaceDeclaration;
335    }
336
337    /**
338     * Add a public interface class to the types of this compilation unit
339     *
340     * @param name the interface name
341     * @return the newly created class
342     */
343    public ClassOrInterfaceDeclaration addInterface(String name) {
344        return addInterface(name, Modifier.PUBLIC);
345    }
346
347    /**
348     * Add an interface to the types of this compilation unit
349     *
350     * @param name the interface name
351     * @param modifiers the modifiers (like Modifier.PUBLIC)
352     * @return the newly created class
353     */
354    public ClassOrInterfaceDeclaration addInterface(String name, Modifier... modifiers) {
355        ClassOrInterfaceDeclaration classOrInterfaceDeclaration = new ClassOrInterfaceDeclaration(Arrays.stream(modifiers).collect(Collectors.toCollection(() -> EnumSet.noneOf(Modifier.class))), true, name);
356        getTypes().add(classOrInterfaceDeclaration);
357        return classOrInterfaceDeclaration;
358    }
359
360    /**
361     * Add a public enum to the types of this compilation unit
362     *
363     * @param name the enum name
364     * @return the newly created class
365     */
366    public EnumDeclaration addEnum(String name) {
367        return addEnum(name, Modifier.PUBLIC);
368    }
369
370    /**
371     * Add an enum to the types of this compilation unit
372     *
373     * @param name the enum name
374     * @param modifiers the modifiers (like Modifier.PUBLIC)
375     * @return the newly created class
376     */
377    public EnumDeclaration addEnum(String name, Modifier... modifiers) {
378        EnumDeclaration enumDeclaration = new EnumDeclaration(Arrays.stream(modifiers).collect(Collectors.toCollection(() -> EnumSet.noneOf(Modifier.class))), name);
379        getTypes().add(enumDeclaration);
380        return enumDeclaration;
381    }
382
383    /**
384     * Add a public annotation declaration to the types of this compilation unit
385     *
386     * @param name the annotation name
387     * @return the newly created class
388     */
389    public AnnotationDeclaration addAnnotationDeclaration(String name) {
390        return addAnnotationDeclaration(name, Modifier.PUBLIC);
391    }
392
393    /**
394     * Add an annotation declaration to the types of this compilation unit
395     *
396     * @param name the annotation name
397     * @param modifiers the modifiers (like Modifier.PUBLIC)
398     * @return the newly created class
399     */
400    public AnnotationDeclaration addAnnotationDeclaration(String name, Modifier... modifiers) {
401        AnnotationDeclaration annotationDeclaration = new AnnotationDeclaration(Arrays.stream(modifiers).collect(Collectors.toCollection(() -> EnumSet.noneOf(Modifier.class))), name);
402        getTypes().add(annotationDeclaration);
403        return annotationDeclaration;
404    }
405
406    /**
407     * Try to get a class by its name
408     *
409     * @param className the class name (case-sensitive)
410     */
411    public Optional<ClassOrInterfaceDeclaration> getClassByName(String className) {
412        return getTypes().stream().filter(type -> type.getNameAsString().equals(className) && type instanceof ClassOrInterfaceDeclaration && !((ClassOrInterfaceDeclaration) type).isInterface()).findFirst().map(t -> (ClassOrInterfaceDeclaration) t);
413    }
414
415    /**
416     * Try to get an interface by its name
417     *
418     * @param interfaceName the interface name (case-sensitive)
419     */
420    public Optional<ClassOrInterfaceDeclaration> getInterfaceByName(String interfaceName) {
421        return getTypes().stream().filter(type -> type.getNameAsString().equals(interfaceName) && type instanceof ClassOrInterfaceDeclaration && ((ClassOrInterfaceDeclaration) type).isInterface()).findFirst().map(t -> (ClassOrInterfaceDeclaration) t);
422    }
423
424    /**
425     * Try to get an enum by its name
426     *
427     * @param enumName the enum name (case-sensitive)
428     */
429    public Optional<EnumDeclaration> getEnumByName(String enumName) {
430        return getTypes().stream().filter(type -> type.getNameAsString().equals(enumName) && type instanceof EnumDeclaration).findFirst().map(t -> (EnumDeclaration) t);
431    }
432
433    /**
434     * Try to get an annotation by its name
435     *
436     * @param annotationName the annotation name (case-sensitive)
437     */
438    public Optional<AnnotationDeclaration> getAnnotationDeclarationByName(String annotationName) {
439        return getTypes().stream().filter(type -> type.getNameAsString().equals(annotationName) && type instanceof AnnotationDeclaration).findFirst().map(t -> (AnnotationDeclaration) t);
440    }
441
442    @Override
443    public List<NodeList<?>> getNodeLists() {
444        return Arrays.asList(getImports(), getTypes());
445    }
446
447    @Override
448    public boolean remove(Node node) {
449        if (node == null)
450            return false;
451        for (int i = 0; i < imports.size(); i++) {
452            if (imports.get(i) == node) {
453                imports.remove(i);
454                return true;
455            }
456        }
457        if (module != null) {
458            if (node == module) {
459                removeModule();
460                return true;
461            }
462        }
463        if (packageDeclaration != null) {
464            if (node == packageDeclaration) {
465                removePackageDeclaration();
466                return true;
467            }
468        }
469        for (int i = 0; i < types.size(); i++) {
470            if (types.get(i) == node) {
471                types.remove(i);
472                return true;
473            }
474        }
475        return super.remove(node);
476    }
477
478    public CompilationUnit removePackageDeclaration() {
479        return setPackageDeclaration((PackageDeclaration) null);
480    }
481
482    public Optional<ModuleDeclaration> getModule() {
483        return Optional.ofNullable(module);
484    }
485
486    public CompilationUnit setModule(final ModuleDeclaration module) {
487        if (module == this.module) {
488            return (CompilationUnit) this;
489        }
490        notifyPropertyChange(ObservableProperty.MODULE, this.module, module);
491        if (this.module != null)
492            this.module.setParentNode(null);
493        this.module = module;
494        setAsParentNodeOf(module);
495        return this;
496    }
497
498    public CompilationUnit removeModule() {
499        return setModule((ModuleDeclaration) null);
500    }
501
502    @Override
503    public CompilationUnit clone() {
504        return (CompilationUnit) accept(new CloneVisitor(), null);
505    }
506
507    @Override
508    public CompilationUnitMetaModel getMetaModel() {
509        return JavaParserMetaModel.compilationUnitMetaModel;
510    }
511}
512