View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.project;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.Writer;
24  import java.nio.file.Path;
25  import java.util.AbstractSequentialList;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.LinkedHashMap;
31  import java.util.LinkedHashSet;
32  import java.util.List;
33  import java.util.ListIterator;
34  import java.util.Map;
35  import java.util.Objects;
36  import java.util.Properties;
37  import java.util.Set;
38  import java.util.function.Predicate;
39  import java.util.stream.Stream;
40  
41  import org.apache.maven.RepositoryUtils;
42  import org.apache.maven.api.Language;
43  import org.apache.maven.api.ProjectScope;
44  import org.apache.maven.api.SourceRoot;
45  import org.apache.maven.artifact.Artifact;
46  import org.apache.maven.artifact.ArtifactUtils;
47  import org.apache.maven.artifact.DependencyResolutionRequiredException;
48  import org.apache.maven.artifact.factory.ArtifactFactory;
49  import org.apache.maven.artifact.handler.ArtifactHandler;
50  import org.apache.maven.artifact.repository.ArtifactRepository;
51  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
52  import org.apache.maven.impl.DefaultSourceRoot;
53  import org.apache.maven.lifecycle.internal.DefaultProjectArtifactFactory;
54  import org.apache.maven.model.Build;
55  import org.apache.maven.model.CiManagement;
56  import org.apache.maven.model.Contributor;
57  import org.apache.maven.model.Dependency;
58  import org.apache.maven.model.DependencyManagement;
59  import org.apache.maven.model.Developer;
60  import org.apache.maven.model.DistributionManagement;
61  import org.apache.maven.model.Extension;
62  import org.apache.maven.model.IssueManagement;
63  import org.apache.maven.model.License;
64  import org.apache.maven.model.MailingList;
65  import org.apache.maven.model.Model;
66  import org.apache.maven.model.Organization;
67  import org.apache.maven.model.Plugin;
68  import org.apache.maven.model.PluginExecution;
69  import org.apache.maven.model.PluginManagement;
70  import org.apache.maven.model.Prerequisites;
71  import org.apache.maven.model.Profile;
72  import org.apache.maven.model.ReportPlugin;
73  import org.apache.maven.model.ReportSet;
74  import org.apache.maven.model.Reporting;
75  import org.apache.maven.model.Repository;
76  import org.apache.maven.model.Resource;
77  import org.apache.maven.model.Scm;
78  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
79  import org.apache.maven.model.root.RootLocator;
80  import org.apache.maven.project.artifact.InvalidDependencyVersionException;
81  import org.codehaus.plexus.classworlds.realm.ClassRealm;
82  import org.codehaus.plexus.util.xml.Xpp3Dom;
83  import org.eclipse.aether.graph.DependencyFilter;
84  import org.eclipse.aether.repository.RemoteRepository;
85  import org.slf4j.Logger;
86  import org.slf4j.LoggerFactory;
87  
88  /**
89   * The concern of the project is provide runtime values based on the model.
90   * <p>
91   * The values in the model remain untouched but during the process of building a project notions like inheritance and
92   * interpolation can be added. This allows to have an entity which is useful in a runtime while preserving the model so
93   * that it can be marshalled and unmarshalled without being tainted by runtime requirements.
94   * </p>
95   * <p>
96   * With changes during 3.2.2 release MavenProject is closer to being immutable after construction with the removal of
97   * all components from this class, and the upfront construction taken care of entirely by the {@link ProjectBuilder}.
98   * There is still the issue of having to run the lifecycle in order to find all the compile source roots and resource
99   * directories but I hope to take care of this during the Maven 4.0 release (jvz).
100  * </p>
101  */
102 public class MavenProject implements Cloneable {
103 
104     private static final Logger LOGGER = LoggerFactory.getLogger(MavenProject.class);
105 
106     public static final String EMPTY_PROJECT_GROUP_ID = "unknown";
107 
108     public static final String EMPTY_PROJECT_ARTIFACT_ID = "empty-project";
109 
110     public static final String EMPTY_PROJECT_VERSION = "0";
111 
112     private Model model;
113 
114     private MavenProject parent;
115 
116     private File file;
117 
118     private File basedir;
119 
120     private Path rootDirectory;
121 
122     private Set<Artifact> resolvedArtifacts;
123 
124     private ArtifactFilter artifactFilter;
125 
126     private Set<Artifact> artifacts;
127 
128     private Artifact parentArtifact;
129 
130     private Set<Artifact> pluginArtifacts;
131 
132     @Deprecated
133     private List<ArtifactRepository> remoteArtifactRepositories;
134 
135     @Deprecated
136     private List<ArtifactRepository> pluginArtifactRepositories;
137 
138     private List<RemoteRepository> remoteProjectRepositories;
139 
140     private List<RemoteRepository> remotePluginRepositories;
141 
142     private List<Artifact> attachedArtifacts = new ArrayList<>();
143 
144     private MavenProject executionProject;
145 
146     private List<MavenProject> collectedProjects;
147 
148     /**
149      * All sources of this project, in the order they were added.
150      */
151     private Set<SourceRoot> sources = new LinkedHashSet<>();
152 
153     @Deprecated
154     private ArtifactRepository releaseArtifactRepository;
155 
156     @Deprecated
157     private ArtifactRepository snapshotArtifactRepository;
158 
159     private List<Profile> activeProfiles = new ArrayList<>();
160 
161     private Map<String, List<String>> injectedProfileIds = new LinkedHashMap<>();
162 
163     @Deprecated
164     private Set<Artifact> dependencyArtifacts;
165 
166     private Artifact artifact;
167 
168     // calculated.
169     private Map<String, Artifact> artifactMap;
170 
171     private Model originalModel;
172 
173     private Map<String, Artifact> pluginArtifactMap;
174 
175     @Deprecated
176     private Set<Artifact> reportArtifacts;
177 
178     @Deprecated
179     private Map<String, Artifact> reportArtifactMap;
180 
181     @Deprecated
182     private Set<Artifact> extensionArtifacts;
183 
184     @Deprecated
185     private Map<String, Artifact> extensionArtifactMap;
186 
187     private Map<String, Artifact> managedVersionMap;
188 
189     private Map<String, MavenProject> projectReferences = new HashMap<>();
190 
191     private boolean executionRoot;
192 
193     private File parentFile;
194 
195     private Map<String, Object> context;
196 
197     private ClassRealm classRealm;
198 
199     private DependencyFilter extensionDependencyFilter;
200 
201     private final Set<String> lifecyclePhases = Collections.synchronizedSet(new LinkedHashSet<>());
202 
203     public MavenProject() {
204         Model model = new Model();
205 
206         model.setGroupId(EMPTY_PROJECT_GROUP_ID);
207         model.setArtifactId(EMPTY_PROJECT_ARTIFACT_ID);
208         model.setVersion(EMPTY_PROJECT_VERSION);
209 
210         setModel(model);
211     }
212 
213     public MavenProject(org.apache.maven.api.model.Model model) {
214         this(new Model(model));
215     }
216 
217     public MavenProject(Model model) {
218         setModel(model);
219     }
220 
221     public MavenProject(MavenProject project) {
222         deepCopy(project);
223     }
224 
225     public File getParentFile() {
226         return parentFile;
227     }
228 
229     public void setParentFile(File parentFile) {
230         this.parentFile = parentFile;
231     }
232 
233     // ----------------------------------------------------------------------
234     // Accessors
235     // ----------------------------------------------------------------------
236 
237     public Artifact getArtifact() {
238         return artifact;
239     }
240 
241     public void setArtifact(Artifact artifact) {
242         this.artifact = artifact;
243     }
244 
245     // TODO I would like to get rid of this. jvz.
246     public Model getModel() {
247         return model;
248     }
249 
250     /**
251      * Returns the project corresponding to a declared parent.
252      *
253      * @return the parent, or null if no parent is declared or there was an error building it
254      */
255     public MavenProject getParent() {
256         return parent;
257     }
258 
259     public void setParent(MavenProject parent) {
260         this.parent = parent;
261     }
262 
263     public boolean hasParent() {
264         return getParent() != null;
265     }
266 
267     public File getFile() {
268         return file;
269     }
270 
271     public void setFile(File file) {
272         this.file = file;
273         this.basedir = file != null ? file.getParentFile() : null;
274     }
275 
276     /**
277      * Sets project {@code file} without changing project {@code basedir}.
278      *
279      * @since 3.2.4
280      */
281     public void setPomFile(File file) {
282         this.file = file;
283     }
284 
285     /**
286      * @deprecated Replaced by {@link #getBaseDirectory()} for migrating from {@code File} to {@code Path}.
287      */
288     @Deprecated(since = "4.0.0")
289     public File getBasedir() {
290         return basedir;
291     }
292 
293     /**
294      * {@return the base directory of this project}.
295      * All source files are relative to this directory, unless they were specified as absolute paths.
296      *
297      * @since 4.0.0
298      */
299     public Path getBaseDirectory() {
300         return getBasedir().toPath();
301     }
302 
303     public void setDependencies(List<Dependency> dependencies) {
304         getModel().setDependencies(dependencies);
305     }
306 
307     public List<Dependency> getDependencies() {
308         return getModel().getDependencies();
309     }
310 
311     public DependencyManagement getDependencyManagement() {
312         return getModel().getDependencyManagement();
313     }
314 
315     // ----------------------------------------------------------------------
316     // Test and compile source roots.
317     // ----------------------------------------------------------------------
318 
319     /**
320      * Adds the given source if not already present.
321      *
322      * @param source the source to add
323      *
324      * @see #getSourceRoots()
325      *
326      * @since 4.0.0
327      */
328     public void addSourceRoot(SourceRoot source) {
329         sources.add(source);
330     }
331 
332     /**
333      * Resolves and adds the given directory as a source with the given scope and language.
334      * First, this method resolves the given root against the {@linkplain #getBaseDirectory() base directory},
335      * then normalizes the path. If a source already exists for the same scope, language and normalized directory,
336      * this method does nothing. Otherwise, the normalized directory is added as a new {@link SourceRoot} element.
337      *
338      * @param scope scope (main or test) of the directory to add
339      * @param language language of the files contained in the directory to add
340      * @param directory the directory to add if not already present in the source
341      *
342      * @see #getEnabledSourceRoots(ProjectScope, Language)
343      *
344      * @since 4.0.0
345      */
346     public void addSourceRoot(ProjectScope scope, Language language, Path directory) {
347         directory = getBaseDirectory().resolve(directory).normalize();
348         addSourceRoot(new DefaultSourceRoot(scope, language, directory));
349     }
350 
351     /**
352      * Resolves and adds the given directory as a source with the given scope and language.
353      * If the given directory is null, blank or already in the sources, then this method does nothing.
354      * Otherwise, the directory is converted to a path, resolved, normalized and finally added as a new
355      * {@link SourceRoot} element if no source exists for these scope, language and normalized directory.
356      *
357      * @param scope scope (main or test) of the directory to add
358      * @param language language of the files contained in the directory to add
359      * @param directory the directory to add if not already present in the source, or null
360      *
361      * @since 4.0.0
362      */
363     public void addSourceRoot(ProjectScope scope, Language language, String directory) {
364         if (directory != null) {
365             directory = directory.trim();
366             if (!directory.isBlank()) {
367                 Path path = getBaseDirectory().resolve(directory).normalize();
368                 addSourceRoot(scope, language, path);
369             }
370         }
371     }
372 
373     /**
374      * @deprecated Replaced by {@code addSourceRoot(ProjectScope.MAIN, Language.JAVA_FAMILY, path)}.
375      */
376     @Deprecated(since = "4.0.0")
377     public void addCompileSourceRoot(String path) {
378         addSourceRoot(ProjectScope.MAIN, Language.JAVA_FAMILY, path);
379     }
380 
381     /**
382      * @deprecated Replaced by {@code addSourceRoot(ProjectScope.TEST, Language.JAVA_FAMILY, path)}.
383      */
384     @Deprecated(since = "4.0.0")
385     public void addTestCompileSourceRoot(String path) {
386         addSourceRoot(ProjectScope.TEST, Language.JAVA_FAMILY, path);
387     }
388 
389     /**
390      * {@return all source root directories, including the disabled ones, for all languages and scopes}.
391      * The iteration order is the order in which the sources are declared in the POM file.
392      * The returned collection is unmodifiable.
393      *
394      * @see #addSourceRoot(SourceRoot)
395      */
396     public Collection<SourceRoot> getSourceRoots() {
397         return Collections.unmodifiableCollection(sources);
398     }
399 
400     /**
401      * {@return all enabled sources that provide files in the given language for the given scope}.
402      * If the given scope is {@code null}, then this method returns the enabled sources for all scopes.
403      * If the given language is {@code null}, then this method returns the enabled sources for all languages.
404      * The iteration order is the order in which the sources are declared in the POM file.
405      *
406      * @param scope the scope of the sources to return, or {@code null} for all scopes
407      * @param language the language of the sources to return, or {@code null} for all languages
408      *
409      * @see #addSourceRoot(ProjectScope, Language, Path)
410      *
411      * @since 4.0.0
412      */
413     public Stream<SourceRoot> getEnabledSourceRoots(ProjectScope scope, Language language) {
414         Stream<SourceRoot> stream = sources.stream().filter(SourceRoot::enabled);
415         if (scope != null) {
416             stream = stream.filter(source -> scope.equals(source.scope()));
417         }
418         if (language != null) {
419             stream = stream.filter(source -> language.equals(source.language()));
420         }
421         return stream;
422     }
423 
424     /**
425      * Returns a list of paths for the given scope.
426      *
427      * @deprecated Used only for the implementation of deprecated methods.
428      */
429     @Deprecated
430     private List<String> getSourceRootDirs(ProjectScope scope, Language language) {
431         return getEnabledSourceRoots(scope, language)
432                 .map((source) -> source.directory().toString())
433                 .toList();
434     }
435 
436     /**
437      * @deprecated Replaced by {@code getEnabledSourceRoots(ProjectScope.MAIN, Language.JAVA_FAMILY)}.
438      */
439     @Deprecated(since = "4.0.0")
440     public List<String> getCompileSourceRoots() {
441         return getSourceRootDirs(ProjectScope.MAIN, Language.JAVA_FAMILY);
442     }
443 
444     /**
445      * @deprecated Replaced by {@code getEnabledSourceRoots(ProjectScope.TEST, Language.JAVA_FAMILY)}.
446      */
447     @Deprecated(since = "4.0.0")
448     public List<String> getTestCompileSourceRoots() {
449         return getSourceRootDirs(ProjectScope.TEST, Language.JAVA_FAMILY);
450     }
451 
452     // TODO let the scope handler deal with this
453     private static boolean isCompilePathElement(final String scope) {
454         return Artifact.SCOPE_COMPILE.equals(scope)
455                 || Artifact.SCOPE_PROVIDED.equals(scope)
456                 || Artifact.SCOPE_SYSTEM.equals(scope);
457     }
458 
459     // TODO let the scope handler deal with this
460     private static boolean isRuntimePathElement(final String scope) {
461         return Artifact.SCOPE_COMPILE.equals(scope) || Artifact.SCOPE_RUNTIME.equals(scope);
462     }
463 
464     // TODO let the scope handler deal with this
465     private static boolean isTestPathElement(final String scope) {
466         return true;
467     }
468 
469     /**
470      * Returns a filtered list of class path elements. This method is invoked when the caller
471      * requested that all dependencies are placed on the class path, with no module path element.
472      *
473      * @param scopeFilter a filter returning {@code true} for the artifact scopes to accept
474      * @param includeTestDir whether to include the test directory in the classpath elements
475      * @return paths of all artifacts placed on the classpath
476      * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
477      */
478     private List<String> getClasspathElements(final Predicate<String> scopeFilter, final boolean includeTestDir)
479             throws DependencyResolutionRequiredException {
480         final List<String> list = new ArrayList<>(getArtifacts().size() + 2);
481         if (includeTestDir) {
482             String d = getBuild().getTestOutputDirectory();
483             if (d != null) {
484                 list.add(d);
485             }
486         }
487         String d = getBuild().getOutputDirectory();
488         if (d != null) {
489             list.add(d);
490         }
491         for (Artifact a : getArtifacts()) {
492             final File f = a.getFile();
493             if (f != null && scopeFilter.test(a.getScope())) {
494                 final ArtifactHandler h = a.getArtifactHandler();
495                 if (h.isAddedToClasspath()) {
496                     list.add(f.getPath());
497                 }
498             }
499         }
500         return list;
501     }
502 
503     /**
504      * Returns the elements placed on the classpath for compilation.
505      * This method can be invoked when the caller does not support module-path.
506      *
507      * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
508      *
509      * @deprecated This method is unreliable because it does not consider other dependency properties.
510      * See {@link org.apache.maven.api.JavaPathType} instead for better analysis.
511      */
512     @Deprecated
513     public List<String> getCompileClasspathElements() throws DependencyResolutionRequiredException {
514         return getClasspathElements(MavenProject::isCompilePathElement, false);
515     }
516 
517     /**
518      * Returns the elements placed on the classpath for tests.
519      * This method can be invoked when the caller does not support module-path.
520      *
521      * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
522      *
523      * @deprecated This method is unreliable because it does not consider other dependency properties.
524      * See {@link org.apache.maven.api.JavaPathType} instead for better analysis.
525      */
526     @Deprecated
527     public List<String> getTestClasspathElements() throws DependencyResolutionRequiredException {
528         return getClasspathElements(MavenProject::isTestPathElement, true);
529     }
530 
531     /**
532      * Returns the elements placed on the classpath for runtime.
533      * This method can be invoked when the caller does not support module-path.
534      *
535      * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
536      *
537      * @deprecated This method is unreliable because it does not consider other dependency properties.
538      * See {@link org.apache.maven.api.JavaPathType} instead for better analysis.
539      */
540     @Deprecated
541     public List<String> getRuntimeClasspathElements() throws DependencyResolutionRequiredException {
542         return getClasspathElements(MavenProject::isRuntimePathElement, false);
543     }
544 
545     // ----------------------------------------------------------------------
546     // Delegate to the model
547     // ----------------------------------------------------------------------
548 
549     public void setModelVersion(String pomVersion) {
550         getModel().setModelVersion(pomVersion);
551     }
552 
553     public String getModelVersion() {
554         return getModel().getModelVersion();
555     }
556 
557     public String getId() {
558         return getModel().getId();
559     }
560 
561     public void setGroupId(String groupId) {
562         getModel().setGroupId(groupId);
563     }
564 
565     public String getGroupId() {
566         String groupId = getModel().getGroupId();
567 
568         if ((groupId == null) && (getModel().getParent() != null)) {
569             groupId = getModel().getParent().getGroupId();
570         }
571 
572         return groupId;
573     }
574 
575     public void setArtifactId(String artifactId) {
576         getModel().setArtifactId(artifactId);
577     }
578 
579     public String getArtifactId() {
580         return getModel().getArtifactId();
581     }
582 
583     public void setName(String name) {
584         getModel().setName(name);
585     }
586 
587     public String getName() {
588         // TODO this should not be allowed to be null.
589         if (getModel().getName() != null) {
590             return getModel().getName();
591         } else {
592             return getArtifactId();
593         }
594     }
595 
596     public void setVersion(String version) {
597         getModel().setVersion(version);
598     }
599 
600     public String getVersion() {
601         String version = getModel().getVersion();
602 
603         if ((version == null) && (getModel().getParent() != null)) {
604             version = getModel().getParent().getVersion();
605         }
606 
607         return version;
608     }
609 
610     public String getPackaging() {
611         return getModel().getPackaging();
612     }
613 
614     public void setPackaging(String packaging) {
615         getModel().setPackaging(packaging);
616     }
617 
618     public void setInceptionYear(String inceptionYear) {
619         getModel().setInceptionYear(inceptionYear);
620     }
621 
622     public String getInceptionYear() {
623         return getModel().getInceptionYear();
624     }
625 
626     public void setUrl(String url) {
627         getModel().setUrl(url);
628     }
629 
630     public String getUrl() {
631         return getModel().getUrl();
632     }
633 
634     public Prerequisites getPrerequisites() {
635         return getModel().getPrerequisites();
636     }
637 
638     public void setIssueManagement(IssueManagement issueManagement) {
639         getModel().setIssueManagement(issueManagement);
640     }
641 
642     public CiManagement getCiManagement() {
643         return getModel().getCiManagement();
644     }
645 
646     public void setCiManagement(CiManagement ciManagement) {
647         getModel().setCiManagement(ciManagement);
648     }
649 
650     public IssueManagement getIssueManagement() {
651         return getModel().getIssueManagement();
652     }
653 
654     public void setDistributionManagement(DistributionManagement distributionManagement) {
655         getModel().setDistributionManagement(distributionManagement);
656     }
657 
658     public DistributionManagement getDistributionManagement() {
659         return getModel().getDistributionManagement();
660     }
661 
662     public void setDescription(String description) {
663         getModel().setDescription(description);
664     }
665 
666     public String getDescription() {
667         return getModel().getDescription();
668     }
669 
670     public void setOrganization(Organization organization) {
671         getModel().setOrganization(organization);
672     }
673 
674     public Organization getOrganization() {
675         return getModel().getOrganization();
676     }
677 
678     public void setScm(Scm scm) {
679         getModel().setScm(scm);
680     }
681 
682     public Scm getScm() {
683         return getModel().getScm();
684     }
685 
686     public void setMailingLists(List<MailingList> mailingLists) {
687         getModel().setMailingLists(mailingLists);
688     }
689 
690     public List<MailingList> getMailingLists() {
691         return getModel().getMailingLists();
692     }
693 
694     public void addMailingList(MailingList mailingList) {
695         getModel().addMailingList(mailingList);
696     }
697 
698     public void setDevelopers(List<Developer> developers) {
699         getModel().setDevelopers(developers);
700     }
701 
702     public List<Developer> getDevelopers() {
703         return getModel().getDevelopers();
704     }
705 
706     public void addDeveloper(Developer developer) {
707         getModel().addDeveloper(developer);
708     }
709 
710     public void setContributors(List<Contributor> contributors) {
711         getModel().setContributors(contributors);
712     }
713 
714     public List<Contributor> getContributors() {
715         return getModel().getContributors();
716     }
717 
718     public void addContributor(Contributor contributor) {
719         getModel().addContributor(contributor);
720     }
721 
722     public void setBuild(Build build) {
723         getModel().setBuild(build);
724     }
725 
726     public Build getBuild() {
727         return getModelBuild();
728     }
729 
730     /**
731      * @deprecated Replaced by {@code getEnabledSourceRoots(ProjectScope.MAIN, Language.RESOURCES)}.
732      */
733     @Deprecated(since = "4.0.0")
734     public List<Resource> getResources() {
735         return getResources(ProjectScope.MAIN);
736     }
737 
738     /**
739      * @deprecated Replaced by {@code getEnabledSourceRoots(ProjectScope.TEST, Language.RESOURCES)}.
740      */
741     @Deprecated(since = "4.0.0")
742     public List<Resource> getTestResources() {
743         return getResources(ProjectScope.TEST);
744     }
745 
746     private List<Resource> getResources(final ProjectScope scope) {
747         return new AbstractSequentialList<>() {
748             private Stream<SourceRoot> sources() {
749                 return getEnabledSourceRoots(scope, Language.RESOURCES);
750             }
751 
752             @Override
753             public ListIterator<Resource> listIterator(int index) {
754                 return sources().map(MavenProject::toResource).toList().listIterator(index);
755             }
756 
757             @Override
758             public int size() {
759                 return Math.toIntExact(sources().count());
760             }
761 
762             @Override
763             public boolean isEmpty() {
764                 return sources().findAny().isEmpty();
765             }
766 
767             @Override
768             public boolean add(Resource resource) {
769                 addResource(scope, resource);
770                 return true;
771             }
772         };
773     }
774 
775     private static Resource toResource(SourceRoot sourceRoot) {
776         return new Resource(org.apache.maven.api.model.Resource.newBuilder()
777                 .directory(sourceRoot.directory().toString())
778                 .includes(sourceRoot.includes().stream().map(Object::toString).toList())
779                 .excludes(sourceRoot.excludes().stream().map(Object::toString).toList())
780                 .filtering(Boolean.toString(sourceRoot.stringFiltering()))
781                 .build());
782     }
783 
784     private void addResource(ProjectScope scope, Resource resource) {
785         addSourceRoot(new DefaultSourceRoot(getBaseDirectory(), scope, resource.getDelegate()));
786     }
787 
788     /**
789      * @deprecated {@link Resource} is replaced by {@link SourceRoot}.
790      */
791     @Deprecated(since = "4.0.0")
792     public void addResource(Resource resource) {
793         addResource(ProjectScope.MAIN, resource);
794     }
795 
796     /**
797      * @deprecated {@link Resource} is replaced by {@link SourceRoot}.
798      */
799     @Deprecated(since = "4.0.0")
800     public void addTestResource(Resource testResource) {
801         addResource(ProjectScope.TEST, testResource);
802     }
803 
804     public void setLicenses(List<License> licenses) {
805         getModel().setLicenses(licenses);
806     }
807 
808     public List<License> getLicenses() {
809         return getModel().getLicenses();
810     }
811 
812     public void addLicense(License license) {
813         getModel().addLicense(license);
814     }
815 
816     public void setArtifacts(Set<Artifact> artifacts) {
817         this.artifacts = artifacts;
818 
819         // flush the calculated artifactMap
820         artifactMap = null;
821     }
822 
823     /**
824      * All dependencies that this project has, including transitive ones. Contents are lazily populated, so depending on
825      * what phases have run dependencies in some scopes won't be included. e.g. if only compile phase has run,
826      * dependencies with scope test won't be included.
827      *
828      * @return {@link Set} &lt; {@link Artifact} &gt;
829      * @see #getDependencyArtifacts() to get only direct dependencies
830      */
831     public Set<Artifact> getArtifacts() {
832         if (artifacts == null) {
833             if (artifactFilter == null || resolvedArtifacts == null) {
834                 artifacts = new LinkedHashSet<>();
835             } else {
836                 artifacts = new LinkedHashSet<>(resolvedArtifacts.size() * 2);
837                 for (Artifact artifact : resolvedArtifacts) {
838                     if (artifactFilter.include(artifact)) {
839                         artifacts.add(artifact);
840                     }
841                 }
842             }
843         }
844         return artifacts;
845     }
846 
847     public Map<String, Artifact> getArtifactMap() {
848         if (artifactMap == null) {
849             artifactMap = ArtifactUtils.artifactMapByVersionlessId(getArtifacts());
850         }
851         return artifactMap;
852     }
853 
854     public void setPluginArtifacts(Set<Artifact> pluginArtifacts) {
855         this.pluginArtifacts = pluginArtifacts;
856 
857         this.pluginArtifactMap = null;
858     }
859 
860     public Set<Artifact> getPluginArtifacts() {
861         return pluginArtifacts;
862     }
863 
864     public Map<String, Artifact> getPluginArtifactMap() {
865         if (pluginArtifactMap == null) {
866             pluginArtifactMap = ArtifactUtils.artifactMapByVersionlessId(getPluginArtifacts());
867         }
868 
869         return pluginArtifactMap;
870     }
871 
872     public void setParentArtifact(Artifact parentArtifact) {
873         this.parentArtifact = parentArtifact;
874     }
875 
876     public Artifact getParentArtifact() {
877         return parentArtifact;
878     }
879 
880     public List<Repository> getRepositories() {
881         return getModel().getRepositories();
882     }
883 
884     // ----------------------------------------------------------------------
885     // Plugins
886     // ----------------------------------------------------------------------
887 
888     public List<Plugin> getBuildPlugins() {
889         if (getModel().getBuild() == null) {
890             return Collections.emptyList();
891         }
892         return Collections.unmodifiableList(getModel().getBuild().getPlugins());
893     }
894 
895     public List<String> getModules() {
896         if (!getModel().getDelegate().getSubprojects().isEmpty()) {
897             return getModel().getDelegate().getSubprojects();
898         }
899         return getModel().getModules();
900     }
901 
902     public PluginManagement getPluginManagement() {
903         PluginManagement pluginMgmt = null;
904 
905         Build build = getModel().getBuild();
906         if (build != null) {
907             pluginMgmt = build.getPluginManagement();
908         }
909 
910         return pluginMgmt;
911     }
912 
913     private Build getModelBuild() {
914         Build build = getModel().getBuild();
915 
916         if (build == null) {
917             build = new Build();
918 
919             getModel().setBuild(build);
920         }
921 
922         return build;
923     }
924 
925     @Deprecated
926     public void setRemoteArtifactRepositories(List<ArtifactRepository> remoteArtifactRepositories) {
927         this.remoteArtifactRepositories = remoteArtifactRepositories;
928         this.remoteProjectRepositories = RepositoryUtils.toRepos(getRemoteArtifactRepositories());
929     }
930 
931     @Deprecated
932     public List<ArtifactRepository> getRemoteArtifactRepositories() {
933         if (remoteArtifactRepositories == null) {
934             remoteArtifactRepositories = new ArrayList<>();
935         }
936 
937         return remoteArtifactRepositories;
938     }
939 
940     @Deprecated
941     public void setPluginArtifactRepositories(List<ArtifactRepository> pluginArtifactRepositories) {
942         this.pluginArtifactRepositories = pluginArtifactRepositories;
943         this.remotePluginRepositories = RepositoryUtils.toRepos(getPluginArtifactRepositories());
944     }
945 
946     /**
947      * @return a list of ArtifactRepository objects constructed from the Repository objects returned by
948      *         getPluginRepositories.
949      */
950     @Deprecated
951     public List<ArtifactRepository> getPluginArtifactRepositories() {
952         if (pluginArtifactRepositories == null) {
953             pluginArtifactRepositories = new ArrayList<>();
954         }
955 
956         return pluginArtifactRepositories;
957     }
958 
959     @Deprecated
960     public ArtifactRepository getDistributionManagementArtifactRepository() {
961         return getArtifact().isSnapshot() && (getSnapshotArtifactRepository() != null)
962                 ? getSnapshotArtifactRepository()
963                 : getReleaseArtifactRepository();
964     }
965 
966     public List<Repository> getPluginRepositories() {
967         return getModel().getPluginRepositories();
968     }
969 
970     public List<RemoteRepository> getRemoteProjectRepositories() {
971         return remoteProjectRepositories;
972     }
973 
974     public List<RemoteRepository> getRemotePluginRepositories() {
975         return remotePluginRepositories;
976     }
977 
978     public void setActiveProfiles(List<Profile> activeProfiles) {
979         this.activeProfiles = activeProfiles;
980     }
981 
982     public List<Profile> getActiveProfiles() {
983         return activeProfiles;
984     }
985 
986     public void setInjectedProfileIds(String source, List<String> injectedProfileIds) {
987         if (injectedProfileIds != null) {
988             this.injectedProfileIds.put(source, new ArrayList<>(injectedProfileIds));
989         } else {
990             this.injectedProfileIds.remove(source);
991         }
992     }
993 
994     /**
995      * Gets the identifiers of all profiles that contributed to this project's effective model. This includes active
996      * profiles from the project's POM and all its parent POMs as well as from external sources like the
997      * {@code settings.xml}. The profile identifiers are grouped by the identifier of their source, e.g.
998      * {@code <groupId>:<artifactId>:<version>} for a POM profile or {@code external} for profiles from the
999      * {@code settings.xml}.
1000      *
1001      * @return The identifiers of all injected profiles, indexed by the source from which the profiles originated, never
1002      *         {@code null}.
1003      */
1004     public Map<String, List<String>> getInjectedProfileIds() {
1005         return this.injectedProfileIds;
1006     }
1007 
1008     /**
1009      * Add or replace an artifact. This method is now deprecated. Use the @{MavenProjectHelper} to attach artifacts to a
1010      * project. In spite of the 'throws' declaration on this API, this method has never thrown an exception since Maven
1011      * 3.0.x. Historically, it logged and ignored a second addition of the same g/a/v/c/t. Now it replaces the file for
1012      * the artifact, so that plugins (e.g. shade) can change the pathname of the file for a particular set of
1013      * coordinates.
1014      *
1015      * @param artifact the artifact to add or replace.
1016      * @deprecated Please use {@link MavenProjectHelper}
1017      * @throws DuplicateArtifactAttachmentException will never happen but leave it for backward compatibility
1018      */
1019     public void addAttachedArtifact(Artifact artifact) throws DuplicateArtifactAttachmentException {
1020         // if already there we remove it and add again
1021         int index = attachedArtifacts.indexOf(artifact);
1022         if (index >= 0) {
1023             LOGGER.warn("artifact '{}' already attached, replacing previous instance", artifact);
1024             attachedArtifacts.set(index, artifact);
1025         } else {
1026             attachedArtifacts.add(artifact);
1027         }
1028     }
1029 
1030     /**
1031      * Returns a read-only list of the attached artifacts to this project.
1032      *
1033      * @return the attached artifacts of this project
1034      */
1035     public List<Artifact> getAttachedArtifacts() {
1036         if (attachedArtifacts == null) {
1037             attachedArtifacts = new ArrayList<>();
1038         }
1039         return Collections.unmodifiableList(attachedArtifacts);
1040     }
1041 
1042     public Xpp3Dom getGoalConfiguration(
1043             String pluginGroupId, String pluginArtifactId, String executionId, String goalId) {
1044         Xpp3Dom dom = null;
1045 
1046         if (getBuildPlugins() != null) {
1047             for (Plugin plugin : getBuildPlugins()) {
1048                 if (pluginGroupId.equals(plugin.getGroupId()) && pluginArtifactId.equals(plugin.getArtifactId())) {
1049                     dom = (Xpp3Dom) plugin.getConfiguration();
1050 
1051                     if (executionId != null) {
1052                         for (PluginExecution execution : plugin.getExecutions()) {
1053                             if (executionId.equals(execution.getId())) {
1054                                 // NOTE: The PluginConfigurationExpander already merged the plugin-level config in
1055                                 dom = (Xpp3Dom) execution.getConfiguration();
1056                                 break;
1057                             }
1058                         }
1059                     }
1060                     break;
1061                 }
1062             }
1063         }
1064 
1065         if (dom != null) {
1066             // make a copy so the original in the POM doesn't get messed with
1067             dom = new Xpp3Dom(dom);
1068         }
1069 
1070         return dom;
1071     }
1072 
1073     public MavenProject getExecutionProject() {
1074         return (executionProject == null ? this : executionProject);
1075     }
1076 
1077     public void setExecutionProject(MavenProject executionProject) {
1078         this.executionProject = executionProject;
1079     }
1080 
1081     public List<MavenProject> getCollectedProjects() {
1082         return collectedProjects;
1083     }
1084 
1085     public void setCollectedProjects(List<MavenProject> collectedProjects) {
1086         this.collectedProjects = collectedProjects;
1087     }
1088 
1089     /**
1090      * Direct dependencies that this project has.
1091      *
1092      * @return {@link Set} &lt; {@link Artifact} &gt;
1093      * @see #getArtifacts() to get all transitive dependencies
1094      */
1095     @Deprecated
1096     public Set<Artifact> getDependencyArtifacts() {
1097         return dependencyArtifacts;
1098     }
1099 
1100     @Deprecated
1101     public void setDependencyArtifacts(Set<Artifact> dependencyArtifacts) {
1102         this.dependencyArtifacts = dependencyArtifacts;
1103     }
1104 
1105     @Deprecated
1106     public void setReleaseArtifactRepository(ArtifactRepository releaseArtifactRepository) {
1107         this.releaseArtifactRepository = releaseArtifactRepository;
1108     }
1109 
1110     @Deprecated
1111     public void setSnapshotArtifactRepository(ArtifactRepository snapshotArtifactRepository) {
1112         this.snapshotArtifactRepository = snapshotArtifactRepository;
1113     }
1114 
1115     public void setOriginalModel(Model originalModel) {
1116         this.originalModel = originalModel;
1117     }
1118 
1119     public Model getOriginalModel() {
1120         return originalModel;
1121     }
1122 
1123     public void setManagedVersionMap(Map<String, Artifact> map) {
1124         managedVersionMap = map;
1125     }
1126 
1127     public Map<String, Artifact> getManagedVersionMap() {
1128         return managedVersionMap;
1129     }
1130 
1131     @Override
1132     public boolean equals(Object other) {
1133         if (other == this) {
1134             return true;
1135         } else {
1136             if (other instanceof MavenProject that) {
1137                 return Objects.equals(getArtifactId(), that.getArtifactId())
1138                         && Objects.equals(getGroupId(), that.getGroupId())
1139                         && Objects.equals(getVersion(), that.getVersion());
1140             } else {
1141                 return false;
1142             }
1143         }
1144     }
1145 
1146     @Override
1147     public int hashCode() {
1148         return Objects.hash(getGroupId(), getArtifactId(), getVersion());
1149     }
1150 
1151     public List<Extension> getBuildExtensions() {
1152         Build build = getBuild();
1153         if ((build == null) || (build.getExtensions() == null)) {
1154             return Collections.emptyList();
1155         } else {
1156             return Collections.unmodifiableList(build.getExtensions());
1157         }
1158     }
1159 
1160     public void addProjectReference(MavenProject project) {
1161         projectReferences.put(
1162                 getProjectReferenceId(project.getGroupId(), project.getArtifactId(), project.getVersion()), project);
1163     }
1164 
1165     public Properties getProperties() {
1166         return getModel().getProperties();
1167     }
1168 
1169     public List<String> getFilters() {
1170         return getBuild().getFilters();
1171     }
1172 
1173     public Map<String, MavenProject> getProjectReferences() {
1174         return projectReferences;
1175     }
1176 
1177     public boolean isExecutionRoot() {
1178         return executionRoot;
1179     }
1180 
1181     public void setExecutionRoot(boolean executionRoot) {
1182         this.executionRoot = executionRoot;
1183     }
1184 
1185     public String getDefaultGoal() {
1186         return getBuild() != null ? getBuild().getDefaultGoal() : null;
1187     }
1188 
1189     public Plugin getPlugin(String pluginKey) {
1190         return getBuild().getPluginsAsMap().get(pluginKey);
1191     }
1192 
1193     /**
1194      * Default toString
1195      */
1196     @Override
1197     public String toString() {
1198         StringBuilder sb = new StringBuilder(128);
1199         sb.append("MavenProject: ");
1200         sb.append(getGroupId());
1201         sb.append(':');
1202         sb.append(getArtifactId());
1203         sb.append(':');
1204         sb.append(getVersion());
1205         if (getFile() != null) {
1206             sb.append(" @ ");
1207             sb.append(getFile().getPath());
1208         }
1209 
1210         return sb.toString();
1211     }
1212 
1213     /**
1214      * @since 2.0.9
1215      */
1216     @Override
1217     public MavenProject clone() {
1218         MavenProject clone;
1219         try {
1220             clone = (MavenProject) super.clone();
1221         } catch (CloneNotSupportedException e) {
1222             throw new UnsupportedOperationException(e);
1223         }
1224 
1225         clone.deepCopy(this);
1226 
1227         return clone;
1228     }
1229 
1230     public void setModel(Model model) {
1231         this.model = model;
1232     }
1233 
1234     protected void setAttachedArtifacts(List<Artifact> attachedArtifacts) {
1235         this.attachedArtifacts = attachedArtifacts;
1236     }
1237 
1238     /**
1239      * @deprecated Used only for the implementation of deprecated methods.
1240      */
1241     @Deprecated
1242     private void setSourceRootDirs(ProjectScope scope, Language language, List<String> roots) {
1243         sources.removeIf((source) -> scope.equals(source.scope()) && language.equals(source.language()));
1244         Path directory = getBaseDirectory();
1245         for (String root : roots) {
1246             addSourceRoot(new DefaultSourceRoot(scope, language, directory.resolve(root)));
1247         }
1248     }
1249 
1250     /**
1251      * @deprecated Replaced by {@link #addSourceRoot(ProjectScope, Language, String)}.
1252      */
1253     @Deprecated(since = "4.0.0")
1254     protected void setCompileSourceRoots(List<String> compileSourceRoots) {
1255         setSourceRootDirs(ProjectScope.MAIN, Language.JAVA_FAMILY, compileSourceRoots);
1256     }
1257 
1258     /**
1259      * @deprecated Replaced by {@link #addSourceRoot(ProjectScope, Language, String)}.
1260      */
1261     @Deprecated(since = "4.0.0")
1262     protected void setTestCompileSourceRoots(List<String> testCompileSourceRoots) {
1263         setSourceRootDirs(ProjectScope.TEST, Language.JAVA_FAMILY, testCompileSourceRoots);
1264     }
1265 
1266     protected ArtifactRepository getReleaseArtifactRepository() {
1267         return releaseArtifactRepository;
1268     }
1269 
1270     protected ArtifactRepository getSnapshotArtifactRepository() {
1271         return snapshotArtifactRepository;
1272     }
1273 
1274     private void deepCopy(MavenProject project) {
1275         // disown the parent
1276 
1277         // copy fields
1278         file = project.file;
1279         basedir = project.basedir;
1280 
1281         // don't need a deep copy, they don't get modified or added/removed to/from - but make them unmodifiable to be
1282         // sure!
1283         if (project.getDependencyArtifacts() != null) {
1284             setDependencyArtifacts(Collections.unmodifiableSet(project.getDependencyArtifacts()));
1285         }
1286 
1287         if (project.getArtifacts() != null) {
1288             setArtifacts(Collections.unmodifiableSet(project.getArtifacts()));
1289         }
1290 
1291         if (project.getParentFile() != null) {
1292             parentFile = new File(project.getParentFile().getAbsolutePath());
1293         }
1294 
1295         if (project.getPluginArtifacts() != null) {
1296             setPluginArtifacts(Collections.unmodifiableSet(project.getPluginArtifacts()));
1297         }
1298 
1299         if (project.getReportArtifacts() != null) {
1300             setReportArtifacts(Collections.unmodifiableSet(project.getReportArtifacts()));
1301         }
1302 
1303         if (project.getExtensionArtifacts() != null) {
1304             setExtensionArtifacts(Collections.unmodifiableSet(project.getExtensionArtifacts()));
1305         }
1306 
1307         setParentArtifact((project.getParentArtifact()));
1308 
1309         if (project.getRemoteArtifactRepositories() != null) {
1310             setRemoteArtifactRepositories(Collections.unmodifiableList(project.getRemoteArtifactRepositories()));
1311         }
1312 
1313         if (project.getPluginArtifactRepositories() != null) {
1314             setPluginArtifactRepositories(Collections.unmodifiableList(project.getPluginArtifactRepositories()));
1315         }
1316 
1317         if (project.getActiveProfiles() != null) {
1318             setActiveProfiles((Collections.unmodifiableList(project.getActiveProfiles())));
1319         }
1320 
1321         if (project.getAttachedArtifacts() != null) {
1322             // clone properties modifiable by plugins in a forked lifecycle
1323             setAttachedArtifacts(new ArrayList<>(project.getAttachedArtifacts()));
1324         }
1325 
1326         // This property is not handled like others as we don't use public API.
1327         // The whole implementation of this `deepCopy` method may need revision,
1328         // but it would be the topic for a separated commit.
1329         sources = new LinkedHashSet<>(project.sources);
1330 
1331         if (project.getModel() != null) {
1332             setModel(project.getModel().clone());
1333         }
1334 
1335         if (project.getOriginalModel() != null) {
1336             setOriginalModel(project.getOriginalModel());
1337         }
1338 
1339         setExecutionRoot(project.isExecutionRoot());
1340 
1341         if (project.getArtifact() != null) {
1342             setArtifact(ArtifactUtils.copyArtifact(project.getArtifact()));
1343         }
1344 
1345         if (project.getManagedVersionMap() != null) {
1346             setManagedVersionMap(project.getManagedVersionMap());
1347         }
1348 
1349         lifecyclePhases.addAll(project.lifecyclePhases);
1350     }
1351 
1352     private static String getProjectReferenceId(String groupId, String artifactId, String version) {
1353         StringBuilder buffer = new StringBuilder(128);
1354         buffer.append(groupId).append(':').append(artifactId).append(':').append(version);
1355         return buffer.toString();
1356     }
1357 
1358     /**
1359      * Sets the value of the context value of this project identified by the given key. If the supplied value is
1360      * <code>null</code>, the context value is removed from this project. Context values are intended to allow core
1361      * extensions to associate derived state with project instances.
1362      */
1363     public void setContextValue(String key, Object value) {
1364         if (context == null) {
1365             context = new HashMap<>();
1366         }
1367         if (value != null) {
1368             context.put(key, value);
1369         } else {
1370             context.remove(key);
1371         }
1372     }
1373 
1374     /**
1375      * Returns context value of this project associated with the given key or null if this project has no such value.
1376      */
1377     public Object getContextValue(String key) {
1378         if (context == null) {
1379             return null;
1380         }
1381         return context.get(key);
1382     }
1383 
1384     /**
1385      * Sets the project's class realm. <strong>Warning:</strong> This is an internal utility method that is only public
1386      * for technical reasons, it is not part of the public API. In particular, this method can be changed or deleted
1387      * without prior notice and must not be used by plugins.
1388      *
1389      * @param classRealm The class realm hosting the build extensions of this project, may be {@code null}.
1390      */
1391     public void setClassRealm(ClassRealm classRealm) {
1392         this.classRealm = classRealm;
1393     }
1394 
1395     /**
1396      * Gets the project's class realm. This class realm hosts the build extensions of the project.
1397      * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
1398      * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
1399      * used by plugins.
1400      *
1401      * @return The project's class realm or {@code null}.
1402      */
1403     public ClassRealm getClassRealm() {
1404         return classRealm;
1405     }
1406 
1407     /**
1408      * Sets the artifact filter used to exclude shared extension artifacts from plugin realms. <strong>Warning:</strong>
1409      * This is an internal utility method that is only public for technical reasons, it is not part of the public API.
1410      * In particular, this method can be changed or deleted without prior notice and must not be used by plugins.
1411      *
1412      * @param extensionDependencyFilter The dependency filter to apply to plugins, may be {@code null}.
1413      */
1414     public void setExtensionDependencyFilter(DependencyFilter extensionDependencyFilter) {
1415         this.extensionDependencyFilter = extensionDependencyFilter;
1416     }
1417 
1418     /**
1419      * Gets the dependency filter used to exclude shared extension artifacts from plugin realms.
1420      * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
1421      * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
1422      * used by plugins.
1423      *
1424      * @return The dependency filter or {@code null}.
1425      */
1426     public DependencyFilter getExtensionDependencyFilter() {
1427         return extensionDependencyFilter;
1428     }
1429 
1430     /**
1431      * Sets the transitive dependency artifacts that have been resolved/collected for this project.
1432      * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
1433      * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
1434      * used by plugins.
1435      *
1436      * @param artifacts The set of artifacts, may be {@code null}.
1437      */
1438     public void setResolvedArtifacts(Set<Artifact> artifacts) {
1439         this.resolvedArtifacts = (artifacts != null) ? artifacts : Collections.<Artifact>emptySet();
1440         this.artifacts = null;
1441         this.artifactMap = null;
1442     }
1443 
1444     /**
1445      * Sets the scope filter to select the artifacts being exposed to the currently executed mojo.
1446      * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
1447      * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
1448      * used by plugins.
1449      *
1450      * @param artifactFilter The artifact filter, may be {@code null} to exclude all artifacts.
1451      */
1452     public void setArtifactFilter(ArtifactFilter artifactFilter) {
1453         this.artifactFilter = artifactFilter;
1454         this.artifacts = null;
1455         this.artifactMap = null;
1456     }
1457 
1458     /**
1459      * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
1460      * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
1461      * used by plugins.
1462      *
1463      * @param phase The phase to check for, must not be {@code null}.
1464      * @return {@code true} if the phase has been seen.
1465      */
1466     public boolean hasLifecyclePhase(String phase) {
1467         return lifecyclePhases.contains(phase);
1468     }
1469 
1470     /**
1471      * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
1472      * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
1473      * used by plugins.
1474      *
1475      * @param lifecyclePhase The lifecycle phase to add, must not be {@code null}.
1476      */
1477     public void addLifecyclePhase(String lifecyclePhase) {
1478         lifecyclePhases.add(lifecyclePhase);
1479     }
1480 
1481     // ----------------------------------------------------------------------------------------------------------------
1482     //
1483     //
1484     // D E P R E C A T E D
1485     //
1486     //
1487     // ----------------------------------------------------------------------------------------------------------------
1488     //
1489     // Everything below will be removed for Maven 4.0.0
1490     //
1491     // ----------------------------------------------------------------------------------------------------------------
1492 
1493     private ProjectBuildingRequest projectBuilderConfiguration;
1494 
1495     private Map<String, String> moduleAdjustments;
1496 
1497     @Deprecated // This appears only to be used in test code
1498     public String getModulePathAdjustment(MavenProject moduleProject) throws IOException {
1499         // FIXME: This is hacky. What if module directory doesn't match artifactid, and parent
1500         // is coming from the repository??
1501         String module = moduleProject.getArtifactId();
1502 
1503         File moduleFile = moduleProject.getFile();
1504 
1505         if (moduleFile != null) {
1506             File moduleDir = moduleFile.getCanonicalFile().getParentFile();
1507 
1508             module = moduleDir.getName();
1509         }
1510 
1511         if (moduleAdjustments == null) {
1512             moduleAdjustments = new HashMap<>();
1513 
1514             List<String> modules = getModules();
1515             if (modules != null) {
1516                 for (String modulePath : modules) {
1517                     String moduleName = modulePath;
1518 
1519                     if (moduleName.endsWith("/") || moduleName.endsWith("\\")) {
1520                         moduleName = moduleName.substring(0, moduleName.length() - 1);
1521                     }
1522 
1523                     int lastSlash = moduleName.lastIndexOf('/');
1524 
1525                     if (lastSlash < 0) {
1526                         lastSlash = moduleName.lastIndexOf('\\');
1527                     }
1528 
1529                     String adjustment = null;
1530 
1531                     if (lastSlash > -1) {
1532                         moduleName = moduleName.substring(lastSlash + 1);
1533                         adjustment = modulePath.substring(0, lastSlash);
1534                     }
1535 
1536                     moduleAdjustments.put(moduleName, adjustment);
1537                 }
1538             }
1539         }
1540 
1541         return moduleAdjustments.get(module);
1542     }
1543 
1544     @Deprecated
1545     public Set<Artifact> createArtifacts(ArtifactFactory artifactFactory, String inheritedScope, ArtifactFilter filter)
1546             throws InvalidDependencyVersionException {
1547         return DefaultProjectArtifactFactory.createArtifacts(
1548                 artifactFactory, getModel().getDependencies(), inheritedScope, filter, this);
1549     }
1550 
1551     @Deprecated
1552     protected void setScriptSourceRoots(List<String> scriptSourceRoots) {
1553         setSourceRootDirs(ProjectScope.MAIN, Language.SCRIPT, scriptSourceRoots);
1554     }
1555 
1556     @Deprecated
1557     public void addScriptSourceRoot(String path) {
1558         addSourceRoot(ProjectScope.MAIN, Language.SCRIPT, path);
1559     }
1560 
1561     @Deprecated
1562     public List<String> getScriptSourceRoots() {
1563         return getSourceRootDirs(ProjectScope.MAIN, Language.SCRIPT);
1564     }
1565 
1566     @Deprecated
1567     public List<Artifact> getCompileArtifacts() {
1568         List<Artifact> list = new ArrayList<>(getArtifacts().size());
1569 
1570         for (Artifact a : getArtifacts()) {
1571             // TODO classpath check doesn't belong here - that's the other method
1572             if (a.getArtifactHandler().isAddedToClasspath()) {
1573                 // TODO let the scope handler deal with this
1574                 if (isCompilePathElement(a.getScope())) {
1575                     list.add(a);
1576                 }
1577             }
1578         }
1579         return list;
1580     }
1581 
1582     @Deprecated
1583     public List<Dependency> getCompileDependencies() {
1584         Set<Artifact> artifacts = getArtifacts();
1585 
1586         if ((artifacts == null) || artifacts.isEmpty()) {
1587             return Collections.emptyList();
1588         }
1589 
1590         List<Dependency> list = new ArrayList<>(artifacts.size());
1591 
1592         for (Artifact a : getArtifacts()) {
1593             // TODO let the scope handler deal with this
1594             if (isCompilePathElement(a.getScope())) {
1595                 Dependency dependency = new Dependency();
1596 
1597                 dependency.setArtifactId(a.getArtifactId());
1598                 dependency.setGroupId(a.getGroupId());
1599                 dependency.setVersion(a.getVersion());
1600                 dependency.setScope(a.getScope());
1601                 dependency.setType(a.getType());
1602                 dependency.setClassifier(a.getClassifier());
1603 
1604                 list.add(dependency);
1605             }
1606         }
1607         return Collections.unmodifiableList(list);
1608     }
1609 
1610     @Deprecated
1611     public List<Artifact> getTestArtifacts() {
1612         List<Artifact> list = new ArrayList<>(getArtifacts().size());
1613 
1614         for (Artifact a : getArtifacts()) {
1615             // TODO classpath check doesn't belong here - that's the other method
1616             if (a.getArtifactHandler().isAddedToClasspath()) {
1617                 list.add(a);
1618             }
1619         }
1620         return list;
1621     }
1622 
1623     @Deprecated
1624     public List<Dependency> getTestDependencies() {
1625         Set<Artifact> artifacts = getArtifacts();
1626 
1627         if ((artifacts == null) || artifacts.isEmpty()) {
1628             return Collections.emptyList();
1629         }
1630 
1631         List<Dependency> list = new ArrayList<>(artifacts.size());
1632 
1633         for (Artifact a : getArtifacts()) {
1634             Dependency dependency = new Dependency();
1635 
1636             dependency.setArtifactId(a.getArtifactId());
1637             dependency.setGroupId(a.getGroupId());
1638             dependency.setVersion(a.getVersion());
1639             dependency.setScope(a.getScope());
1640             dependency.setType(a.getType());
1641             dependency.setClassifier(a.getClassifier());
1642 
1643             list.add(dependency);
1644         }
1645         return Collections.unmodifiableList(list);
1646     }
1647 
1648     @Deprecated // used by the Maven ITs
1649     public List<Dependency> getRuntimeDependencies() {
1650         Set<Artifact> artifacts = getArtifacts();
1651 
1652         if ((artifacts == null) || artifacts.isEmpty()) {
1653             return Collections.emptyList();
1654         }
1655 
1656         List<Dependency> list = new ArrayList<>(artifacts.size());
1657 
1658         for (Artifact a : getArtifacts()) {
1659             // TODO let the scope handler deal with this
1660             if (isRuntimePathElement(a.getScope())) {
1661                 Dependency dependency = new Dependency();
1662 
1663                 dependency.setArtifactId(a.getArtifactId());
1664                 dependency.setGroupId(a.getGroupId());
1665                 dependency.setVersion(a.getVersion());
1666                 dependency.setScope(a.getScope());
1667                 dependency.setType(a.getType());
1668                 dependency.setClassifier(a.getClassifier());
1669 
1670                 list.add(dependency);
1671             }
1672         }
1673         return Collections.unmodifiableList(list);
1674     }
1675 
1676     @Deprecated
1677     public List<Artifact> getRuntimeArtifacts() {
1678         List<Artifact> list = new ArrayList<>(getArtifacts().size());
1679 
1680         for (Artifact a : getArtifacts()) {
1681             // TODO classpath check doesn't belong here - that's the other method
1682             if (a.getArtifactHandler().isAddedToClasspath() && isRuntimePathElement(a.getScope())) {
1683                 list.add(a);
1684             }
1685         }
1686         return list;
1687     }
1688 
1689     @Deprecated
1690     public List<String> getSystemClasspathElements() throws DependencyResolutionRequiredException {
1691         List<String> list = new ArrayList<>(getArtifacts().size());
1692 
1693         String d = getBuild().getOutputDirectory();
1694         if (d != null) {
1695             list.add(d);
1696         }
1697 
1698         for (Artifact a : getArtifacts()) {
1699             if (a.getArtifactHandler().isAddedToClasspath()) {
1700                 // TODO let the scope handler deal with this
1701                 if (Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
1702                     File f = a.getFile();
1703                     if (f != null) {
1704                         list.add(f.getPath());
1705                     }
1706                 }
1707             }
1708         }
1709         return list;
1710     }
1711 
1712     @Deprecated
1713     public List<Artifact> getSystemArtifacts() {
1714         List<Artifact> list = new ArrayList<>(getArtifacts().size());
1715 
1716         for (Artifact a : getArtifacts()) {
1717             // TODO classpath check doesn't belong here - that's the other method
1718             if (a.getArtifactHandler().isAddedToClasspath()) {
1719                 // TODO let the scope handler deal with this
1720                 if (Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
1721                     list.add(a);
1722                 }
1723             }
1724         }
1725         return list;
1726     }
1727 
1728     @Deprecated
1729     public List<Dependency> getSystemDependencies() {
1730         Set<Artifact> artifacts = getArtifacts();
1731 
1732         if ((artifacts == null) || artifacts.isEmpty()) {
1733             return Collections.emptyList();
1734         }
1735 
1736         List<Dependency> list = new ArrayList<>(artifacts.size());
1737 
1738         for (Artifact a : getArtifacts()) {
1739             // TODO let the scope handler deal with this
1740             if (Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
1741                 Dependency dependency = new Dependency();
1742 
1743                 dependency.setArtifactId(a.getArtifactId());
1744                 dependency.setGroupId(a.getGroupId());
1745                 dependency.setVersion(a.getVersion());
1746                 dependency.setScope(a.getScope());
1747                 dependency.setType(a.getType());
1748                 dependency.setClassifier(a.getClassifier());
1749 
1750                 list.add(dependency);
1751             }
1752         }
1753         return Collections.unmodifiableList(list);
1754     }
1755 
1756     @Deprecated
1757     public void setReporting(Reporting reporting) {
1758         getModel().setReporting(reporting);
1759     }
1760 
1761     @Deprecated
1762     public Reporting getReporting() {
1763         return getModel().getReporting();
1764     }
1765 
1766     @Deprecated
1767     public void setReportArtifacts(Set<Artifact> reportArtifacts) {
1768         this.reportArtifacts = reportArtifacts;
1769 
1770         reportArtifactMap = null;
1771     }
1772 
1773     @Deprecated
1774     public Set<Artifact> getReportArtifacts() {
1775         return reportArtifacts;
1776     }
1777 
1778     @Deprecated
1779     public Map<String, Artifact> getReportArtifactMap() {
1780         if (reportArtifactMap == null) {
1781             reportArtifactMap = ArtifactUtils.artifactMapByVersionlessId(getReportArtifacts());
1782         }
1783 
1784         return reportArtifactMap;
1785     }
1786 
1787     @Deprecated
1788     public void setExtensionArtifacts(Set<Artifact> extensionArtifacts) {
1789         this.extensionArtifacts = extensionArtifacts;
1790         extensionArtifactMap = null;
1791     }
1792 
1793     @Deprecated
1794     public Set<Artifact> getExtensionArtifacts() {
1795         return extensionArtifacts;
1796     }
1797 
1798     @Deprecated
1799     public Map<String, Artifact> getExtensionArtifactMap() {
1800         if (extensionArtifactMap == null) {
1801             extensionArtifactMap = ArtifactUtils.artifactMapByVersionlessId(getExtensionArtifacts());
1802         }
1803 
1804         return extensionArtifactMap;
1805     }
1806 
1807     @Deprecated
1808     public List<ReportPlugin> getReportPlugins() {
1809         if (getModel().getReporting() == null) {
1810             return Collections.emptyList();
1811         }
1812         return Collections.unmodifiableList(getModel().getReporting().getPlugins());
1813     }
1814 
1815     @Deprecated
1816     public Xpp3Dom getReportConfiguration(String pluginGroupId, String pluginArtifactId, String reportSetId) {
1817         Xpp3Dom dom = null;
1818 
1819         // ----------------------------------------------------------------------
1820         // I would like to be able to look up the Mojo object using a key but
1821         // we have a limitation in modello that will be remedied shortly. So
1822         // for now I have to iterate through and see what we have.
1823         // ----------------------------------------------------------------------
1824 
1825         if (getReportPlugins() != null) {
1826             for (ReportPlugin plugin : getReportPlugins()) {
1827                 if (pluginGroupId.equals(plugin.getGroupId()) && pluginArtifactId.equals(plugin.getArtifactId())) {
1828                     dom = (Xpp3Dom) plugin.getConfiguration();
1829 
1830                     if (reportSetId != null) {
1831                         ReportSet reportSet = plugin.getReportSetsAsMap().get(reportSetId);
1832                         if (reportSet != null) {
1833                             Xpp3Dom executionConfiguration = (Xpp3Dom) reportSet.getConfiguration();
1834                             if (executionConfiguration != null) {
1835                                 Xpp3Dom newDom = new Xpp3Dom(executionConfiguration);
1836                                 dom = Xpp3Dom.mergeXpp3Dom(newDom, dom);
1837                             }
1838                         }
1839                     }
1840                     break;
1841                 }
1842             }
1843         }
1844 
1845         if (dom != null) {
1846             // make a copy so the original in the POM doesn't get messed with
1847             dom = new Xpp3Dom(dom);
1848         }
1849 
1850         return dom;
1851     }
1852 
1853     /**
1854      * @deprecated Use MavenProjectHelper.attachArtifact(..) instead.
1855      */
1856     @Deprecated
1857     public void attachArtifact(String type, String classifier, File file) {}
1858 
1859     /**
1860      * @deprecated Use {@link org.apache.maven.model.io.ModelWriter}.
1861      */
1862     @Deprecated
1863     public void writeModel(Writer writer) throws IOException {
1864         MavenXpp3Writer pomWriter = new MavenXpp3Writer();
1865         pomWriter.write(writer, getModel());
1866     }
1867 
1868     /**
1869      * @deprecated Use {@link org.apache.maven.model.io.ModelWriter}.
1870      */
1871     @Deprecated
1872     public void writeOriginalModel(Writer writer) throws IOException {
1873         MavenXpp3Writer pomWriter = new MavenXpp3Writer();
1874         pomWriter.write(writer, getOriginalModel());
1875     }
1876 
1877     @Deprecated
1878     public Artifact replaceWithActiveArtifact(Artifact pluginArtifact) {
1879         return pluginArtifact;
1880     }
1881 
1882     /**
1883      * Gets the project building request from which this project instance was created. <strong>Warning:</strong> This is
1884      * a utility method that is meant to assist integrators of Maven, it must not be used by Maven plugins.
1885      *
1886      * @return The project building request or {@code null}.
1887      * @since 2.1
1888      */
1889     @Deprecated
1890     public ProjectBuildingRequest getProjectBuildingRequest() {
1891         return projectBuilderConfiguration;
1892     }
1893 
1894     /**
1895      * Sets the project building request from which this project instance was created. <strong>Warning:</strong> This is
1896      * a utility method that is meant to assist integrators of Maven, it must not be used by Maven plugins.
1897      *
1898      * @param projectBuildingRequest The project building request, may be {@code null}.
1899      * @since 2.1
1900      */
1901     // used by maven-dependency-tree
1902     @Deprecated
1903     public void setProjectBuildingRequest(ProjectBuildingRequest projectBuildingRequest) {
1904         this.projectBuilderConfiguration = projectBuildingRequest;
1905     }
1906 
1907     /**
1908      * @since 4.0.0
1909      * @return the rootDirectory for this project
1910      * @throws IllegalStateException if the rootDirectory cannot be found
1911      */
1912     public Path getRootDirectory() {
1913         if (rootDirectory == null) {
1914             throw new IllegalStateException(RootLocator.UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE);
1915         }
1916         return rootDirectory;
1917     }
1918 
1919     public void setRootDirectory(Path rootDirectory) {
1920         this.rootDirectory = rootDirectory;
1921     }
1922 }