001package org.apache.maven.project;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.RepositoryUtils;
023import org.apache.maven.artifact.Artifact;
024import org.apache.maven.artifact.ArtifactUtils;
025import org.apache.maven.artifact.DependencyResolutionRequiredException;
026import org.apache.maven.artifact.InvalidRepositoryException;
027import org.apache.maven.artifact.factory.ArtifactFactory;
028import org.apache.maven.artifact.repository.ArtifactRepository;
029import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
030import org.apache.maven.model.Build;
031import org.apache.maven.model.CiManagement;
032import org.apache.maven.model.Contributor;
033import org.apache.maven.model.Dependency;
034import org.apache.maven.model.DependencyManagement;
035import org.apache.maven.model.Developer;
036import org.apache.maven.model.DistributionManagement;
037import org.apache.maven.model.Extension;
038import org.apache.maven.model.IssueManagement;
039import org.apache.maven.model.License;
040import org.apache.maven.model.MailingList;
041import org.apache.maven.model.Model;
042import org.apache.maven.model.Organization;
043import org.apache.maven.model.Parent;
044import org.apache.maven.model.Plugin;
045import org.apache.maven.model.PluginExecution;
046import org.apache.maven.model.PluginManagement;
047import org.apache.maven.model.Prerequisites;
048import org.apache.maven.model.Profile;
049import org.apache.maven.model.ReportPlugin;
050import org.apache.maven.model.ReportSet;
051import org.apache.maven.model.Reporting;
052import org.apache.maven.model.Repository;
053import org.apache.maven.model.Resource;
054import org.apache.maven.model.Scm;
055import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
056import org.apache.maven.project.artifact.InvalidDependencyVersionException;
057import org.apache.maven.project.artifact.MavenMetadataSource;
058import org.apache.maven.repository.RepositorySystem;
059import org.codehaus.plexus.classworlds.realm.ClassRealm;
060import org.codehaus.plexus.logging.Logger;
061import org.codehaus.plexus.util.StringUtils;
062import org.codehaus.plexus.util.xml.Xpp3Dom;
063import org.eclipse.aether.graph.DependencyFilter;
064import org.eclipse.aether.repository.RemoteRepository;
065
066import java.io.File;
067import java.io.IOException;
068import java.io.Writer;
069import java.util.ArrayList;
070import java.util.Arrays;
071import java.util.Collections;
072import java.util.HashMap;
073import java.util.HashSet;
074import java.util.Iterator;
075import java.util.LinkedHashMap;
076import java.util.LinkedHashSet;
077import java.util.List;
078import java.util.Map;
079import java.util.Properties;
080import java.util.Set;
081
082/**
083 * The concern of the project is provide runtime values based on the model.
084 * <p/>
085 * The values in the model remain untouched but during the process of building a project notions
086 * like inheritance and interpolation can be added. This allows to have an entity which is useful in
087 * a runtime while preserving the model so that it can be marshalled and unmarshalled without being
088 * tainted by runtime requirements.
089 * <p/>
090 * We need to leave the model intact because we don't want the following:
091 * <ol>
092 * <li>We don't want interpolated values being written back into the model.
093 * <li>We don't want inherited values being written back into the model.
094 * </ol>
095 */
096public class MavenProject
097    implements Cloneable
098{
099    public static final String EMPTY_PROJECT_GROUP_ID = "unknown";
100
101    public static final String EMPTY_PROJECT_ARTIFACT_ID = "empty-project";
102
103    public static final String EMPTY_PROJECT_VERSION = "0";
104
105    private Model model;
106
107    private MavenProject parent;
108
109    private File file;
110
111    private Set<Artifact> resolvedArtifacts;
112
113    private ArtifactFilter artifactFilter;
114
115    private Set<Artifact> artifacts;
116
117    private Artifact parentArtifact;
118
119    private Set<Artifact> pluginArtifacts;
120
121    private List<ArtifactRepository> remoteArtifactRepositories;
122
123    private List<ArtifactRepository> pluginArtifactRepositories;
124
125    private List<RemoteRepository> remoteProjectRepositories;
126
127    private List<RemoteRepository> remotePluginRepositories;
128
129    private List<Artifact> attachedArtifacts;
130
131    private MavenProject executionProject;
132
133    private List<MavenProject> collectedProjects;
134
135    private List<String> compileSourceRoots = new ArrayList<String>();
136
137    private List<String> testCompileSourceRoots = new ArrayList<String>();
138
139    private List<String> scriptSourceRoots = new ArrayList<String>();
140
141    private ArtifactRepository releaseArtifactRepository;
142
143    private ArtifactRepository snapshotArtifactRepository;
144
145    private List<Profile> activeProfiles = new ArrayList<Profile>();
146
147    private Map<String, List<String>> injectedProfileIds = new LinkedHashMap<String, List<String>>();
148
149    private Set<Artifact> dependencyArtifacts;
150
151    private Artifact artifact;
152
153    // calculated.
154    private Map<String, Artifact> artifactMap;
155
156    private Model originalModel;
157
158    private Map<String, Artifact> pluginArtifactMap;
159
160    private Set<Artifact> reportArtifacts;
161
162    private Map<String, Artifact> reportArtifactMap;
163
164    private Set<Artifact> extensionArtifacts;
165
166    private Map<String, Artifact> extensionArtifactMap;
167
168    private Map<String, Artifact> managedVersionMap;
169
170    private Map<String, MavenProject> projectReferences = new HashMap<String, MavenProject>();
171
172    private boolean executionRoot;
173
174    private Map<String, String> moduleAdjustments;
175
176    private ProjectBuilder mavenProjectBuilder;
177
178    private ProjectBuildingRequest projectBuilderConfiguration;
179
180    private RepositorySystem repositorySystem;
181    
182    private File parentFile;
183
184    private Map<String, Object> context;
185
186    private ClassRealm classRealm;
187
188    private DependencyFilter extensionDependencyFilter;
189
190    private final Set<String> lifecyclePhases = Collections.synchronizedSet( new LinkedHashSet<String>() );
191
192    private Logger logger;
193
194    public MavenProject()
195    {
196        Model model = new Model();
197
198        model.setGroupId( EMPTY_PROJECT_GROUP_ID );
199        model.setArtifactId( EMPTY_PROJECT_ARTIFACT_ID );
200        model.setVersion( EMPTY_PROJECT_VERSION );
201
202        setModel( model );
203    }
204
205    public MavenProject( Model model )
206    {
207        setModel( model );
208    }
209
210    /**
211     * @deprecated use {@link #clone()} so subclasses can provide a copy of the same class
212     */
213    @Deprecated
214    public MavenProject( MavenProject project )
215    {
216        repositorySystem = project.repositorySystem;
217        logger = project.logger;
218        mavenProjectBuilder = project.mavenProjectBuilder;
219        projectBuilderConfiguration = project.projectBuilderConfiguration;
220        deepCopy( project );
221    }
222    
223    @Deprecated
224    public MavenProject( Model model, RepositorySystem repositorySystem )
225    {        
226        this.repositorySystem = repositorySystem;
227        setModel( model );
228    }
229
230    public File getParentFile()
231    {
232        return parentFile;
233    }
234
235    public void setParentFile( File parentFile )
236    {
237        this.parentFile = parentFile;
238    }
239
240    /**
241     * Constructor
242     * 
243     * @param repositorySystem - may not be null
244     * @param mavenProjectBuilder
245     * @param projectBuilderConfiguration
246     * @throws InvalidRepositoryException
247     */
248    MavenProject( RepositorySystem repositorySystem, ProjectBuilder mavenProjectBuilder,
249                  ProjectBuildingRequest projectBuilderConfiguration, Logger logger )
250    {
251        if ( repositorySystem == null )
252        {
253            throw new IllegalArgumentException( "mavenTools: null" );
254        }
255
256        this.mavenProjectBuilder = mavenProjectBuilder;
257        this.projectBuilderConfiguration = projectBuilderConfiguration;
258        this.repositorySystem = repositorySystem;
259        this.logger = logger;
260    }
261
262    @Deprecated
263    public Set<Artifact> createArtifacts( ArtifactFactory artifactFactory, String inheritedScope, ArtifactFilter filter )
264        throws InvalidDependencyVersionException
265    {
266        return MavenMetadataSource.createArtifacts( artifactFactory, getDependencies(), inheritedScope, filter, this );
267    }
268
269    // TODO: Find a way to use <relativePath/> here...it's tricky, because the moduleProject
270    // usually doesn't have a file associated with it yet.
271    public String getModulePathAdjustment( MavenProject moduleProject )
272        throws IOException
273    {
274        // FIXME: This is hacky. What if module directory doesn't match artifactid, and parent
275        // is coming from the repository??
276        String module = moduleProject.getArtifactId();
277
278        File moduleFile = moduleProject.getFile();
279
280        if ( moduleFile != null )
281        {
282            File moduleDir = moduleFile.getCanonicalFile().getParentFile();
283
284            module = moduleDir.getName();
285        }
286
287        if ( moduleAdjustments == null )
288        {
289            moduleAdjustments = new HashMap<String, String>();
290
291            List<String> modules = getModules();
292            if ( modules != null )
293            {
294                for ( String modulePath : modules )
295                {
296                    String moduleName = modulePath;
297
298                    if ( moduleName.endsWith( "/" ) || moduleName.endsWith( "\\" ) )
299                    {
300                        moduleName = moduleName.substring( 0, moduleName.length() - 1 );
301                    }
302
303                    int lastSlash = moduleName.lastIndexOf( '/' );
304
305                    if ( lastSlash < 0 )
306                    {
307                        lastSlash = moduleName.lastIndexOf( '\\' );
308                    }
309
310                    String adjustment = null;
311
312                    if ( lastSlash > -1 )
313                    {
314                        moduleName = moduleName.substring( lastSlash + 1 );
315                        adjustment = modulePath.substring( 0, lastSlash );
316                    }
317
318                    moduleAdjustments.put( moduleName, adjustment );
319                }
320            }
321        }
322
323        return moduleAdjustments.get( module );
324    }
325
326    // ----------------------------------------------------------------------
327    // Accessors
328    // ----------------------------------------------------------------------
329
330    public Artifact getArtifact()
331    {
332        return artifact;
333    }
334
335    public void setArtifact( Artifact artifact )
336    {
337        this.artifact = artifact;
338    }
339
340    //@todo I would like to get rid of this. jvz.
341    public Model getModel()
342    {
343        return model;
344    }
345
346    public MavenProject getParent()
347    {
348        if ( parent == null )
349        {
350            /*
351             * TODO: This is suboptimal. Without a cache in the project builder, rebuilding the parent chain currently
352             * causes O(n^2) parser invocations for an inheritance hierarchy of depth n.
353             */
354            if ( parentFile != null )
355            {
356                checkProjectBuildingRequest();
357                ProjectBuildingRequest request = new DefaultProjectBuildingRequest( projectBuilderConfiguration );
358                request.setRemoteRepositories( getRemoteArtifactRepositories() );
359
360                try
361                {
362                    parent = mavenProjectBuilder.build( parentFile, request ).getProject();
363                }
364                catch ( ProjectBuildingException e )
365                {
366                    throw new IllegalStateException( "Failed to build parent project for " + getId(), e );
367                }
368            }
369            else if ( model.getParent() != null )
370            {
371                checkProjectBuildingRequest();
372                ProjectBuildingRequest request = new DefaultProjectBuildingRequest( projectBuilderConfiguration );
373                request.setRemoteRepositories( getRemoteArtifactRepositories() );
374
375                try
376                {
377                    parent = mavenProjectBuilder.build( getParentArtifact(), request ).getProject();
378                }
379                catch ( ProjectBuildingException e )
380                {
381                    throw new IllegalStateException( "Failed to build parent project for " + getId(), e );
382                }
383            }
384        }
385        return parent;
386    }
387
388    public void setParent( MavenProject parent )
389    {
390        this.parent = parent;
391    }
392    
393    public boolean hasParent()
394    {
395        return getParent() != null;
396    }
397
398    public File getFile()
399    {
400        return file;
401    }
402
403    public void setFile( File file )
404    {
405        this.file = file;
406    }
407
408    public File getBasedir()
409    {
410        if ( getFile() != null )
411        {
412            return getFile().getParentFile();
413        }
414        else
415        {
416            // repository based POM
417            return null;
418        }
419    }
420
421    public void setDependencies( List<Dependency> dependencies )
422    {
423        getModel().setDependencies( dependencies );
424    }
425
426    public List<Dependency> getDependencies()
427    {
428        return getModel().getDependencies();
429    }
430
431    public DependencyManagement getDependencyManagement()
432    {
433        return getModel().getDependencyManagement();
434    }
435
436    // ----------------------------------------------------------------------
437    // Test and compile sourceroots.
438    // ----------------------------------------------------------------------
439
440    private void addPath( List<String> paths, String path )
441    {
442        if ( path != null )
443        {
444            path = path.trim();
445            if ( path.length() > 0 )
446            {
447                File file = new File( path );
448                if ( file.isAbsolute() )
449                {
450                    path = file.getAbsolutePath();
451                }
452                else
453                {
454                    path = new File( getBasedir(), path ).getAbsolutePath();
455                }
456
457                if ( !paths.contains( path ) )
458                {
459                    paths.add( path );
460                }
461            }
462        }
463    }
464
465    public void addCompileSourceRoot( String path )
466    {
467        addPath( getCompileSourceRoots(), path );
468    }
469
470    public void addScriptSourceRoot( String path )
471    {
472        if ( path != null )
473        {
474            path = path.trim();
475            if ( path.length() != 0 )
476            {
477                if ( !getScriptSourceRoots().contains( path ) )
478                {
479                    getScriptSourceRoots().add( path );
480                }
481            }
482        }
483    }
484
485    public void addTestCompileSourceRoot( String path )
486    {
487        addPath( getTestCompileSourceRoots(), path );
488    }
489
490    public List<String> getCompileSourceRoots()
491    {
492        return compileSourceRoots;
493    }
494
495    public List<String> getScriptSourceRoots()
496    {
497        return scriptSourceRoots;
498    }
499
500    public List<String> getTestCompileSourceRoots()
501    {
502        return testCompileSourceRoots;
503    }
504
505    public List<String> getCompileClasspathElements()
506        throws DependencyResolutionRequiredException
507    {
508        List<String> list = new ArrayList<String>( getArtifacts().size() + 1 );
509
510        String d = getBuild().getOutputDirectory();
511        if ( d != null )
512        {
513            list.add( d );
514        }
515
516        for ( Artifact a : getArtifacts() )
517        {                        
518            if ( a.getArtifactHandler().isAddedToClasspath() )
519            {
520                // TODO: let the scope handler deal with this
521                if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) || Artifact.SCOPE_PROVIDED.equals( a.getScope() ) || Artifact.SCOPE_SYSTEM.equals( a.getScope() ) )
522                {
523                    addArtifactPath( a, list );
524                }
525            }
526        }
527
528        return list;
529    }
530
531    @Deprecated
532    public List<Artifact> getCompileArtifacts()
533    {
534        List<Artifact> list = new ArrayList<Artifact>( getArtifacts().size() );
535
536        for ( Artifact a : getArtifacts() )
537        {
538            // TODO: classpath check doesn't belong here - that's the other method
539            if ( a.getArtifactHandler().isAddedToClasspath() )
540            {
541                // TODO: let the scope handler deal with this
542                if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) || Artifact.SCOPE_PROVIDED.equals( a.getScope() ) || Artifact.SCOPE_SYSTEM.equals( a.getScope() ) )
543                {
544                    list.add( a );
545                }
546            }
547        }
548        return list;
549    }
550
551    @Deprecated
552    public List<Dependency> getCompileDependencies()
553    {
554        Set<Artifact> artifacts = getArtifacts();
555
556        if ( ( artifacts == null ) || artifacts.isEmpty() )
557        {
558            return Collections.emptyList();
559        }
560
561        List<Dependency> list = new ArrayList<Dependency>( artifacts.size() );
562
563        for ( Artifact a : getArtifacts()  )
564        {
565            // TODO: let the scope handler deal with this
566            if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) || Artifact.SCOPE_PROVIDED.equals( a.getScope() ) || Artifact.SCOPE_SYSTEM.equals( a.getScope() ) )
567            {
568                Dependency dependency = new Dependency();
569
570                dependency.setArtifactId( a.getArtifactId() );
571                dependency.setGroupId( a.getGroupId() );
572                dependency.setVersion( a.getVersion() );
573                dependency.setScope( a.getScope() );
574                dependency.setType( a.getType() );
575                dependency.setClassifier( a.getClassifier() );
576
577                list.add( dependency );
578            }
579        }
580        return list;
581    }
582
583    //TODO: this checking for file == null happens because the resolver has been confused about the root
584    // artifact or not. things like the stupid dummy artifact coming from surefire.
585    public List<String> getTestClasspathElements()
586        throws DependencyResolutionRequiredException
587    {
588        List<String> list = new ArrayList<String>( getArtifacts().size() + 2 );
589
590        String d = getBuild().getTestOutputDirectory();
591        if ( d != null )
592        {
593            list.add( d );
594        }
595
596        d = getBuild().getOutputDirectory();
597        if ( d != null )
598        {
599            list.add( d );
600        }
601        
602        for ( Artifact a : getArtifacts() )
603        {            
604            if ( a.getArtifactHandler().isAddedToClasspath() )
605            {                
606                addArtifactPath( a, list );
607            }
608        }
609
610        return list;
611    }
612
613    @Deprecated
614    public List<Artifact> getTestArtifacts()
615    {
616        List<Artifact> list = new ArrayList<Artifact>( getArtifacts().size() );
617
618        for ( Artifact a : getArtifacts() )
619        {
620            // TODO: classpath check doesn't belong here - that's the other method
621            if ( a.getArtifactHandler().isAddedToClasspath() )
622            {
623                list.add( a );
624            }
625        }
626        return list;
627    }
628
629    @Deprecated
630    public List<Dependency> getTestDependencies()
631    {
632        Set<Artifact> artifacts = getArtifacts();
633
634        if ( ( artifacts == null ) || artifacts.isEmpty() )
635        {
636            return Collections.emptyList();
637        }
638
639        List<Dependency> list = new ArrayList<Dependency>( artifacts.size() );
640
641        for ( Artifact a : getArtifacts()  )
642        {
643            Dependency dependency = new Dependency();
644
645            dependency.setArtifactId( a.getArtifactId() );
646            dependency.setGroupId( a.getGroupId() );
647            dependency.setVersion( a.getVersion() );
648            dependency.setScope( a.getScope() );
649            dependency.setType( a.getType() );
650            dependency.setClassifier( a.getClassifier() );
651
652            list.add( dependency );
653        }
654        return list;
655    }
656
657    public List<String> getRuntimeClasspathElements()
658        throws DependencyResolutionRequiredException
659    {
660        List<String> list = new ArrayList<String>( getArtifacts().size() + 1 );
661
662        String d = getBuild().getOutputDirectory();
663        if ( d != null )
664        {
665            list.add( d );
666        }
667
668        for ( Artifact a : getArtifacts() )
669        {
670            if ( a.getArtifactHandler().isAddedToClasspath() )
671            {
672                // TODO: let the scope handler deal with this
673                if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) || Artifact.SCOPE_RUNTIME.equals( a.getScope() ) )
674                {
675                    addArtifactPath( a, list );
676                }
677            }
678        }
679        return list;
680    }
681
682    @Deprecated
683    public List<Artifact> getRuntimeArtifacts()
684    {
685        List<Artifact> list = new ArrayList<Artifact>( getArtifacts().size() );
686
687        for ( Artifact a : getArtifacts()  )
688        {
689            // TODO: classpath check doesn't belong here - that's the other method
690            if ( a.getArtifactHandler().isAddedToClasspath() )
691            {
692                // TODO: let the scope handler deal with this
693                if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) || Artifact.SCOPE_RUNTIME.equals( a.getScope() ) )
694                {
695                    list.add( a );
696                }
697            }
698        }
699        return list;
700    }
701
702    @Deprecated
703    public List<Dependency> getRuntimeDependencies()
704    {
705        Set<Artifact> artifacts = getArtifacts();
706
707        if ( ( artifacts == null ) || artifacts.isEmpty() )
708        {
709            return Collections.emptyList();
710        }
711
712        List<Dependency> list = new ArrayList<Dependency>( artifacts.size() );
713
714        for ( Artifact a : getArtifacts()  )
715        {
716            // TODO: let the scope handler deal with this
717            if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) || Artifact.SCOPE_RUNTIME.equals( a.getScope() ) )
718            {
719                Dependency dependency = new Dependency();
720
721                dependency.setArtifactId( a.getArtifactId() );
722                dependency.setGroupId( a.getGroupId() );
723                dependency.setVersion( a.getVersion() );
724                dependency.setScope( a.getScope() );
725                dependency.setType( a.getType() );
726                dependency.setClassifier( a.getClassifier() );
727
728                list.add( dependency );
729            }
730        }
731        return list;
732    }
733
734    public List<String> getSystemClasspathElements()
735        throws DependencyResolutionRequiredException
736    {
737        List<String> list = new ArrayList<String>( getArtifacts().size() );
738
739        String d = getBuild().getOutputDirectory();
740        if ( d != null )
741        {
742            list.add( d );
743        }
744
745        for ( Artifact a : getArtifacts() )
746        {
747            if ( a.getArtifactHandler().isAddedToClasspath() )
748            {
749                // TODO: let the scope handler deal with this
750                if ( Artifact.SCOPE_SYSTEM.equals( a.getScope() ) )
751                {
752                    addArtifactPath( a, list );
753                }
754            }
755        }
756        return list;
757    }
758
759    @Deprecated
760    public List<Artifact> getSystemArtifacts()
761    {
762        List<Artifact> list = new ArrayList<Artifact>( getArtifacts().size() );
763
764        for ( Artifact a : getArtifacts()  )
765        {
766            // TODO: classpath check doesn't belong here - that's the other method
767            if ( a.getArtifactHandler().isAddedToClasspath() )
768            {
769                // TODO: let the scope handler deal with this
770                if ( Artifact.SCOPE_SYSTEM.equals( a.getScope() ) )
771                {
772                    list.add( a );
773                }
774            }
775        }
776        return list;
777    }
778
779    @Deprecated
780    public List<Dependency> getSystemDependencies()
781    {
782        Set<Artifact> artifacts = getArtifacts();
783
784        if ( ( artifacts == null ) || artifacts.isEmpty() )
785        {
786            return Collections.emptyList();
787        }
788
789        List<Dependency> list = new ArrayList<Dependency>( artifacts.size() );
790
791        for ( Artifact a : getArtifacts()  )
792        {
793            // TODO: let the scope handler deal with this
794            if ( Artifact.SCOPE_SYSTEM.equals( a.getScope() ) )
795            {
796                Dependency dependency = new Dependency();
797
798                dependency.setArtifactId( a.getArtifactId() );
799                dependency.setGroupId( a.getGroupId() );
800                dependency.setVersion( a.getVersion() );
801                dependency.setScope( a.getScope() );
802                dependency.setType( a.getType() );
803                dependency.setClassifier( a.getClassifier() );
804
805                list.add( dependency );
806            }
807        }
808        return list;
809    }
810
811    // ----------------------------------------------------------------------
812    // Delegate to the model
813    // ----------------------------------------------------------------------
814
815    public void setModelVersion( String pomVersion )
816    {
817        getModel().setModelVersion( pomVersion );
818    }
819
820    public String getModelVersion()
821    {
822        return getModel().getModelVersion();
823    }
824
825    public String getId()
826    {
827        return getModel().getId();
828    }
829
830    public void setGroupId( String groupId )
831    {
832        getModel().setGroupId( groupId );
833    }
834
835    public String getGroupId()
836    {
837        String groupId = getModel().getGroupId();
838
839        if ( ( groupId == null ) && ( getModel().getParent() != null ) )
840        {
841            groupId = getModel().getParent().getGroupId();
842        }
843
844        return groupId;
845    }
846
847    public void setArtifactId( String artifactId )
848    {
849        getModel().setArtifactId( artifactId );
850    }
851
852    public String getArtifactId()
853    {
854        return getModel().getArtifactId();
855    }
856
857    public void setName( String name )
858    {
859        getModel().setName( name );
860    }
861
862    public String getName()
863    {
864        // TODO: this should not be allowed to be null.
865        if ( getModel().getName() != null )
866        {
867            return getModel().getName();
868        }
869        else
870        {
871            return getArtifactId();
872        }
873    }
874
875    public void setVersion( String version )
876    {
877        getModel().setVersion( version );
878    }
879
880    public String getVersion()
881    {
882        String version = getModel().getVersion();
883
884        if ( ( version == null ) && ( getModel().getParent() != null ) )
885        {
886            version = getModel().getParent().getVersion();
887        }
888
889        return version;
890    }
891
892    public String getPackaging()
893    {
894        return getModel().getPackaging();
895    }
896
897    public void setPackaging( String packaging )
898    {
899        getModel().setPackaging( packaging );
900    }
901
902    public void setInceptionYear( String inceptionYear )
903    {
904        getModel().setInceptionYear( inceptionYear );
905    }
906
907    public String getInceptionYear()
908    {
909        return getModel().getInceptionYear();
910    }
911
912    public void setUrl( String url )
913    {
914        getModel().setUrl( url );
915    }
916
917    public String getUrl()
918    {
919        return getModel().getUrl();
920    }
921
922    public Prerequisites getPrerequisites()
923    {
924        return getModel().getPrerequisites();
925    }
926
927    public void setIssueManagement( IssueManagement issueManagement )
928    {
929        getModel().setIssueManagement( issueManagement );
930    }
931
932    public CiManagement getCiManagement()
933    {
934        return getModel().getCiManagement();
935    }
936
937    public void setCiManagement( CiManagement ciManagement )
938    {
939        getModel().setCiManagement( ciManagement );
940    }
941
942    public IssueManagement getIssueManagement()
943    {
944        return getModel().getIssueManagement();
945    }
946
947    public void setDistributionManagement( DistributionManagement distributionManagement )
948    {
949        getModel().setDistributionManagement( distributionManagement );
950    }
951
952    public DistributionManagement getDistributionManagement()
953    {
954        return getModel().getDistributionManagement();
955    }
956
957    public void setDescription( String description )
958    {
959        getModel().setDescription( description );
960    }
961
962    public String getDescription()
963    {
964        return getModel().getDescription();
965    }
966
967    public void setOrganization( Organization organization )
968    {
969        getModel().setOrganization( organization );
970    }
971
972    public Organization getOrganization()
973    {
974        return getModel().getOrganization();
975    }
976
977    public void setScm( Scm scm )
978    {
979        getModel().setScm( scm );
980    }
981
982    public Scm getScm()
983    {
984        return getModel().getScm();
985    }
986
987    public void setMailingLists( List<MailingList> mailingLists )
988    {
989        getModel().setMailingLists( mailingLists );
990    }
991
992    public List<MailingList> getMailingLists()
993    {
994        return getModel().getMailingLists();
995    }
996
997    public void addMailingList( MailingList mailingList )
998    {
999        getModel().addMailingList( mailingList );
1000    }
1001
1002    public void setDevelopers( List<Developer> developers )
1003    {
1004        getModel().setDevelopers( developers );
1005    }
1006
1007    public List<Developer> getDevelopers()
1008    {
1009        return getModel().getDevelopers();
1010    }
1011
1012    public void addDeveloper( Developer developer )
1013    {
1014        getModel().addDeveloper( developer );
1015    }
1016
1017    public void setContributors( List<Contributor> contributors )
1018    {
1019        getModel().setContributors( contributors );
1020    }
1021
1022    public List<Contributor> getContributors()
1023    {
1024        return getModel().getContributors();
1025    }
1026
1027    public void addContributor( Contributor contributor )
1028    {
1029        getModel().addContributor( contributor );
1030    }
1031
1032    public void setBuild( Build build )
1033    {
1034        getModel().setBuild( build );
1035    }
1036
1037    public Build getBuild()
1038    {
1039        return getModelBuild();
1040    }
1041
1042    public List<Resource> getResources()
1043    {
1044        return getBuild().getResources();
1045    }
1046
1047    public List<Resource> getTestResources()
1048    {
1049        return getBuild().getTestResources();
1050    }
1051
1052    public void addResource( Resource resource )
1053    {
1054        getBuild().addResource( resource );
1055    }
1056
1057    public void addTestResource( Resource testResource )
1058    {
1059        getBuild().addTestResource( testResource );
1060    }
1061
1062    @Deprecated
1063    public void setReporting( Reporting reporting )
1064    {
1065        getModel().setReporting( reporting );
1066    }
1067
1068    @Deprecated
1069    public Reporting getReporting()
1070    {
1071        return getModel().getReporting();
1072    }
1073
1074    public void setLicenses( List<License> licenses )
1075    {
1076        getModel().setLicenses( licenses );
1077    }
1078
1079    public List<License> getLicenses()
1080    {
1081        return getModel().getLicenses();
1082    }
1083
1084    public void addLicense( License license )
1085    {
1086        getModel().addLicense( license );
1087    }
1088
1089    public void setArtifacts( Set<Artifact> artifacts )
1090    {
1091        this.artifacts = artifacts;
1092
1093        // flush the calculated artifactMap
1094        artifactMap = null;
1095    }
1096
1097    /**
1098     * All dependencies that this project has, including transitive ones. Contents are lazily
1099     * populated, so depending on what phases have run dependencies in some scopes won't be
1100     * included. eg. if only compile phase has run, dependencies with scope test won't be included.
1101     * 
1102     * @return {@link Set} &lt; {@link Artifact} >
1103     * @see #getDependencyArtifacts() to get only direct dependencies
1104     */
1105    public Set<Artifact> getArtifacts()
1106    {
1107        if ( artifacts == null )
1108        {
1109            if ( artifactFilter == null || resolvedArtifacts == null )
1110            {
1111                artifacts = new LinkedHashSet<Artifact>();
1112            }
1113            else
1114            {
1115                artifacts = new LinkedHashSet<Artifact>( resolvedArtifacts.size() * 2 );
1116                for ( Artifact artifact : resolvedArtifacts )
1117                {
1118                    if ( artifactFilter.include( artifact ) )
1119                    {
1120                        artifacts.add( artifact );
1121                    }
1122                }
1123            }
1124        }
1125        return artifacts;
1126    }
1127
1128    public Map<String, Artifact> getArtifactMap()
1129    {
1130        if ( artifactMap == null )
1131        {
1132            artifactMap = ArtifactUtils.artifactMapByVersionlessId( getArtifacts() );
1133        }
1134        return artifactMap;
1135    }
1136
1137    public void setPluginArtifacts( Set<Artifact> pluginArtifacts )
1138    {
1139        this.pluginArtifacts = pluginArtifacts;
1140
1141        this.pluginArtifactMap = null;
1142    }
1143
1144    public Set<Artifact> getPluginArtifacts()
1145    {
1146        if ( pluginArtifacts != null )
1147        {
1148            return pluginArtifacts;
1149        }
1150
1151        pluginArtifacts = new HashSet<Artifact>();
1152
1153        if ( repositorySystem != null )
1154        {
1155            for ( Plugin p : getBuildPlugins() )
1156            {
1157                Artifact artifact = repositorySystem.createPluginArtifact( p );
1158
1159                if ( artifact != null )
1160                {
1161                    pluginArtifacts.add( artifact );
1162                }
1163            }
1164        }
1165
1166        pluginArtifactMap = null;
1167
1168        return pluginArtifacts;
1169    }
1170
1171    public Map<String, Artifact> getPluginArtifactMap()
1172    {
1173        if ( pluginArtifactMap == null )
1174        {
1175            pluginArtifactMap = ArtifactUtils.artifactMapByVersionlessId( getPluginArtifacts() );
1176        }
1177
1178        return pluginArtifactMap;
1179    }
1180
1181    @Deprecated
1182    public void setReportArtifacts( Set<Artifact> reportArtifacts )
1183    {
1184        this.reportArtifacts = reportArtifacts;
1185
1186        reportArtifactMap = null;
1187    }
1188
1189    @Deprecated
1190    public Set<Artifact> getReportArtifacts()
1191    {
1192        if ( reportArtifacts != null )
1193        {
1194            return reportArtifacts;
1195        }
1196
1197        reportArtifacts = new HashSet<Artifact>();
1198
1199        if ( repositorySystem != null )
1200        {
1201            for ( ReportPlugin p : getReportPlugins() )
1202            {
1203                Plugin pp = new Plugin();
1204                pp.setGroupId( p.getGroupId() );
1205                pp.setArtifactId( p.getArtifactId() );
1206                pp.setVersion( p.getVersion() );
1207
1208                Artifact artifact = repositorySystem.createPluginArtifact( pp );
1209
1210                if ( artifact != null )
1211                {
1212                    reportArtifacts.add( artifact );
1213                }
1214            }
1215        }
1216
1217        reportArtifactMap = null;
1218
1219        return reportArtifacts;
1220    }
1221
1222    @Deprecated
1223    public Map<String, Artifact> getReportArtifactMap()
1224    {
1225        if ( reportArtifactMap == null )
1226        {
1227            reportArtifactMap = ArtifactUtils.artifactMapByVersionlessId( getReportArtifacts() );
1228        }
1229
1230        return reportArtifactMap;
1231    }
1232
1233    public void setExtensionArtifacts( Set<Artifact> extensionArtifacts )
1234    {
1235        this.extensionArtifacts = extensionArtifacts;
1236
1237        extensionArtifactMap = null;
1238    }
1239
1240    public Set<Artifact> getExtensionArtifacts()
1241    {
1242        if ( extensionArtifacts != null )
1243        {
1244            return extensionArtifacts;
1245        }
1246        extensionArtifacts = new HashSet<Artifact>();
1247        List<Extension> extensions = getBuildExtensions();
1248        if ( extensions != null )
1249        {
1250            for ( Extension ext : extensions )
1251            {
1252                String version;
1253                if ( StringUtils.isEmpty( ext.getVersion() ) )
1254                {
1255                    version = "RELEASE";
1256                }
1257                else
1258                {
1259                    version = ext.getVersion();
1260                }
1261
1262                Artifact artifact =
1263                    repositorySystem.createArtifact( ext.getGroupId(), ext.getArtifactId(), version, null, "jar" );
1264
1265                if ( artifact != null )
1266                {
1267                    extensionArtifacts.add( artifact );
1268                }
1269            }
1270        }
1271        extensionArtifactMap = null;
1272        return extensionArtifacts;
1273    }
1274
1275    public Map<String, Artifact> getExtensionArtifactMap()
1276    {
1277        if ( extensionArtifactMap == null )
1278        {
1279            extensionArtifactMap = ArtifactUtils.artifactMapByVersionlessId( getExtensionArtifacts() );
1280        }
1281
1282        return extensionArtifactMap;
1283    }
1284
1285    public void setParentArtifact( Artifact parentArtifact )
1286    {
1287        this.parentArtifact = parentArtifact;
1288    }
1289
1290    public Artifact getParentArtifact()
1291    {
1292        if ( parentArtifact == null && model.getParent() != null )
1293        {
1294            Parent p = model.getParent();
1295            parentArtifact = repositorySystem.createProjectArtifact( p.getGroupId(), p.getArtifactId(), p.getVersion() );
1296        }
1297        return parentArtifact;
1298    }
1299
1300    public List<Repository> getRepositories()
1301    {
1302        return getModel().getRepositories();
1303    }
1304
1305    // ----------------------------------------------------------------------
1306    // Plugins
1307    // ----------------------------------------------------------------------
1308
1309    @Deprecated
1310    public List<ReportPlugin> getReportPlugins()
1311    {
1312        if ( getModel().getReporting() == null )
1313        {
1314            return Collections.emptyList();
1315        }
1316        return getModel().getReporting().getPlugins();
1317
1318    }
1319
1320    public List<Plugin> getBuildPlugins()
1321    {
1322        if ( getModel().getBuild() == null )
1323        {
1324            return Collections.emptyList();
1325        }
1326        return getModel().getBuild().getPlugins();
1327    }
1328
1329    public List<String> getModules()
1330    {
1331        return getModel().getModules();
1332    }
1333
1334    public PluginManagement getPluginManagement()
1335    {
1336        PluginManagement pluginMgmt = null;
1337
1338        Build build = getModel().getBuild();
1339        if ( build != null )
1340        {
1341            pluginMgmt = build.getPluginManagement();
1342        }
1343
1344        return pluginMgmt;
1345    }
1346
1347    private Build getModelBuild()
1348    {
1349        Build build = getModel().getBuild();
1350
1351        if ( build == null )
1352        {
1353            build = new Build();
1354
1355            getModel().setBuild( build );
1356        }
1357
1358        return build;
1359    }
1360
1361    public void setRemoteArtifactRepositories( List<ArtifactRepository> remoteArtifactRepositories )
1362    {
1363        this.remoteArtifactRepositories = remoteArtifactRepositories;
1364        this.remoteProjectRepositories = RepositoryUtils.toRepos( getRemoteArtifactRepositories() );
1365    }
1366
1367    public List<ArtifactRepository> getRemoteArtifactRepositories()
1368    {
1369        if ( remoteArtifactRepositories == null )
1370        {
1371            remoteArtifactRepositories = new ArrayList<ArtifactRepository>();
1372        }
1373
1374        return remoteArtifactRepositories;
1375    }
1376
1377    public void setPluginArtifactRepositories( List<ArtifactRepository> pluginArtifactRepositories )
1378    {
1379        this.pluginArtifactRepositories = pluginArtifactRepositories;
1380        this.remotePluginRepositories = RepositoryUtils.toRepos( getPluginArtifactRepositories() );
1381    }
1382
1383    /**
1384     * @return a list of ArtifactRepository objects constructed from the Repository objects returned
1385     *         by getPluginRepositories.
1386     */
1387    public List<ArtifactRepository> getPluginArtifactRepositories()
1388    {
1389        if ( pluginArtifactRepositories == null )
1390        {
1391            pluginArtifactRepositories = new ArrayList<ArtifactRepository>();
1392        }
1393
1394        return pluginArtifactRepositories;
1395    }
1396
1397    public ArtifactRepository getDistributionManagementArtifactRepository()
1398    {
1399        return getArtifact().isSnapshot() && ( getSnapshotArtifactRepository() != null ) ? getSnapshotArtifactRepository() : getReleaseArtifactRepository();
1400    }
1401
1402    public List<Repository> getPluginRepositories()
1403    {
1404        return getModel().getPluginRepositories();
1405    }
1406
1407    public List<RemoteRepository> getRemoteProjectRepositories()
1408    {
1409        return remoteProjectRepositories;
1410    }
1411
1412    public List<RemoteRepository> getRemotePluginRepositories()
1413    {
1414        return remotePluginRepositories;
1415    }
1416
1417    public void setActiveProfiles( List<Profile> activeProfiles )
1418    {
1419        this.activeProfiles = activeProfiles;
1420    }
1421
1422    public List<Profile> getActiveProfiles()
1423    {
1424        return activeProfiles;
1425    }
1426
1427    public void setInjectedProfileIds( String source, List<String> injectedProfileIds )
1428    {
1429        if ( injectedProfileIds != null )
1430        {
1431            this.injectedProfileIds.put( source, new ArrayList<String>( injectedProfileIds ) );
1432        }
1433        else
1434        {
1435            this.injectedProfileIds.remove( source );
1436        }
1437    }
1438
1439    /**
1440     * Gets the identifiers of all profiles that contributed to this project's effective model. This includes active
1441     * profiles from the project's POM and all its parent POMs as well as from external sources like the {@code
1442     * settings.xml}. The profile identifiers are grouped by the identifier of their source, e.g. {@code
1443     * <groupId>:<artifactId>:<version>} for a POM profile or {@code external} for profiles from the {@code
1444     * settings.xml}.
1445     * 
1446     * @return The identifiers of all injected profiles, indexed by the source from which the profiles originated, never
1447     *         {@code null}.
1448     */
1449    public Map<String, List<String>> getInjectedProfileIds()
1450    {
1451        return this.injectedProfileIds;
1452    }
1453
1454    private String logStringForArtifactFile( Artifact a )
1455    {
1456        if ( a.getFile() != null )
1457        {
1458            return a.getFile().getAbsolutePath();
1459        }
1460        else
1461        {
1462            return "(no path)";
1463        }
1464    }
1465
1466    /**
1467     * Add or replace an artifact.
1468     * In spite of the 'throws' declaration on this API, this method has never thrown an exception since Maven 3.0.x.
1469     * Historically, it logged and ignored a second addition of the same g/a/v/c/t. Now it replaces the file for
1470     * the artifact, so that plugins (e.g. shade) can change the pathname of the file for a particular set of
1471     * coordinates.
1472     * @param artifact the artifact to add or replace.
1473     * @throws DuplicateArtifactAttachmentException
1474     */
1475    public void addAttachedArtifact( Artifact artifact )
1476        throws DuplicateArtifactAttachmentException
1477    {
1478        List<Artifact> attachedArtifacts = getAttachedArtifacts();
1479        for ( int ax = 0; ax < attachedArtifacts.size(); ax++ )
1480        {
1481            Artifact a = attachedArtifacts.get( ax );
1482            if ( a.equals( artifact ) )
1483            {
1484                if ( logger != null )
1485                {
1486                    logger.debug( String.format( "Replacing attached artifact %s. Old path %s, new path %s. ",
1487                                                 a,
1488                                                 logStringForArtifactFile( a ),
1489                                                 logStringForArtifactFile( artifact ) ) );
1490                }
1491                attachedArtifacts.set( ax, artifact );
1492                return;
1493            }
1494        }
1495
1496        getAttachedArtifacts().add( artifact );
1497    }
1498
1499    public List<Artifact> getAttachedArtifacts()
1500    {
1501        if ( attachedArtifacts == null )
1502        {
1503            attachedArtifacts = new ArrayList<Artifact>();
1504        }
1505        return attachedArtifacts;
1506    }
1507
1508    public Xpp3Dom getGoalConfiguration( String pluginGroupId, String pluginArtifactId, String executionId,
1509                                         String goalId )
1510    {
1511        Xpp3Dom dom = null;
1512
1513        if ( getBuildPlugins() != null )
1514        {
1515            for ( Plugin plugin : getBuildPlugins() )
1516            {
1517                if ( pluginGroupId.equals( plugin.getGroupId() ) && pluginArtifactId.equals( plugin.getArtifactId() ) )
1518                {
1519                    dom = (Xpp3Dom) plugin.getConfiguration();
1520
1521                    if ( executionId != null )
1522                    {
1523                        PluginExecution execution = plugin.getExecutionsAsMap().get( executionId );
1524                        if ( execution != null )
1525                        {
1526                            // NOTE: The PluginConfigurationExpander already merged the plugin-level config in
1527                            dom = (Xpp3Dom) execution.getConfiguration();
1528                        }
1529                    }
1530                    break;
1531                }
1532            }
1533        }
1534
1535        if ( dom != null )
1536        {
1537            // make a copy so the original in the POM doesn't get messed with
1538            dom = new Xpp3Dom( dom );
1539        }
1540
1541        return dom;
1542    }
1543
1544    @Deprecated
1545    public Xpp3Dom getReportConfiguration( String pluginGroupId, String pluginArtifactId, String reportSetId )
1546    {
1547        Xpp3Dom dom = null;
1548
1549        // ----------------------------------------------------------------------
1550        // I would like to be able to lookup the Mojo object using a key but
1551        // we have a limitation in modello that will be remedied shortly. So
1552        // for now I have to iterate through and see what we have.
1553        // ----------------------------------------------------------------------
1554
1555        if ( getReportPlugins() != null )
1556        {
1557            for ( ReportPlugin plugin : getReportPlugins() )
1558            {
1559                if ( pluginGroupId.equals( plugin.getGroupId() ) && pluginArtifactId.equals( plugin.getArtifactId() ) )
1560                {
1561                    dom = (Xpp3Dom) plugin.getConfiguration();
1562
1563                    if ( reportSetId != null )
1564                    {
1565                        ReportSet reportSet = plugin.getReportSetsAsMap().get( reportSetId );
1566                        if ( reportSet != null )
1567                        {
1568                            Xpp3Dom executionConfiguration = (Xpp3Dom) reportSet.getConfiguration();
1569                            if ( executionConfiguration != null )
1570                            {
1571                                Xpp3Dom newDom = new Xpp3Dom( executionConfiguration );
1572                                dom = Xpp3Dom.mergeXpp3Dom( newDom, dom );
1573                            }
1574                        }
1575                    }
1576                    break;
1577                }
1578            }
1579        }
1580
1581        if ( dom != null )
1582        {
1583            // make a copy so the original in the POM doesn't get messed with
1584            dom = new Xpp3Dom( dom );
1585        }
1586
1587        return dom;
1588    }
1589
1590    public MavenProject getExecutionProject()
1591    {
1592        return ( executionProject == null ? this : executionProject );
1593    }
1594
1595    public void setExecutionProject( MavenProject executionProject )
1596    {
1597        this.executionProject = executionProject;
1598    }
1599
1600    public List<MavenProject> getCollectedProjects()
1601    {
1602        return collectedProjects;
1603    }
1604
1605    public void setCollectedProjects( List<MavenProject> collectedProjects )
1606    {
1607        this.collectedProjects = collectedProjects;
1608    }
1609
1610    /**
1611     * Direct dependencies that this project has.
1612     * 
1613     * @return {@link Set} &lt; {@link Artifact} >
1614     * @see #getArtifacts() to get all transitive dependencies
1615     */
1616    public Set<Artifact> getDependencyArtifacts()
1617    {
1618        return dependencyArtifacts;
1619    }
1620
1621    public void setDependencyArtifacts( Set<Artifact> dependencyArtifacts )
1622    {
1623        this.dependencyArtifacts = dependencyArtifacts;
1624    }
1625
1626    public void setReleaseArtifactRepository( ArtifactRepository releaseArtifactRepository )
1627    {
1628        this.releaseArtifactRepository = releaseArtifactRepository;
1629    }
1630
1631    public void setSnapshotArtifactRepository( ArtifactRepository snapshotArtifactRepository )
1632    {
1633        this.snapshotArtifactRepository = snapshotArtifactRepository;
1634    }
1635
1636    public void setOriginalModel( Model originalModel )
1637    {
1638        this.originalModel = originalModel;
1639    }
1640
1641    public Model getOriginalModel()
1642    {
1643        return originalModel;
1644    }
1645
1646    public void setManagedVersionMap( Map<String, Artifact> map )
1647    {
1648        managedVersionMap = map;
1649    }
1650
1651    public Map<String, Artifact> getManagedVersionMap()
1652    {
1653        if ( managedVersionMap != null )
1654        {
1655            return managedVersionMap;
1656        }
1657
1658        Map<String, Artifact> map = null;
1659        if ( repositorySystem != null )
1660        {
1661
1662            List<Dependency> deps;
1663            DependencyManagement dependencyManagement = getDependencyManagement();
1664            if ( ( dependencyManagement != null ) && ( ( deps = dependencyManagement.getDependencies() ) != null ) && ( deps.size() > 0 ) )
1665            {
1666                map = new HashMap<String, Artifact>();
1667                for ( Dependency d : dependencyManagement.getDependencies() )
1668                {
1669                    Artifact artifact = repositorySystem.createDependencyArtifact( d );
1670
1671                    if ( artifact == null )
1672                    {
1673                        map = Collections.emptyMap();
1674                    }
1675
1676                    map.put( d.getManagementKey(), artifact );
1677                }
1678            }
1679            else
1680            {
1681                map = Collections.emptyMap();
1682            }
1683        }
1684        managedVersionMap = map;
1685        return managedVersionMap;
1686    }
1687
1688    @Override
1689    public boolean equals( Object other )
1690    {
1691        if ( other == this )
1692        {
1693            return true;
1694        }
1695        else if ( !( other instanceof MavenProject ) )
1696        {
1697            return false;
1698        }
1699
1700        MavenProject that = (MavenProject) other;
1701
1702        return eq( getArtifactId(), that.getArtifactId() )
1703            && eq( getGroupId(), that.getGroupId() )
1704            && eq( getVersion(), that.getVersion() );
1705    }
1706
1707    private static <T> boolean eq( T s1, T s2 )
1708    {
1709        return ( s1 != null ) ? s1.equals( s2 ) : s2 == null;
1710    }
1711
1712    @Override
1713    public int hashCode()
1714    {
1715        int hash = 17;
1716        hash = 31 * hash + getGroupId().hashCode();
1717        hash = 31 * hash + getArtifactId().hashCode();
1718        hash = 31 * hash + getVersion().hashCode();
1719        return hash;
1720    }
1721
1722    public List<Extension> getBuildExtensions()
1723    {
1724        Build build = getBuild();
1725        if ( ( build == null ) || ( build.getExtensions() == null ) )
1726        {
1727            return Collections.emptyList();
1728        }
1729        else
1730        {
1731            return build.getExtensions();
1732        }
1733    }
1734
1735    public void addProjectReference( MavenProject project )
1736    {
1737        projectReferences.put( getProjectReferenceId( project.getGroupId(), project.getArtifactId(), project.getVersion() ), project );
1738    }
1739
1740    /**
1741     * @deprecated Use MavenProjectHelper.attachArtifact(..) instead.
1742     */
1743    @Deprecated
1744    public void attachArtifact( String type, String classifier, File file )
1745    {
1746    }
1747
1748    public Properties getProperties()
1749    {
1750        return getModel().getProperties();
1751    }
1752
1753    public List<String> getFilters()
1754    {
1755        return getBuild().getFilters();
1756    }
1757
1758    public Map<String, MavenProject> getProjectReferences()
1759    {
1760        return projectReferences;
1761    }
1762
1763    public boolean isExecutionRoot()
1764    {
1765        return executionRoot;
1766    }
1767
1768    public void setExecutionRoot( boolean executionRoot )
1769    {
1770        this.executionRoot = executionRoot;
1771    }
1772
1773    public String getDefaultGoal()
1774    {
1775        return getBuild() != null ? getBuild().getDefaultGoal() : null;
1776    }
1777
1778    public Plugin getPlugin( String pluginKey )
1779    {
1780        return getBuild().getPluginsAsMap().get( pluginKey );
1781    }
1782
1783    /**
1784     * Default toString
1785     */
1786    @Override
1787    public String toString()
1788    {
1789        StringBuilder sb = new StringBuilder( 128 );
1790        sb.append( "MavenProject: " );
1791        sb.append( getGroupId() );
1792        sb.append( ":" );
1793        sb.append( getArtifactId() );
1794        sb.append( ":" );
1795        sb.append( getVersion() );
1796        sb.append( " @ " );
1797
1798        try
1799        {
1800            sb.append( getFile().getPath() );
1801        }
1802        catch ( NullPointerException e )
1803        {
1804            //don't log it.
1805        }
1806
1807        return sb.toString();
1808    }
1809
1810    /**
1811     * @deprecated Use {@link org.apache.maven.model.io.ModelWriter}.
1812     */
1813    @Deprecated
1814    public void writeModel( Writer writer )
1815        throws IOException
1816    {
1817        MavenXpp3Writer pomWriter = new MavenXpp3Writer();
1818        pomWriter.write( writer, getModel() );
1819    }
1820
1821    /**
1822     * @deprecated Use {@link org.apache.maven.model.io.ModelWriter}.
1823     */
1824    @Deprecated
1825    public void writeOriginalModel( Writer writer )
1826        throws IOException
1827    {
1828        MavenXpp3Writer pomWriter = new MavenXpp3Writer();
1829        pomWriter.write( writer, getOriginalModel() );
1830    }
1831
1832    /**
1833     * @throws CloneNotSupportedException
1834     * @since 2.0.9
1835     */
1836    @Override
1837    public MavenProject clone()
1838    {
1839        MavenProject clone;
1840        try
1841        {
1842            clone = (MavenProject) super.clone();
1843        }
1844        catch ( CloneNotSupportedException e )
1845        {
1846            throw new UnsupportedOperationException( e );
1847        }
1848
1849        clone.deepCopy( this );
1850
1851        return clone;
1852    }
1853
1854    protected void setModel( Model model )
1855    {
1856        this.model = model;
1857    }
1858
1859    protected void setAttachedArtifacts( List<Artifact> attachedArtifacts )
1860    {
1861        this.attachedArtifacts = attachedArtifacts;
1862    }
1863
1864    protected void setCompileSourceRoots( List<String> compileSourceRoots )
1865    {
1866        this.compileSourceRoots = compileSourceRoots;
1867    }
1868
1869    protected void setTestCompileSourceRoots( List<String> testCompileSourceRoots )
1870    {
1871        this.testCompileSourceRoots = testCompileSourceRoots;
1872    }
1873
1874    protected void setScriptSourceRoots( List<String> scriptSourceRoots )
1875    {
1876        this.scriptSourceRoots = scriptSourceRoots;
1877    }
1878
1879    protected ArtifactRepository getReleaseArtifactRepository()
1880    {
1881        if ( releaseArtifactRepository == null )
1882        {
1883            if ( getDistributionManagement() != null && getDistributionManagement().getRepository() != null )
1884            {
1885                checkProjectBuildingRequest();
1886                try
1887                {
1888                    ArtifactRepository repo =
1889                        repositorySystem.buildArtifactRepository( getDistributionManagement().getRepository() );
1890                    repositorySystem.injectProxy( projectBuilderConfiguration.getRepositorySession(),
1891                                                  Arrays.asList( repo ) );
1892                    repositorySystem.injectAuthentication( projectBuilderConfiguration.getRepositorySession(),
1893                                                           Arrays.asList( repo ) );
1894                    setReleaseArtifactRepository( repo );
1895                }
1896                catch ( InvalidRepositoryException e )
1897                {
1898                    throw new IllegalStateException( "Failed to create release distribution repository for " + getId(),
1899                                                     e );
1900                }
1901            }
1902        }
1903
1904        return releaseArtifactRepository;
1905    }
1906
1907    protected ArtifactRepository getSnapshotArtifactRepository()
1908    {
1909        if ( snapshotArtifactRepository == null )
1910        {
1911            if ( getDistributionManagement() != null && getDistributionManagement().getSnapshotRepository() != null )
1912            {
1913                checkProjectBuildingRequest();
1914                try
1915                {
1916                    ArtifactRepository repo =
1917                        repositorySystem.buildArtifactRepository( getDistributionManagement().getSnapshotRepository() );
1918                    repositorySystem.injectProxy( projectBuilderConfiguration.getRepositorySession(),
1919                                                  Arrays.asList( repo ) );
1920                    repositorySystem.injectAuthentication( projectBuilderConfiguration.getRepositorySession(),
1921                                                           Arrays.asList( repo ) );
1922                    setSnapshotArtifactRepository( repo );
1923                }
1924                catch ( InvalidRepositoryException e )
1925                {
1926                    throw new IllegalStateException(
1927                                                     "Failed to create snapshot distribution repository for " + getId(),
1928                                                     e );
1929                }
1930            }
1931        }
1932
1933        return snapshotArtifactRepository;
1934    }
1935
1936    @Deprecated
1937    public Artifact replaceWithActiveArtifact( Artifact pluginArtifact )
1938    {
1939        return pluginArtifact;
1940    }
1941
1942    private void deepCopy( MavenProject project )
1943    {
1944        // disown the parent
1945
1946        // copy fields
1947        setFile( project.getFile() );
1948
1949        // don't need a deep copy, they don't get modified or added/removed to/from - but make them unmodifiable to be
1950        // sure!
1951        if ( project.getDependencyArtifacts() != null )
1952        {
1953            setDependencyArtifacts( Collections.unmodifiableSet( project.getDependencyArtifacts() ) );
1954        }
1955
1956        if ( project.getArtifacts() != null )
1957        {
1958            setArtifacts( Collections.unmodifiableSet( project.getArtifacts() ) );
1959        }
1960
1961        if ( project.getParentFile() != null )
1962        {
1963            parentFile = new File( project.getParentFile().getAbsolutePath() );
1964        }
1965
1966        if ( project.getPluginArtifacts() != null )
1967        {
1968            setPluginArtifacts( Collections.unmodifiableSet( project.getPluginArtifacts() ) );
1969        }
1970
1971        if ( project.getReportArtifacts() != null )
1972        {
1973            setReportArtifacts( Collections.unmodifiableSet( project.getReportArtifacts() ) );
1974        }
1975
1976        if ( project.getExtensionArtifacts() != null )
1977        {
1978            setExtensionArtifacts( Collections.unmodifiableSet( project.getExtensionArtifacts() ) );
1979        }
1980
1981        setParentArtifact( ( project.getParentArtifact() ) );
1982
1983        if ( project.getRemoteArtifactRepositories() != null )
1984        {
1985            setRemoteArtifactRepositories( Collections.unmodifiableList( project.getRemoteArtifactRepositories() ) );
1986        }
1987
1988        if ( project.getPluginArtifactRepositories() != null )
1989        {
1990            setPluginArtifactRepositories( ( Collections.unmodifiableList( project.getPluginArtifactRepositories() ) ) );
1991        }
1992
1993        if ( project.getActiveProfiles() != null )
1994        {
1995            setActiveProfiles( ( Collections.unmodifiableList( project.getActiveProfiles() ) ) );
1996        }
1997
1998        if ( project.getAttachedArtifacts() != null )
1999        {
2000            // clone properties modifyable by plugins in a forked lifecycle
2001            setAttachedArtifacts( new ArrayList<Artifact>( project.getAttachedArtifacts() ) );
2002        }
2003
2004        if ( project.getCompileSourceRoots() != null )
2005        {
2006            // clone source roots
2007            setCompileSourceRoots( ( new ArrayList<String>( project.getCompileSourceRoots() ) ) );
2008        }
2009
2010        if ( project.getTestCompileSourceRoots() != null )
2011        {
2012            setTestCompileSourceRoots( ( new ArrayList<String>( project.getTestCompileSourceRoots() ) ) );
2013        }
2014
2015        if ( project.getScriptSourceRoots() != null )
2016        {
2017            setScriptSourceRoots( ( new ArrayList<String>( project.getScriptSourceRoots() ) ) );
2018        }
2019
2020        if ( project.getModel() != null )
2021        {
2022            setModel( project.getModel().clone() );
2023        }
2024
2025        if ( project.getOriginalModel() != null )
2026        {
2027            setOriginalModel( project.getOriginalModel() );
2028        }
2029
2030        setExecutionRoot( project.isExecutionRoot() );
2031
2032        if ( project.getArtifact() != null )
2033        {
2034            setArtifact( ArtifactUtils.copyArtifact( project.getArtifact() ) );
2035        }
2036
2037        if ( project.getManagedVersionMap() != null )
2038        {
2039            setManagedVersionMap( new HashMap<String, Artifact>( project.getManagedVersionMap() ) );
2040        }
2041
2042        lifecyclePhases.addAll( project.lifecyclePhases );
2043    }
2044
2045    private void addArtifactPath( Artifact artifact, List<String> classpath )
2046    {
2047        File file = artifact.getFile();
2048        if ( file != null )
2049        {
2050            classpath.add( file.getPath() );
2051        }
2052    }
2053
2054    private static String getProjectReferenceId( String groupId, String artifactId, String version )
2055    {
2056        StringBuilder buffer = new StringBuilder( 128 );
2057        buffer.append( groupId ).append( ':' ).append( artifactId ).append( ':' ).append( version );
2058        return buffer.toString();
2059    }
2060
2061    /**
2062     * Sets the value of the context value of this project identified
2063     * by the given key. If the supplied value is <code>null</code>,
2064     * the context value is removed from this project.
2065     * 
2066     * Context values are intended to allow core extensions to associate
2067     * derived state with project instances. 
2068     */
2069    public void setContextValue( String key, Object value )
2070    {
2071        if ( context == null )
2072        {
2073            context = new HashMap<String, Object>();
2074        }
2075        if ( value != null )
2076        {
2077            context.put( key, value );
2078        }
2079        else
2080        {
2081            context.remove( key );
2082        }
2083    }
2084
2085    /**
2086     * Returns context value of this project associated with the given key 
2087     * or null if this project has no such value. 
2088     */
2089    public Object getContextValue( String key )
2090    {
2091        if ( context == null )
2092        {
2093            return null;
2094        }
2095        return context.get( key );
2096    }
2097
2098    /**
2099     * Sets the project's class realm. <strong>Warning:</strong> This is an internal utility method that is only public
2100     * for technical reasons, it is not part of the public API. In particular, this method can be changed or deleted
2101     * without prior notice and must not be used by plugins.
2102     * 
2103     * @param classRealm The class realm hosting the build extensions of this project, may be {@code null}.
2104     */
2105    public void setClassRealm( ClassRealm classRealm )
2106    {
2107        this.classRealm = classRealm;
2108    }
2109
2110    /**
2111     * Gets the project's class realm. This class realm hosts the build extensions of the project.
2112     * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
2113     * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
2114     * used by plugins.
2115     * 
2116     * @return The project's class realm or {@code null}.
2117     */
2118    public ClassRealm getClassRealm()
2119    {
2120        return classRealm;
2121    }
2122
2123    /**
2124     * Sets the artifact filter used to exclude shared extension artifacts from plugin realms. <strong>Warning:</strong>
2125     * This is an internal utility method that is only public for technical reasons, it is not part of the public API.
2126     * In particular, this method can be changed or deleted without prior notice and must not be used by plugins.
2127     * 
2128     * @param extensionDependencyFilter The dependency filter to apply to plugins, may be {@code null}.
2129     */
2130    public void setExtensionDependencyFilter( DependencyFilter extensionDependencyFilter )
2131    {
2132        this.extensionDependencyFilter = extensionDependencyFilter;
2133    }
2134
2135    /**
2136     * Gets the dependency filter used to exclude shared extension artifacts from plugin realms.
2137     * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
2138     * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
2139     * used by plugins.
2140     * 
2141     * @return The dependency filter or {@code null}.
2142     */
2143    public DependencyFilter getExtensionDependencyFilter()
2144    {
2145        return extensionDependencyFilter;
2146    }
2147
2148    /**
2149     * Sets the transitive dependency artifacts that have been resolved/collected for this project.
2150     * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
2151     * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
2152     * used by plugins.
2153     * 
2154     * @param artifacts The set of artifacts, may be {@code null}.
2155     */
2156    public void setResolvedArtifacts( Set<Artifact> artifacts )
2157    {
2158        this.resolvedArtifacts = ( artifacts != null ) ? artifacts : Collections.<Artifact> emptySet();
2159        this.artifacts = null;
2160        this.artifactMap = null;
2161    }
2162
2163    /**
2164     * Sets the scope filter to select the artifacts being exposed to the currently executed mojo.
2165     * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
2166     * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
2167     * used by plugins.
2168     * 
2169     * @param artifactFilter The artifact filter, may be {@code null} to exclude all artifacts.
2170     */
2171    public void setArtifactFilter( ArtifactFilter artifactFilter )
2172    {
2173        this.artifactFilter = artifactFilter;
2174        this.artifacts = null;
2175        this.artifactMap = null;
2176    }
2177
2178    /**
2179     * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
2180     * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
2181     * used by plugins.
2182     * 
2183     * @param phase The phase to check for, must not be {@code null}.
2184     * @return {@code true} if the phase has been seen.
2185     */
2186    public boolean hasLifecyclePhase( String phase )
2187    {
2188        return lifecyclePhases.contains( phase );
2189    }
2190
2191    /**
2192     * <strong>Warning:</strong> This is an internal utility method that is only public for technical reasons, it is not
2193     * part of the public API. In particular, this method can be changed or deleted without prior notice and must not be
2194     * used by plugins.
2195     * 
2196     * @param lifecyclePhase The lifecycle phase to add, must not be {@code null}.
2197     */
2198    public void addLifecyclePhase( String lifecyclePhase )
2199    {
2200        lifecyclePhases.add( lifecyclePhase );
2201    }
2202
2203    /**
2204     * Gets the project building request from which this project instance was created. <strong>Warning:</strong> This is
2205     * an utility method that is meant to assist integrators of Maven, it must not be used by Maven plugins.
2206     * 
2207     * @return The project building request or {@code null}.
2208     * @since 2.1
2209     */
2210    public ProjectBuildingRequest getProjectBuildingRequest()
2211    {
2212        return projectBuilderConfiguration;
2213    }
2214
2215    /**
2216     * Sets the project building request from which this project instance was created. <strong>Warning:</strong> This is
2217     * an utility method that is meant to assist integrators of Maven, it must not be used by Maven plugins.
2218     * 
2219     * @param projectBuildingRequest The project building request, may be {@code null}.
2220     * @since 2.1
2221     */
2222    public void setProjectBuildingRequest( ProjectBuildingRequest projectBuildingRequest )
2223    {
2224        projectBuilderConfiguration = projectBuildingRequest;
2225    }
2226
2227    private void checkProjectBuildingRequest()
2228    {
2229        if ( projectBuilderConfiguration == null )
2230        {
2231            throw new IllegalStateException( "project building request missing" );
2232        }
2233    }
2234
2235}