package org.apache.maven.project;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactStatus;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.InvalidRepositoryException;
import org.apache.maven.artifact.manager.WagonManager;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.ManagedVersionMap;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.DistributionManagement;
import org.apache.maven.model.Exclusion;
import org.apache.maven.model.Extension;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.model.PluginManagement;
import org.apache.maven.model.Profile;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.Repository;
import org.apache.maven.model.Resource;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.profiles.DefaultProfileManager;
import org.apache.maven.profiles.MavenProfilesBuilder;
import org.apache.maven.profiles.ProfileManager;
import org.apache.maven.profiles.ProfilesConversionUtils;
import org.apache.maven.profiles.ProfilesRoot;
import org.apache.maven.profiles.activation.ProfileActivationException;
import org.apache.maven.project.artifact.InvalidDependencyVersionException;
import org.apache.maven.project.artifact.ProjectArtifactFactory;
import org.apache.maven.project.inheritance.ModelInheritanceAssembler;
import org.apache.maven.project.injection.ModelDefaultsInjector;
import org.apache.maven.project.injection.ProfileInjector;
import org.apache.maven.project.interpolation.ModelInterpolationException;
import org.apache.maven.project.interpolation.ModelInterpolator;
import org.apache.maven.project.path.PathTranslator;
import org.apache.maven.project.validation.ModelValidationResult;
import org.apache.maven.project.validation.ModelValidator;
import org.apache.maven.wagon.events.TransferListener;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/*:apt

 -----
 POM lifecycle
 -----

POM Lifecycle

 Order of operations when building a POM

 * inheritance
 * path translation
 * interpolation
 * defaults injection

 Current processing is:

 * inheritance
 * interpolation
 * defaults injection
 * path translation

 I'm not sure how this is working at all ... i think i have a case where this is failing but i need to
 encapsulate as a test so i can fix it. Also need to think of the in working build directory versus looking
 things up from the repository i.e buildFromSource vs buildFromRepository.

Notes

 * when the model is read it may not have a groupId, as it must be inherited

 * the inheritance assembler must use models that are unadulterated!

*/

/**
 * @version $Id: DefaultMavenProjectBuilder.java 788791 2009-06-26 17:55:26Z jdcasey $
 */
public class DefaultMavenProjectBuilder
    extends AbstractLogEnabled
    implements MavenProjectBuilder, Initializable, Contextualizable
{
    // TODO: remove
    private PlexusContainer container;

    protected MavenProfilesBuilder profilesBuilder;

    protected ArtifactResolver artifactResolver;

    protected ArtifactMetadataSource artifactMetadataSource;

    private ProjectArtifactFactory artifactFactory;

    private ModelInheritanceAssembler modelInheritanceAssembler;

    private ProfileInjector profileInjector;

    private ModelValidator validator;

    private Map rawProjectCache = new HashMap();

    private Map processedProjectCache = new HashMap();

    // TODO: make it a component
    private MavenXpp3Reader modelReader;

    private PathTranslator pathTranslator;

    private ModelDefaultsInjector modelDefaultsInjector;

    private ModelInterpolator modelInterpolator;
    
    private ArtifactRepositoryFactory artifactRepositoryFactory;

    // ----------------------------------------------------------------------
    // I am making this available for use with a new method that takes a
    // a monitor wagon monitor as a parameter so that tools can use the
    // methods here and receive callbacks. MNG-1015
    // ----------------------------------------------------------------------

    private WagonManager wagonManager;

    public static final String MAVEN_MODEL_VERSION = "4.0.0";

    public void initialize()
    {
        modelReader = new MavenXpp3Reader();
    }

    // ----------------------------------------------------------------------
    // MavenProjectBuilder Implementation
    // ----------------------------------------------------------------------

    public MavenProject build( File pom,
                               ProjectBuilderConfiguration config )
        throws ProjectBuildingException
    {
        return buildFromSourceFileInternal( pom, config, true );
    }

    public MavenProject build( File pom,
                               ProjectBuilderConfiguration config,
                               boolean checkDistributionManagementStatus )
        throws ProjectBuildingException
    {
        return buildFromSourceFileInternal( pom, config, checkDistributionManagementStatus );
    }

    public MavenProject build( File projectDescriptor,
                               ArtifactRepository localRepository,
                               ProfileManager profileManager )
        throws ProjectBuildingException
    {
        ProjectBuilderConfiguration config = new DefaultProjectBuilderConfiguration().setLocalRepository( localRepository ).setGlobalProfileManager( profileManager );
        return buildFromSourceFileInternal( projectDescriptor, config, true );
    }

    public MavenProject build( File projectDescriptor,
                               ArtifactRepository localRepository,
                               ProfileManager profileManager,
                               boolean checkDistributionManagementStatus )
        throws ProjectBuildingException
    {
        ProjectBuilderConfiguration config = new DefaultProjectBuilderConfiguration().setLocalRepository( localRepository ).setGlobalProfileManager( profileManager );
        return buildFromSourceFileInternal( projectDescriptor, config, checkDistributionManagementStatus );
    }

    // jvz:note
    // When asked for something from the repository are we getting it from the reactor? Yes, when using this call
    // we are assuming that the reactor has been run and we have collected the projects required to satisfy it0042
    // which means the projects in the reactor are required for finding classes in <project>/target/classes. Not
    // sure this is ideal. I remove all caching from the builder and all reactor related ITs which assume
    // access to simbling project resources failed.
    public MavenProject buildFromRepository( Artifact artifact,
                                             List remoteArtifactRepositories,
                                             ArtifactRepository localRepository,
                                             boolean allowStubModel )
        throws ProjectBuildingException
    {
        String cacheKey = createCacheKey( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion() );

        MavenProject project = (MavenProject) processedProjectCache.get( cacheKey );

        if ( project != null )
        {
            return project;
        }

        Model model = findModelFromRepository( artifact, remoteArtifactRepositories, localRepository, allowStubModel );

        ProjectBuilderConfiguration config = new DefaultProjectBuilderConfiguration().setLocalRepository( localRepository );

        return buildInternal( "Artifact [" + artifact + "]", model, config, remoteArtifactRepositories,
                              null, false );
    }

    public MavenProject buildFromRepository( Artifact artifact,
                                             List remoteArtifactRepositories,
                                             ArtifactRepository localRepository )
        throws ProjectBuildingException
    {
        return buildFromRepository( artifact, remoteArtifactRepositories, localRepository, true );
    }

    // what is using this externally? jvz.
    public MavenProject buildStandaloneSuperProject( ArtifactRepository localRepository )
        throws ProjectBuildingException
    {
        //TODO mkleint - use the (Container, Properties) constructor to make system properties embeddable
        ProfileManager profileManager = new DefaultProfileManager( container );

        return buildStandaloneSuperProject( new DefaultProjectBuilderConfiguration().setLocalRepository( localRepository )
                                                                                    .setGlobalProfileManager( profileManager ) );
    }

    public MavenProject buildStandaloneSuperProject( ArtifactRepository localRepository,
                                                     ProfileManager profileManager )
        throws ProjectBuildingException
    {
        return buildStandaloneSuperProject( new DefaultProjectBuilderConfiguration().setLocalRepository( localRepository )
                                                                                    .setGlobalProfileManager( profileManager ) );
    }

    public MavenProject buildStandaloneSuperProject( ProjectBuilderConfiguration config )
        throws ProjectBuildingException
    {
        Model superModel = getSuperModel();

        superModel.setGroupId( STANDALONE_SUPERPOM_GROUPID );

        superModel.setArtifactId( STANDALONE_SUPERPOM_ARTIFACTID );

        superModel.setVersion( STANDALONE_SUPERPOM_VERSION );


        List activeProfiles;

        ProfileManager profileManager = config.getGlobalProfileManager();

        if ( profileManager == null )
        {
            profileManager = new DefaultProfileManager( container );
        }

        profileManager.addProfiles( superModel.getProfiles() );

        String projectId = safeVersionlessKey( STANDALONE_SUPERPOM_GROUPID, STANDALONE_SUPERPOM_ARTIFACTID );

        activeProfiles = injectActiveProfiles( profileManager, superModel );

        MavenProject project = new MavenProject( superModel );

        project.setManagedVersionMap(
            createManagedVersionMap( projectId, superModel.getDependencyManagement(), null ) );

        project.setActiveProfiles( activeProfiles );

        project.setOriginalModel( superModel );

        try
        {
            project = processProjectLogic( "<Super-POM>", project, config, null, null, true, true );

            project.setExecutionRoot( true );

            return project;
        }
        catch ( ModelInterpolationException e )
        {
            throw new ProjectBuildingException( projectId, e.getMessage(), e );
        }
        catch ( InvalidRepositoryException e )
        {
            throw new ProjectBuildingException( projectId, e.getMessage(), e );
        }
    }

    public MavenProject buildWithDependencies( File projectDescriptor,
                                               ArtifactRepository localRepository,
                                               ProfileManager profileManager )
        throws ProjectBuildingException, ArtifactResolutionException, ArtifactNotFoundException
    {
        return buildWithDependencies( projectDescriptor, localRepository, profileManager, null );
    }

    // note:jvz This was added for the embedder.

    /** @todo move to metadatasource itself? */
    public MavenProject buildWithDependencies( File projectDescriptor,
                                               ArtifactRepository localRepository,
                                               ProfileManager profileManager,
                                               TransferListener transferListener )
        throws ProjectBuildingException, ArtifactResolutionException, ArtifactNotFoundException
    {
        MavenProject project = build( projectDescriptor, localRepository, profileManager, false );

        // ----------------------------------------------------------------------
        // Typically when the project builder is being used from maven proper
        // the transitive dependencies will not be resolved here because this
        // requires a lot of work when we may only be interested in running
        // something simple like 'm2 clean'. So the artifact collector is used
        // in the dependency resolution phase if it is required by any of the
        // goals being executed. But when used as a component in another piece
        // of code people may just want to build maven projects and have the
        // dependencies resolved for whatever reason: this is why we keep
        // this snippet of code here.
        // ----------------------------------------------------------------------

        // TODO: such a call in MavenMetadataSource too - packaging not really the intention of type
        Artifact projectArtifact = project.getArtifact();

        String projectId = safeVersionlessKey( project.getGroupId(), project.getArtifactId() );

        // Map managedVersions = createManagedVersionMap( projectId, project.getDependencyManagement() );
        Map managedVersions = project.getManagedVersionMap();

        ensureMetadataSourceIsInitialized();

        try
        {
            project.setDependencyArtifacts( project.createArtifacts( artifactFactory, null, null ) );
        }
        catch ( InvalidDependencyVersionException e )
        {
            throw new ProjectBuildingException( projectId,
                                                "Unable to build project due to an invalid dependency version: " +
                                                    e.getMessage(), e );
        }

        if ( transferListener != null )
        {
            wagonManager.setDownloadMonitor( transferListener );
        }

        ArtifactResolutionResult result = artifactResolver.resolveTransitively( project.getDependencyArtifacts(),
                                                                                projectArtifact, managedVersions,
                                                                                localRepository,
                                                                                project.getRemoteArtifactRepositories(),
                                                                                artifactMetadataSource );

        project.setArtifacts( result.getArtifacts() );

        return project;
    }

    // ----------------------------------------------------------------------
    //
    // ----------------------------------------------------------------------

    private void ensureMetadataSourceIsInitialized()
        throws ProjectBuildingException
    {
        if ( artifactMetadataSource == null )
        {
            try
            {
                artifactMetadataSource = (ArtifactMetadataSource) container.lookup( ArtifactMetadataSource.ROLE );
            }
            catch ( ComponentLookupException e )
            {
                throw new ProjectBuildingException( "all", "Cannot lookup metadata source for building the project.",
                                                    e );
            }
        }
    }

    private Map createManagedVersionMap( String projectId,
                                         DependencyManagement dependencyManagement,
                                         MavenProject parent )
        throws ProjectBuildingException
    {
        Map map = null;
        List deps;
        if ( ( dependencyManagement != null ) && ( ( deps = dependencyManagement.getDependencies() ) != null ) &&
            ( deps.size() > 0 ) )
        {
            map = new ManagedVersionMap( map );

            if ( getLogger().isDebugEnabled() )
            {
                getLogger().debug( "Adding managed dependencies for " + projectId );
            }

            for ( Iterator i = dependencyManagement.getDependencies().iterator(); i.hasNext(); )
            {
                Dependency d = (Dependency) i.next();

                try
                {
                    VersionRange versionRange = VersionRange.createFromVersionSpec( d.getVersion() );

                    Artifact artifact = artifactFactory.createDependencyArtifact( d.getGroupId(), d.getArtifactId(),
                                                                                  versionRange, d.getType(),
                                                                                  d.getClassifier(), d.getScope(),
                                                                                  d.isOptional() );
                    if ( getLogger().isDebugEnabled() )
                    {
                        getLogger().debug( "  " + artifact );
                    }

                    // If the dependencyManagement section listed exclusions,
                    // add them to the managed artifacts here so that transitive
                    // dependencies will be excluded if necessary.
                    if ( ( null != d.getExclusions() ) && !d.getExclusions().isEmpty() )
                    {
                        List exclusions = new ArrayList();

                        Iterator exclItr = d.getExclusions().iterator();

                        while ( exclItr.hasNext() )
                        {
                            Exclusion e = (Exclusion) exclItr.next();
                            exclusions.add( e.getGroupId() + ":" + e.getArtifactId() );
                        }
                        ExcludesArtifactFilter eaf = new ExcludesArtifactFilter( exclusions );
                        artifact.setDependencyFilter( eaf );
                    }
                    else
                    {
                        artifact.setDependencyFilter( null );
                    }
                    map.put( d.getManagementKey(), artifact );
                }
                catch ( InvalidVersionSpecificationException e )
                {
                    throw new ProjectBuildingException( projectId, "Unable to parse version '" + d.getVersion() +
                        "' for dependency '" + d.getManagementKey() + "': " + e.getMessage(), e );
                }
            }
        }
        else if ( map == null )
        {
            map = Collections.EMPTY_MAP;
        }

        return map;
    }

    private MavenProject buildFromSourceFileInternal( File projectDescriptor,
                                                      ProjectBuilderConfiguration config,
                                                      boolean checkDistributionManagementStatus )
        throws ProjectBuildingException
    {
        Model model = readModel( "unknown", projectDescriptor, true );

        MavenProject project = buildInternal( projectDescriptor.getAbsolutePath(), model, config,
                                              buildArtifactRepositories( getSuperModel() ), projectDescriptor,
                                              true );

        if ( checkDistributionManagementStatus )
        {
            if ( ( project.getDistributionManagement() != null ) &&
                ( project.getDistributionManagement().getStatus() != null ) )
            {
                String projectId = safeVersionlessKey( project.getGroupId(), project.getArtifactId() );

                throw new ProjectBuildingException( projectId,
                                                    "Invalid project file: distribution status must not be specified for a project outside of the repository" );
            }
        }

        return project;
    }

    private Model findModelFromRepository( Artifact artifact,
                                           List remoteArtifactRepositories,
                                           ArtifactRepository localRepository,
                                           boolean allowStubModel )
        throws ProjectBuildingException
    {
        String projectId = safeVersionlessKey( artifact.getGroupId(), artifact.getArtifactId() );

        normalizeToArtifactRepositories( remoteArtifactRepositories, projectId );

        Artifact projectArtifact;

        // if the artifact is not a POM, we need to construct a POM artifact based on the artifact parameter given.
        if ( "pom".equals( artifact.getType() ) )
        {
            projectArtifact = artifact;
        }
        else
        {
            getLogger().debug( "Attempting to build MavenProject instance for Artifact (" + artifact.getGroupId() + ":"
                                  + artifact.getArtifactId() + ":" + artifact.getVersion() + ") of type: "
                                  + artifact.getType() + "; constructing POM artifact instead." );

            projectArtifact = artifactFactory.createProjectArtifact( artifact.getGroupId(), artifact.getArtifactId(),
                                                                     artifact.getVersion(), artifact.getScope() );
        }

        Model model;

        try
        {
            artifactResolver.resolve( projectArtifact, remoteArtifactRepositories, localRepository );

            File file = projectArtifact.getFile();

            model = readModel( projectId, file, false );

            String downloadUrl = null;

            ArtifactStatus status = ArtifactStatus.NONE;

            DistributionManagement distributionManagement = model.getDistributionManagement();

            if ( distributionManagement != null )
            {
                downloadUrl = distributionManagement.getDownloadUrl();

                status = ArtifactStatus.valueOf( distributionManagement.getStatus() );
            }

            checkStatusAndUpdate( projectArtifact, status, file, remoteArtifactRepositories, localRepository );

            // TODO: this is gross. Would like to give it the whole model, but maven-artifact shouldn't depend on that
            // Can a maven-core implementation of the Artifact interface store it, and be used in the exceptions?
            if ( downloadUrl != null )
            {
                projectArtifact.setDownloadUrl( downloadUrl );
            }
            else
            {
                projectArtifact.setDownloadUrl( model.getUrl() );
            }
        }
        catch ( ArtifactResolutionException e )
        {
            throw new ProjectBuildingException( projectId, "Error getting POM for '" + projectId +
                "' from the repository: " + e.getMessage(), e );
        }
        catch ( ArtifactNotFoundException e )
        {
            if ( allowStubModel )
            {
                getLogger().debug( "Artifact not found - using stub model: " + e.getMessage() );

                model = createStubModel( projectArtifact );
            }
            else
            {
                throw new ProjectBuildingException( projectId, "POM '" + projectId + "' not found in repository: " +
                    e.getMessage(), e );
            }
        }

        return model;
    }

    private List normalizeToArtifactRepositories( List remoteArtifactRepositories,
                                                  String projectId )
        throws ProjectBuildingException
    {
        List normalized = new ArrayList( remoteArtifactRepositories.size() );

        boolean normalizationNeeded = false;
        for ( Iterator it = remoteArtifactRepositories.iterator(); it.hasNext(); )
        {
            Object item = it.next();

            if ( item instanceof ArtifactRepository )
            {
                normalized.add( item );
            }
            else if ( item instanceof Repository )
            {
                Repository repo = (Repository) item;
                try
                {
                    item = ProjectUtils.buildArtifactRepository( repo, artifactRepositoryFactory, container );

                    normalized.add( item );
                    normalizationNeeded = true;
                }
                catch ( InvalidRepositoryException e )
                {
                    throw new ProjectBuildingException( projectId, "Error building artifact repository for id: " + repo.getId(), e );
                }
            }
            else
            {
                throw new ProjectBuildingException( projectId, "Error building artifact repository from non-repository information item: " + item );
            }
        }

        if ( normalizationNeeded )
        {
            return normalized;
        }
        else
        {
            return remoteArtifactRepositories;
        }
    }

    private void checkStatusAndUpdate( Artifact projectArtifact,
                                       ArtifactStatus status,
                                       File file,
                                       List remoteArtifactRepositories,
                                       ArtifactRepository localRepository )
        throws ArtifactNotFoundException
    {
        // TODO: configurable actions dependant on status
        if ( !projectArtifact.isSnapshot() && ( status.compareTo( ArtifactStatus.DEPLOYED ) < 0 ) )
        {
            // use default policy (enabled, daily update, warn on bad checksum)
            ArtifactRepositoryPolicy policy = new ArtifactRepositoryPolicy();
            // TODO: re-enable [MNG-798/865]
            policy.setUpdatePolicy( ArtifactRepositoryPolicy.UPDATE_POLICY_NEVER );

            if ( policy.checkOutOfDate( new Date( file.lastModified() ) ) )
            {
                getLogger().info(
                    projectArtifact.getArtifactId() + ": updating metadata due to status of '" + status + "'" );
                try
                {
                    projectArtifact.setResolved( false );
                    artifactResolver.resolveAlways( projectArtifact, remoteArtifactRepositories, localRepository );
                }
                catch ( ArtifactResolutionException e )
                {
                    getLogger().warn( "Error updating POM - using existing version" );
                    getLogger().debug( "Cause", e );
                }
                catch ( ArtifactNotFoundException e )
                {
                    getLogger().warn( "Error updating POM - not found. Removing local copy." );
                    getLogger().debug( "Cause", e );
                    file.delete();
                    throw e;
                }
            }
        }
    }

    // jvz:note
    // This is used when requested artifacts do not have an associated POM. This is for the case where we are
    // using an m1 repo where the only thing required to be present are the JAR files.
    private Model createStubModel( Artifact projectArtifact )
    {
        getLogger().debug( "Using defaults for missing POM " + projectArtifact );

        Model model = new Model();

        model.setModelVersion( "4.0.0" );

        model.setArtifactId( projectArtifact.getArtifactId() );

        model.setGroupId( projectArtifact.getGroupId() );

        model.setVersion( projectArtifact.getVersion() );

        // TODO: not correct in some instances
        model.setPackaging( projectArtifact.getType() );

        model.setDistributionManagement( new DistributionManagement() );

        model.getDistributionManagement().setStatus( ArtifactStatus.GENERATED.toString() );

        return model;
    }

    // jvz:note
    // We've got a mixture of things going in the USD and from the repository, sometimes the descriptor
    // is a real file and sometimes null which makes things confusing.
    private MavenProject buildInternal( String pomLocation,
                                        Model model,
                                        ProjectBuilderConfiguration config,
                                        List parentSearchRepositories,
                                        File projectDescriptor,
                                        boolean strict )
        throws ProjectBuildingException
    {
        File projectDir = null;

        if ( projectDescriptor != null )
        {
            projectDir = projectDescriptor.getAbsoluteFile().getParentFile();
        }

        Model superModel = getSuperModel();

        ProfileManager externalProfileManager = config.getGlobalProfileManager();
        ProfileManager superProjectProfileManager;
        if ( externalProfileManager != null )
        {
            superProjectProfileManager = new DefaultProfileManager(
                                                                    container,
                                                                    externalProfileManager.getRequestProperties() );
        }
        else
        {
            superProjectProfileManager = new DefaultProfileManager( container );
        }

        List activeProfiles;

        superProjectProfileManager.addProfiles( superModel.getProfiles() );

        activeProfiles = injectActiveProfiles( superProjectProfileManager, superModel );

        MavenProject superProject = new MavenProject( superModel );

        superProject.setActiveProfiles( activeProfiles );

        //noinspection CollectionDeclaredAsConcreteClass
        LinkedList lineage = new LinkedList();

        // TODO: the aRWR can get out of sync with project.model.repositories. We should do all the processing of
        // profiles, etc on the models then recreate the aggregated sets at the end from the project repositories (they
        // must still be created along the way so that parent poms can be discovered, however)
        // Use a TreeSet to ensure ordering is retained
        Set aggregatedRemoteWagonRepositories = new LinkedHashSet();

        String projectId = safeVersionlessKey( model.getGroupId(), model.getArtifactId() );

        List activeExternalProfiles;
        try
        {
            if ( externalProfileManager != null )
            {
                activeExternalProfiles = externalProfileManager.getActiveProfiles();
            }
            else
            {
                activeExternalProfiles = Collections.EMPTY_LIST;
            }
        }
        catch ( ProfileActivationException e )
        {
            throw new ProjectBuildingException( projectId, "Failed to calculate active external profiles.", e );
        }

        for ( Iterator i = activeExternalProfiles.iterator(); i.hasNext(); )
        {
            Profile externalProfile = (Profile) i.next();

            for ( Iterator repoIterator = externalProfile.getRepositories().iterator(); repoIterator.hasNext(); )
            {
                Repository mavenRepo = (Repository) repoIterator.next();

                ArtifactRepository artifactRepo = null;
                try
                {
                    artifactRepo =
                        ProjectUtils.buildArtifactRepository( mavenRepo, artifactRepositoryFactory, container );
                }
                catch ( InvalidRepositoryException e )
                {
                    throw new ProjectBuildingException( projectId, e.getMessage(), e );
                }

                aggregatedRemoteWagonRepositories.add( artifactRepo );
            }
        }

        MavenProject project = null;
        try
        {
            project = assembleLineage( model, lineage, config, projectDescriptor, parentSearchRepositories,
                                       aggregatedRemoteWagonRepositories, strict );
        }
        catch ( InvalidRepositoryException e )
        {
            throw new ProjectBuildingException( projectId, e.getMessage(), e );
        }

        // we don't have to force the collision exception for superModel here, it's already been done in getSuperModel()
        MavenProject previousProject = superProject;

        Model previous = superProject.getModel();

        for ( Iterator i = lineage.iterator(); i.hasNext(); )
        {
            MavenProject currentProject = (MavenProject) i.next();

            Model current = currentProject.getModel();

            String pathAdjustment = null;

            try
            {
                pathAdjustment = previousProject.getModulePathAdjustment( currentProject );
            }
            catch ( IOException e )
            {
                getLogger().debug( "Cannot determine whether " + currentProject.getId() + " is a module of " +
                    previousProject.getId() + ". Reason: " + e.getMessage(), e );
            }

            modelInheritanceAssembler.assembleModelInheritance( current, previous, pathAdjustment );

            previous = current;
            previousProject = currentProject;
        }

        // only add the super repository if it wasn't overridden by a profile or project
        List repositories = new ArrayList( aggregatedRemoteWagonRepositories );

        List superRepositories = buildArtifactRepositories( superModel );

        for ( Iterator i = superRepositories.iterator(); i.hasNext(); )
        {
            ArtifactRepository repository = (ArtifactRepository) i.next();

            if ( !repositories.contains( repository ) )
            {
                repositories.add( repository );
            }
        }

        // merge any duplicated plugin definitions together, using the first appearance as the dominant one.
        ModelUtils.mergeDuplicatePluginDefinitions( project.getModel().getBuild() );

        try
        {
            project = processProjectLogic( pomLocation, project, config, projectDir, repositories, strict, false );
        }
        catch ( ModelInterpolationException e )
        {
            throw new InvalidProjectModelException( projectId, pomLocation, e.getMessage(), e );
        }
        catch ( InvalidRepositoryException e )
        {
            throw new InvalidProjectModelException( projectId, pomLocation, e.getMessage(), e );
        }

        processedProjectCache.put(
                                  createCacheKey( project.getGroupId(), project.getArtifactId(), project.getVersion() ), project );

          // jvz:note
        // this only happens if we are building from a source file
        if ( projectDescriptor != null )
        {
            Build build = project.getBuild();

            project.addCompileSourceRoot( build.getSourceDirectory() );

            project.addScriptSourceRoot( build.getScriptSourceDirectory() );

            project.addTestCompileSourceRoot( build.getTestSourceDirectory() );

            // Only track the file of a POM in the source tree
            project.setFile( projectDescriptor );
        }
        
        project.setManagedVersionMap( createManagedVersionMap( projectId,
                                                               project.getDependencyManagement(),
                                                               project.getParent() ) );

        return project;
    }

    private String safeVersionlessKey( String groupId,
                                       String artifactId )
    {
        String gid = groupId;

        if ( StringUtils.isEmpty( gid ) )
        {
            gid = "unknown";
        }

        String aid = artifactId;

        if ( StringUtils.isEmpty( aid ) )
        {
            aid = "unknown";
        }

        return ArtifactUtils.versionlessKey( gid, aid );
    }

    private List buildArtifactRepositories( Model model )
        throws ProjectBuildingException
    {
        try
        {
            return ProjectUtils.buildArtifactRepositories( model.getRepositories(), artifactRepositoryFactory,
                                                           container );
        }
        catch ( InvalidRepositoryException e )
        {
            String projectId = safeVersionlessKey( model.getGroupId(), model.getArtifactId() );

            throw new ProjectBuildingException( projectId, e.getMessage(), e );
        }
    }

    /**
     * @todo can this take in a model instead of a project and still be successful?
     * @todo In fact, does project REALLY need a MavenProject as a parent? Couldn't it have just a wrapper around a
     * model that supported parents which were also the wrapper so that inheritence was assembled. We don't really need
     * the resolved source roots, etc for the parent - that occurs for the parent when it is constructed independently
     * and projects are not cached or reused
     */
    private MavenProject processProjectLogic( String pomLocation,
                                              MavenProject project,
                                              ProjectBuilderConfiguration config,
                                              File projectDir,
                                              List remoteRepositories,
                                              boolean strict,
                                              boolean isSuperPom )
        throws ProjectBuildingException, ModelInterpolationException, InvalidRepositoryException
    {
        Model model = project.getModel();

        List activeProfiles = project.getActiveProfiles();

        if ( activeProfiles == null )
        {
            activeProfiles = new ArrayList();
        }

        ProfileManager profileMgr = config == null ? null : config.getGlobalProfileManager();

        List injectedProfiles = injectActiveProfiles( profileMgr, model );

        activeProfiles.addAll( injectedProfiles );
        
        // --------------------------------------------------------------------------------
        
        Build dynamicBuild = model.getBuild();

        model.setBuild( ModelUtils.cloneBuild( dynamicBuild ) );

        model = modelInterpolator.interpolate( model, projectDir, config, getLogger().isDebugEnabled() );

        mergeDeterministicBuildElements( model.getBuild(), dynamicBuild );

        model.setBuild( dynamicBuild );
        
        // MNG-3482: Make sure depMgmt is interpolated before merging.
        if ( !isSuperPom )
        {
            mergeManagedDependencies( model, config.getLocalRepository(), remoteRepositories );
        }

        // interpolation is before injection, because interpolation is off-limits in the injected variables
        modelDefaultsInjector.injectDefaults( model );

        MavenProject parentProject = project.getParent();

        Model originalModel = project.getOriginalModel();

        // We will return a different project object using the new model (hence the need to return a project, not just modify the parameter)
        project = new MavenProject( model, getLogger() );

        project.setOriginalModel( originalModel );
        
        project.setActiveProfiles( activeProfiles );

        // TODO: maybe not strictly correct, while we should enfore that packaging has a type handler of the same id, we don't
        Artifact projectArtifact = artifactFactory.create( project );
        
        project.setArtifact( projectArtifact );
        project.setProjectBuilderConfiguration( config );

        project.setPluginArtifactRepositories( ProjectUtils.buildArtifactRepositories( model.getPluginRepositories(),
                                                                                       artifactRepositoryFactory,
                                                                                       container ) );

        DistributionManagement dm = model.getDistributionManagement();
        if ( dm != null )
        {
            ArtifactRepository repo = ProjectUtils.buildDeploymentArtifactRepository( dm.getRepository(),
                                                                                      artifactRepositoryFactory,
                                                                                      container );
            project.setReleaseArtifactRepository( repo );

            if ( dm.getSnapshotRepository() != null )
            {
                repo = ProjectUtils.buildDeploymentArtifactRepository( dm.getSnapshotRepository(),
                                                                       artifactRepositoryFactory, container );
                project.setSnapshotArtifactRepository( repo );
            }
        }

        if ( parentProject != null )
        {
            String cacheKey = createCacheKey( parentProject.getGroupId(),
                                              parentProject.getArtifactId(),
                                              parentProject.getVersion() );

            MavenProject processedParent = (MavenProject) processedProjectCache.get( cacheKey );
            Artifact parentArtifact;

            // yeah, this null check might be a bit paranoid, but better safe than sorry...
            if ( processedParent != null )
            {
                project.setParent( processedParent );

                parentArtifact = processedParent.getArtifact();
            }
            else
            {
                project.setParent( parentProject );

                parentArtifact = artifactFactory.createParentArtifact( parentProject.getGroupId(),
                                                                                parentProject.getArtifactId(),
                                                                                parentProject.getVersion() );
            }

            project.setParentArtifact( parentArtifact );
        }

        // Must validate before artifact construction to make sure dependencies are good
        ModelValidationResult validationResult = validator.validate( model );

        String projectId = safeVersionlessKey( model.getGroupId(), model.getArtifactId() );

        if ( validationResult.getMessageCount() > 0 )
        {
            throw new InvalidProjectModelException( projectId, pomLocation, "Failed to validate POM",
                                                    validationResult );
        }

        project.setRemoteArtifactRepositories(
            ProjectUtils.buildArtifactRepositories( model.getRepositories(), artifactRepositoryFactory, container ) );

        // TODO: these aren't taking active project artifacts into consideration in the reactor
        project.setPluginArtifacts( createPluginArtifacts( projectId, project.getBuildPlugins() ) );

        project.setReportArtifacts( createReportArtifacts( projectId, project.getReportPlugins() ) );

        project.setExtensionArtifacts( createExtensionArtifacts( projectId, project.getBuildExtensions() ) );
        
        return project;
    }

    private void mergeDeterministicBuildElements( Build interpolatedBuild,
                                                  Build dynamicBuild )
    {
        mergeDeterministicPluginElements( interpolatedBuild.getPlugins(), dynamicBuild.getPlugins() );

        PluginManagement dPluginMgmt = dynamicBuild.getPluginManagement();
        PluginManagement iPluginMgmt = interpolatedBuild.getPluginManagement();

        if ( dPluginMgmt != null )
        {
            mergeDeterministicPluginElements( iPluginMgmt.getPlugins(), dPluginMgmt.getPlugins() );
        }

        if ( dynamicBuild.getExtensions() != null )
        {
            dynamicBuild.setExtensions( interpolatedBuild.getExtensions() );
        }
    }

    private void mergeDeterministicPluginElements( List iPlugins, List dPlugins )
    {
        if ( dPlugins != null )
        {
            for ( int i = 0; i < dPlugins.size(); i++ )
            {
                Plugin dPlugin = (Plugin) dPlugins.get( i );
                Plugin iPlugin = (Plugin) iPlugins.get( i );

                dPlugin.setGroupId( iPlugin.getGroupId() );
                dPlugin.setArtifactId( iPlugin.getArtifactId() );
                dPlugin.setVersion( iPlugin.getVersion() );
                
                dPlugin.setDependencies( iPlugin.getDependencies() );
                
                List dExecutions = dPlugin.getExecutions();
                if ( dExecutions != null )
                {
                    List iExecutions = iPlugin.getExecutions();
                    
                    for ( int j = 0; j < dExecutions.size(); j++ )
                    {
                        PluginExecution dExec = (PluginExecution) dExecutions.get( j );
                        PluginExecution iExec = (PluginExecution) iExecutions.get( j );
                        
                        dExec.setId( iExec.getId() );
                    }
                }
            }
        }
    }

    /**
     * @noinspection CollectionDeclaredAsConcreteClass
     * @todo We need to find an effective way to unit test parts of this method!
     * @todo Refactor this into smaller methods with discrete purposes.
     */
    private MavenProject assembleLineage( Model model,
                                          LinkedList lineage,
                                          ProjectBuilderConfiguration config,
                                          File projectDescriptor,
                                          List parentSearchRepositories,
                                          Set aggregatedRemoteWagonRepositories,
                                          boolean strict )
        throws ProjectBuildingException, InvalidRepositoryException
    {
        Model originalModel = ModelUtils.cloneModel( model );

        File projectDir = null;
        if ( projectDescriptor != null )
        {
            projectDir = projectDescriptor.getAbsoluteFile().getParentFile();
        }

        ProfileManager externalProfileManager = config.getGlobalProfileManager();
        ProfileManager profileManager;
        if ( externalProfileManager != null )
        {
            profileManager = new DefaultProfileManager( container, externalProfileManager.getRequestProperties() );
        }
        else
        {
            //TODO mkleint - use the (Container, Properties constructor to make system properties embeddable
            profileManager = new DefaultProfileManager( container );
        }

        if ( externalProfileManager != null )
        {
            profileManager.explicitlyActivate( externalProfileManager.getExplicitlyActivatedIds() );

            profileManager.explicitlyDeactivate( externalProfileManager.getExplicitlyDeactivatedIds() );
        }

        List activeProfiles;

        try
        {
            profileManager.addProfiles( model.getProfiles() );

            loadProjectExternalProfiles( profileManager, projectDir );

            activeProfiles = injectActiveProfiles( profileManager, model );
        }
        catch ( ProfileActivationException e )
        {
            String projectId = safeVersionlessKey( model.getGroupId(), model.getArtifactId() );

            throw new ProjectBuildingException( projectId, "Failed to activate local (project-level) build profiles: " +
                e.getMessage(), e );
        }

        if ( !model.getRepositories().isEmpty() )
        {
            List respositories = buildArtifactRepositories( model );

            for ( Iterator it = respositories.iterator(); it.hasNext(); )
            {
                ArtifactRepository repository = (ArtifactRepository) it.next();

                if ( !aggregatedRemoteWagonRepositories.contains( repository ) )
                {
                    aggregatedRemoteWagonRepositories.add( repository );
                }
            }
        }

        MavenProject project = new MavenProject( model, getLogger() );

        project.setFile( projectDescriptor );
        project.setActiveProfiles( activeProfiles );
        project.setOriginalModel( originalModel );

        lineage.addFirst( project );

        Parent parentModel = model.getParent();

        String projectId = safeVersionlessKey( model.getGroupId(), model.getArtifactId() );

        if ( parentModel != null )
        {
            if ( StringUtils.isEmpty( parentModel.getGroupId() ) )
            {
                throw new ProjectBuildingException( projectId, "Missing groupId element from parent element" );
            }
            else if ( StringUtils.isEmpty( parentModel.getArtifactId() ) )
            {
                throw new ProjectBuildingException( projectId, "Missing artifactId element from parent element" );
            }
            else if ( parentModel.getGroupId().equals( model.getGroupId() ) &&
                parentModel.getArtifactId().equals( model.getArtifactId() ) )
            {
                throw new ProjectBuildingException( projectId,
                                                    "Parent element is a duplicate of " + "the current project " );
            }
            else if ( StringUtils.isEmpty( parentModel.getVersion() ) )
            {
                throw new ProjectBuildingException( projectId, "Missing version element from parent element" );
            }

            // the only way this will have a value is if we find the parent on disk...
            File parentDescriptor = null;

            model = null;

            String parentKey =
                createCacheKey( parentModel.getGroupId(), parentModel.getArtifactId(), parentModel.getVersion() );
            MavenProject parentProject = (MavenProject) rawProjectCache.get( parentKey );

            if ( parentProject != null )
            {
                model = ModelUtils.cloneModel( parentProject.getOriginalModel() );

                parentDescriptor = parentProject.getFile();
            }

            String parentRelativePath = parentModel.getRelativePath();

            // if we can't find a cached model matching the parent spec, then let's try to look on disk using
            // <relativePath/>
            if ( ( model == null ) && ( projectDir != null ) && StringUtils.isNotEmpty( parentRelativePath ) )
            {
                parentDescriptor = new File( projectDir, parentRelativePath );

                if ( getLogger().isDebugEnabled() )
                {
                    getLogger().debug( "Searching for parent-POM: " + parentModel.getId() + " of project: " +
                        project.getId() + " in relative path: " + parentRelativePath );
                }

                if ( parentDescriptor.isDirectory() )
                {
                    if ( getLogger().isDebugEnabled() )
                    {
                        getLogger().debug( "Path specified in <relativePath/> (" + parentRelativePath +
                            ") is a directory. Searching for 'pom.xml' within this directory." );
                    }

                    parentDescriptor = new File( parentDescriptor, "pom.xml" );

                    if ( !parentDescriptor.exists() )
                    {
                        if ( getLogger().isDebugEnabled() )
                        {
                            getLogger().debug( "Parent-POM: " + parentModel.getId() + " for project: " +
                                project.getId() + " cannot be loaded from relative path: " + parentDescriptor +
                                "; path does not exist." );
                        }
                    }
                }

                if ( parentDescriptor != null )
                {
                    try
                    {
                        parentDescriptor = parentDescriptor.getCanonicalFile();
                    }
                    catch ( IOException e )
                    {
                        getLogger().debug( "Failed to canonicalize potential parent POM: \'" + parentDescriptor + "\'",
                                           e );

                        parentDescriptor = null;
                    }
                }

                if ( ( parentDescriptor != null ) && parentDescriptor.exists() )
                {
                    Model candidateParent = readModel( projectId, parentDescriptor, strict );

                    String candidateParentGroupId = candidateParent.getGroupId();
                    if ( ( candidateParentGroupId == null ) && ( candidateParent.getParent() != null ) )
                    {
                        candidateParentGroupId = candidateParent.getParent().getGroupId();
                    }

                    String candidateParentVersion = candidateParent.getVersion();
                    if ( ( candidateParentVersion == null ) && ( candidateParent.getParent() != null ) )
                    {
                        candidateParentVersion = candidateParent.getParent().getVersion();
                    }

                    if ( parentModel.getGroupId().equals( candidateParentGroupId ) &&
                        parentModel.getArtifactId().equals( candidateParent.getArtifactId() ) &&
                        parentModel.getVersion().equals( candidateParentVersion ) )
                    {
                        model = candidateParent;

                        getLogger().debug( "Using parent-POM from the project hierarchy at: \'" +
                            parentModel.getRelativePath() + "\' for project: " + project.getId() );
                    }
                    else
                    {
                        getLogger().debug( "Invalid parent-POM referenced by relative path '" +
                            parentModel.getRelativePath() + "' in parent specification in " + project.getId() + ":" +
                            "\n  Specified: " + parentModel.getId() + "\n  Found:     " + candidateParent.getId() );
                    }
                }
                else if ( getLogger().isDebugEnabled() )
                {
                    getLogger().debug(
                        "Parent-POM: " + parentModel.getId() + " not found in relative path: " + parentRelativePath );
                }
            }

            Artifact parentArtifact = null;

            // only resolve the parent model from the repository system if we didn't find it on disk...
            if ( model == null )
            {
                // MNG-2302: parent's File was being populated incorrectly when parent is loaded from repo.
                // keep this in line with other POMs loaded from the repository...the file should be null.
                parentDescriptor = null;

                //!! (**)
                // ----------------------------------------------------------------------
                // Do we have the necessary information to actually find the parent
                // POMs here?? I don't think so ... Say only one remote repository is
                // specified and that is ibiblio then this model that we just read doesn't
                // have any repository information ... I think we might have to inherit
                // as we go in order to do this.
                // ----------------------------------------------------------------------

                // we must add the repository this POM was found in too, by chance it may be located where the parent is
                // we can't query the parent to ask where it is :)
                List remoteRepositories = new ArrayList( aggregatedRemoteWagonRepositories );
                remoteRepositories.addAll( parentSearchRepositories );

                if ( getLogger().isDebugEnabled() )
                {
                    getLogger().debug( "Retrieving parent-POM: " + parentModel.getId() + " for project: " +
                        project.getId() + " from the repository." );
                }

                parentArtifact = artifactFactory.createParentArtifact( parentModel.getGroupId(),
                                                                       parentModel.getArtifactId(),
                                                                       parentModel.getVersion() );

                try
                {
                    model = findModelFromRepository( parentArtifact, remoteRepositories, config.getLocalRepository(), false );
                }
                catch ( ProjectBuildingException e )
                {
                    throw new ProjectBuildingException( project.getId(), "Cannot find parent: " + e.getProjectId() +
                        " for project: " + project.getId(), e );
                }
            }

            if ( ( model != null ) && !"pom".equals( model.getPackaging() ) )
            {
                throw new ProjectBuildingException( projectId, "Parent: " + model.getId() + " of project: " +
                    projectId + " has wrong packaging: " + model.getPackaging() + ". Must be 'pom'." );
            }

            MavenProject parent = assembleLineage( model,
                                                   lineage,
                                                   config,
                                                   parentDescriptor,
                                                   parentSearchRepositories,
                                                   aggregatedRemoteWagonRepositories,
                                                   strict );

            project.setParent( parent );

            project.setParentArtifact( parentArtifact );
        }

        rawProjectCache.put( createCacheKey( project.getGroupId(), project.getArtifactId(), project.getVersion() ), new MavenProject( project ) );

        return project;
    }

    private void mergeManagedDependencies(Model model, ArtifactRepository localRepository, List parentSearchRepositories)
        throws ProjectBuildingException
    {
        DependencyManagement modelDepMgmt = model.getDependencyManagement();

        if (modelDepMgmt != null)
        {
            Map depsMap = new TreeMap();
            Iterator iter = modelDepMgmt.getDependencies().iterator();
            boolean doInclude = false;
            while (iter.hasNext())
            {
                Dependency dep = (Dependency) iter.next();
                depsMap.put( dep.getManagementKey(), dep );
                if ( dep.getType().equals( "pom" ) && Artifact.SCOPE_IMPORT.equals( dep.getScope() ) )
                {
                    doInclude = true;
                }
            }
            Map newDeps = new TreeMap(depsMap);
            iter = modelDepMgmt.getDependencies().iterator();
            if (doInclude)
            {
                while (iter.hasNext())
                {
                    Dependency dep = (Dependency)iter.next();
                    if ( dep.getType().equals( "pom" )
                         && Artifact.SCOPE_IMPORT.equals( dep.getScope() ) )
                    {
                        Artifact artifact = artifactFactory.createProjectArtifact( dep.getGroupId(), dep.getArtifactId(),
                                                                                  dep.getVersion(), dep.getScope() );
                        MavenProject project = buildFromRepository(artifact, parentSearchRepositories, localRepository, false);

                        DependencyManagement depMgmt = project.getDependencyManagement();

                        if (depMgmt != null)
                        {
                            if ( getLogger().isDebugEnabled() )
                            {
                                getLogger().debug( "Importing managed dependencies for " + dep.toString() );
                            }

                            for ( Iterator it = depMgmt.getDependencies().iterator(); it.hasNext(); )
                            {
                                Dependency includedDep = (Dependency) it.next();
                                String key = includedDep.getManagementKey();
                                if (!newDeps.containsKey(key))
                                {
                                    newDeps.put( includedDep.getManagementKey(), includedDep );
                                }
                            }
                            newDeps.remove(dep.getManagementKey());
                        }
                    }
                }
                List deps = new ArrayList(newDeps.values());
                modelDepMgmt.setDependencies(deps);
            }
        }
    }

    private List injectActiveProfiles( ProfileManager profileManager,
                                       Model model )
        throws ProjectBuildingException
    {
        List activeProfiles;

        if ( profileManager != null )
        {
            try
            {
                activeProfiles = profileManager.getActiveProfiles();
            }
            catch ( ProfileActivationException e )
            {
                String projectId = safeVersionlessKey( model.getGroupId(), model.getArtifactId() );

                throw new ProjectBuildingException( projectId, e.getMessage(), e );
            }

            for ( Iterator it = activeProfiles.iterator(); it.hasNext(); )
            {
                Profile profile = (Profile) it.next();

                profileInjector.inject( profile, model );
            }
        }
        else
        {
            activeProfiles = Collections.EMPTY_LIST;
        }

        return activeProfiles;
    }

    private void loadProjectExternalProfiles( ProfileManager profileManager,
                                              File projectDir )
        throws ProfileActivationException
    {
        if ( projectDir != null )
        {
            try
            {
                ProfilesRoot root = profilesBuilder.buildProfiles( projectDir );

                if ( root != null )
                {
                    List active = root.getActiveProfiles();

                    if ( ( active != null ) && !active.isEmpty() )
                    {
                        profileManager.explicitlyActivate( root.getActiveProfiles() );
                    }

                    for ( Iterator it = root.getProfiles().iterator(); it.hasNext(); )
                    {
                        org.apache.maven.profiles.Profile rawProfile = (org.apache.maven.profiles.Profile) it.next();

                        Profile converted = ProfilesConversionUtils.convertFromProfileXmlProfile( rawProfile );

                        profileManager.addProfile( converted );
                    }
                }
            }
            catch ( IOException e )
            {
                throw new ProfileActivationException( "Cannot read profiles.xml resource from directory: " + projectDir,
                                                      e );
            }
            catch ( XmlPullParserException e )
            {
                throw new ProfileActivationException(
                    "Cannot parse profiles.xml resource from directory: " + projectDir, e );
            }
        }
    }

    private Model readModel( String projectId,
                             File file,
                             boolean strict )
        throws ProjectBuildingException
    {
        Reader reader = null;
        try
        {
            reader = ReaderFactory.newXmlReader( file );
            return readModel( projectId, file.getAbsolutePath(), reader, strict );
        }
        catch ( FileNotFoundException e )
        {
            throw new ProjectBuildingException( projectId,
                                                "Could not find the model file '" + file.getAbsolutePath() + "'.", e );
        }
        catch ( IOException e )
        {
            throw new ProjectBuildingException( projectId, "Failed to build model from file '" +
                file.getAbsolutePath() + "'.\nError: \'" + e.getLocalizedMessage() + "\'", e );
        }
        finally
        {
            IOUtil.close( reader );
        }
    }

    private Model readModel( String projectId,
                             String pomLocation,
                             Reader reader,
                             boolean strict )
        throws IOException, InvalidProjectModelException
    {
        String modelSource = IOUtil.toString( reader );

        if ( modelSource.indexOf( "<modelVersion>" + MAVEN_MODEL_VERSION ) < 0 )
        {
            throw new InvalidProjectModelException( projectId, pomLocation, "Not a v" + MAVEN_MODEL_VERSION  + " POM." );
        }

        StringReader sReader = new StringReader( modelSource );

        try
        {
            return modelReader.read( sReader, strict );
        }
        catch ( XmlPullParserException e )
        {
            throw new InvalidProjectModelException( projectId, pomLocation,
                                                    "Parse error reading POM. Reason: " + e.getMessage(), e );
        }
    }

    private Model readModel( String projectId,
                             URL url,
                             boolean strict )
        throws ProjectBuildingException
    {
        Reader reader = null;
        try
        {
            reader = ReaderFactory.newXmlReader( url.openStream() );
            return readModel( projectId, url.toExternalForm(), reader, strict );
        }
        catch ( IOException e )
        {
            throw new ProjectBuildingException( projectId, "Failed build model from URL \'" + url.toExternalForm() +
                "\'\nError: \'" + e.getLocalizedMessage() + "\'", e );
        }
        finally
        {
            IOUtil.close( reader );
        }
    }

    private static String createCacheKey( String groupId,
                                          String artifactId,
                                          String version )
    {
        return groupId + ":" + artifactId + ":" + version;
    }

    protected Set createPluginArtifacts( String projectId,
                                         List plugins )
        throws ProjectBuildingException
    {
        Set pluginArtifacts = new LinkedHashSet();

        for ( Iterator i = plugins.iterator(); i.hasNext(); )
        {
            Plugin p = (Plugin) i.next();

            String version;
            if ( StringUtils.isEmpty( p.getVersion() ) )
            {
                version = "RELEASE";
            }
            else
            {
                version = p.getVersion();
            }

            Artifact artifact;
            try
            {
                artifact = artifactFactory.createPluginArtifact( p.getGroupId(), p.getArtifactId(),
                                                                 VersionRange.createFromVersionSpec( version ) );
            }
            catch ( InvalidVersionSpecificationException e )
            {
                throw new ProjectBuildingException( projectId, "Unable to parse version '" + version +
                    "' for plugin '" + ArtifactUtils.versionlessKey( p.getGroupId(), p.getArtifactId() ) + "': " +
                    e.getMessage(), e );
            }

            if ( artifact != null )
            {
                pluginArtifacts.add( artifact );
            }
        }

        return pluginArtifacts;
    }

    // TODO: share with createPluginArtifacts?
    protected Set createReportArtifacts( String projectId,
                                         List reports )
        throws ProjectBuildingException
    {
        Set pluginArtifacts = new LinkedHashSet();

        if ( reports != null )
        {
            for ( Iterator i = reports.iterator(); i.hasNext(); )
            {
                ReportPlugin p = (ReportPlugin) i.next();

                String version;
                if ( StringUtils.isEmpty( p.getVersion() ) )
                {
                    version = "RELEASE";
                }
                else
                {
                    version = p.getVersion();
                }

                Artifact artifact;
                try
                {
                    artifact = artifactFactory.createPluginArtifact( p.getGroupId(), p.getArtifactId(),
                                                                     VersionRange.createFromVersionSpec( version ) );
                }
                catch ( InvalidVersionSpecificationException e )
                {
                    throw new ProjectBuildingException( projectId, "Unable to parse version '" + version +
                        "' for report '" + ArtifactUtils.versionlessKey( p.getGroupId(), p.getArtifactId() ) + "': " +
                        e.getMessage(), e );
                }

                if ( artifact != null )
                {
                    pluginArtifacts.add( artifact );
                }
            }
        }

        return pluginArtifacts;
    }

    // TODO: share with createPluginArtifacts?
    protected Set createExtensionArtifacts( String projectId,
                                            List extensions )
        throws ProjectBuildingException
    {
        Set extensionArtifacts = new LinkedHashSet();

        if ( extensions != null )
        {
            for ( Iterator i = extensions.iterator(); i.hasNext(); )
            {
                Extension ext = (Extension) i.next();

                String version;
                if ( StringUtils.isEmpty( ext.getVersion() ) )
                {
                    version = "RELEASE";
                }
                else
                {
                    version = ext.getVersion();
                }

                Artifact artifact;
                try
                {
                    VersionRange versionRange = VersionRange.createFromVersionSpec( version );
                    artifact =
                        artifactFactory.createExtensionArtifact( ext.getGroupId(), ext.getArtifactId(), versionRange );
                }
                catch ( InvalidVersionSpecificationException e )
                {
                    throw new ProjectBuildingException( projectId, "Unable to parse version '" + version +
                        "' for extension '" + ArtifactUtils.versionlessKey( ext.getGroupId(), ext.getArtifactId() ) +
                        "': " + e.getMessage(), e );
                }

                if ( artifact != null )
                {
                    extensionArtifacts.add( artifact );
                }
            }
        }

        return extensionArtifacts;
    }

    // ----------------------------------------------------------------------
    //
    // ----------------------------------------------------------------------

    private Model getSuperModel()
        throws ProjectBuildingException
    {
        URL url = DefaultMavenProjectBuilder.class.getResource( "pom-" + MAVEN_MODEL_VERSION + ".xml" );

        String projectId = safeVersionlessKey( STANDALONE_SUPERPOM_GROUPID, STANDALONE_SUPERPOM_ARTIFACTID );

        return readModel( projectId, url, true );
    }

    public void contextualize( Context context )
        throws ContextException
    {
        container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
    }

    /**
     * {@inheritDoc}
     */
    public void calculateConcreteState( MavenProject project, ProjectBuilderConfiguration config )
        throws ModelInterpolationException
    {
        calculateConcreteStateInternal( project, config, true, new HashSet() );
    }
    
    /**
     * {@inheritDoc}
     */
    public void calculateConcreteState( MavenProject project, ProjectBuilderConfiguration config, boolean processProjectReferences )
        throws ModelInterpolationException
    {
        calculateConcreteStateInternal( project, config, processProjectReferences, ( processProjectReferences ? new HashSet() : null ) );
    }
    
    /*
     * NOTE: This is a code hotspot, PLEASE be careful about the performance of logic inside or
     * called from this method. 
     * 
     * NOTE: If processProjectReferences == false, processedProjects MUST NOT BE USED. It will be null.
     */
    private void calculateConcreteStateInternal( MavenProject project, ProjectBuilderConfiguration config, boolean processProjectReferences, Set processedProjects )
        throws ModelInterpolationException
    {
        if ( processProjectReferences )
        {
            processedProjects.add( project.getId() );
        }
        
        restoreDynamicStateInternal( project, config, processProjectReferences, processProjectReferences ? new HashSet( processedProjects ) : null );
        
        if ( !project.isConcrete() )
        {
            if ( project.getParent() != null )
            {
                calculateConcreteStateInternal( project.getParent(), config, processProjectReferences, processedProjects );
            }
            
            Build build = project.getBuild();
            if ( build != null )
            {
                initResourceMergeIds( build.getResources() );
                initResourceMergeIds( build.getTestResources() );
            }

            // NOTE: Since interpolation makes a copy through serialization, we don't need this.
            // See note below.
            //
            // Model model = ModelUtils.cloneModel( project.getModel() );

            File basedir = project.getBasedir();

            // NOTE: If we ever get past serialization/deserialization for interpolation, we'll need to copy the model here!
            Model model = ModelUtils.cloneModel( project.getModel() );
            model = modelInterpolator.interpolate( model, project.getBasedir(), config, getLogger().isDebugEnabled() );

            List originalInterpolatedCompileSourceRoots = interpolateListOfStrings( project.getCompileSourceRoots(),
                                                                               model,
                                                                               project.getBasedir(),
                                                                               config,
                                                                               getLogger().isDebugEnabled() );

            project.preserveCompileSourceRoots( originalInterpolatedCompileSourceRoots );

            project.setCompileSourceRoots( originalInterpolatedCompileSourceRoots == null ? null
                            : translateListOfPaths( originalInterpolatedCompileSourceRoots, basedir ) );

            List originalInterpolatedTestCompileSourceRoots = interpolateListOfStrings( project.getTestCompileSourceRoots(),
                                                                                   model,
                                                                                   project.getBasedir(),
                                                                                   config,
                                                                                   getLogger().isDebugEnabled() );

            project.preserveTestCompileSourceRoots( originalInterpolatedTestCompileSourceRoots );
            project.setTestCompileSourceRoots( originalInterpolatedTestCompileSourceRoots == null ? null
                            : translateListOfPaths( originalInterpolatedTestCompileSourceRoots, basedir ) );

            List originalInterpolatedScriptSourceRoots = interpolateListOfStrings( project.getScriptSourceRoots(),
                                                                              model,
                                                                              project.getBasedir(),
                                                                              config,
                                                                              getLogger().isDebugEnabled() );

            project.preserveScriptSourceRoots( originalInterpolatedScriptSourceRoots );
            
            // TODO: MNG-3731
            project.setScriptSourceRoots( originalInterpolatedScriptSourceRoots );
//            project.setScriptSourceRoots( originalInterpolatedScriptSourceRoots == null ? null
//                            : translateListOfPaths( originalInterpolatedScriptSourceRoots, basedir ) );

            if ( basedir != null )
            {
                pathTranslator.alignToBaseDirectory( model, basedir );
            }

            project.preserveBuild( ModelUtils.cloneBuild( model.getBuild() ) );
            project.preserveProperties();
            project.preserveBasedir();
            project.setBuild( model.getBuild() );
            
            if ( project.getExecutionProject() != null )
            {
                calculateConcreteStateInternal( project.getExecutionProject(), config, processProjectReferences, processedProjects );
            }
            
            project.setConcrete( true );
        }

        if ( processProjectReferences )
        {
            calculateConcreteProjectReferences( project, config, processedProjects );
        }
    }

    private void initResourceMergeIds( List resources )
    {
        if ( resources != null )
        {
            for ( Iterator it = resources.iterator(); it.hasNext(); )
            {
                Resource resource = (Resource) it.next();

                resource.initMergeId();
            }
        }
    }

    private void calculateConcreteProjectReferences( MavenProject project,
                                                     ProjectBuilderConfiguration config,
                                                     Set processedProjects )
        throws ModelInterpolationException
    {
        Map projectRefs = project.getProjectReferences();

        if ( projectRefs != null )
        {
            for ( Iterator it = projectRefs.values().iterator(); it.hasNext(); )
            {
                MavenProject reference = (MavenProject) it.next();
                if ( !processedProjects.contains( reference.getId() ) )
                {
                    calculateConcreteStateInternal( reference, config, true, processedProjects );
                }
            }
        }
    }

    private List translateListOfPaths( List paths, File basedir )
    {
        if ( paths == null )
        {
            return null;
        }
        else if ( basedir == null )
        {
            return paths;
        }

        List result = new ArrayList( paths.size() );
        for ( Iterator it = paths.iterator(); it.hasNext(); )
        {
            String path = (String) it.next();

            String aligned = pathTranslator.alignToBaseDirectory( path, basedir );

            result.add( aligned );
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    public void restoreDynamicState( MavenProject project, ProjectBuilderConfiguration config )
        throws ModelInterpolationException
    {
        restoreDynamicStateInternal( project, config, true, new HashSet() );
    }
    
    /**
     * {@inheritDoc}
     */
    public void restoreDynamicState( MavenProject project, ProjectBuilderConfiguration config, boolean processProjectReferences )
        throws ModelInterpolationException
    {
        restoreDynamicStateInternal( project, config, processProjectReferences, ( processProjectReferences ? new HashSet() : null ) );
    }
    
    /*
     * NOTE: This is a code hotspot, PLEASE be careful about the performance of logic inside or
     * called from this method. 
     * 
     * NOTE: If processProjectReferences == false, processedProjects MUST NOT BE USED. It will be null.
     */
    private void restoreDynamicStateInternal( MavenProject project, ProjectBuilderConfiguration config, boolean processProjectReferences, Set processedProjects )
        throws ModelInterpolationException
    {
        if ( processProjectReferences )
        {
            processedProjects.add( project.getId() );
        }
        
        if ( project.isConcrete() && projectWasChanged( project ) )
        {
            if ( project.getParent() != null )
            {
                restoreDynamicStateInternal( project.getParent(), config, processProjectReferences, processedProjects );
            }
            
            restoreBuildRoots( project, config, getLogger().isDebugEnabled() );
            restoreModelBuildSection( project, config, getLogger().isDebugEnabled() );
            
            if ( project.getExecutionProject() != null )
            {
                restoreDynamicStateInternal( project.getExecutionProject(), config, processProjectReferences, processedProjects );
            }
            
            project.setConcrete( false );
        }

        if ( processProjectReferences )
        {
            restoreDynamicProjectReferences( project, config, processedProjects );
        }
    }

    private boolean projectWasChanged( MavenProject project )
    {
        if ( !objectEquals( project.getBasedir(), project.getPreservedBasedir() ) )
        {
            return true;
        }
        
        if ( !objectEquals( project.getProperties(), project.getPreservedProperties() ) )
        {
            return true;
        }
        
        Build oBuild = project.getOriginalInterpolatedBuild();
        Build build = project.getBuild();
        
        if ( !objectEquals( oBuild.getDirectory(), build.getDirectory() ) )
        {
            return true;
        }
        
        if ( !objectEquals( oBuild.getOutputDirectory(), build.getOutputDirectory() ) )
        {
            return true;
        }
        
        if ( !objectEquals( oBuild.getSourceDirectory(), build.getSourceDirectory() ) )
        {
            return true;
        }
        
        if ( !objectEquals( oBuild.getTestSourceDirectory(), build.getTestSourceDirectory() ) )
        {
            return true;
        }
        
        if ( !objectEquals( oBuild.getScriptSourceDirectory(), build.getScriptSourceDirectory() ) )
        {
            return true;
        }
        
        return false;
    }

    private boolean objectEquals( Object obj1, Object obj2 )
    {
        return obj1 == null ? obj2 == null : obj2 != null && ( obj1 == obj2 || obj1.equals( obj2 ) );
    }

    private void propagateNewPlugins( MavenProject project )
    {
        Build changedBuild = project.getBuild();
        Build dynamicBuild = project.getDynamicBuild();
        
        if ( changedBuild == null || dynamicBuild == null )
        {
            return;
        }
        
        List changedPlugins = changedBuild.getPlugins();
        List dynamicPlugins = dynamicBuild.getPlugins();
        
        if ( changedPlugins != null && dynamicPlugins != null && changedPlugins.size() != dynamicPlugins.size() )
        {
            changedPlugins.removeAll( dynamicPlugins );
            if ( !changedPlugins.isEmpty() )
            {
                for ( Iterator it = changedPlugins.iterator(); it.hasNext(); )
                {
                    Plugin plugin = (Plugin) it.next();
                    
                    dynamicBuild.addPlugin( plugin );
                }
            }
        }
        
        dynamicBuild.flushPluginMap();
    }

    private void restoreDynamicProjectReferences( MavenProject project,
                                                  ProjectBuilderConfiguration config,
                                                  Set processedProjects )
        throws ModelInterpolationException
    {
        Map projectRefs = project.getProjectReferences();
        if ( projectRefs != null )
        {
            for ( Iterator it = projectRefs.values().iterator(); it.hasNext(); )
            {
                MavenProject projectRef = (MavenProject) it.next();
                if ( !processedProjects.contains( projectRef.getId() ) )
                {
                    restoreDynamicStateInternal( projectRef, config, true, processedProjects );
                }
            }
        }
    }

    private void restoreBuildRoots( MavenProject project,
                                    ProjectBuilderConfiguration config,
                                    boolean debugMessages )
        throws ModelInterpolationException
    {
        project.setCompileSourceRoots( restoreListOfStrings( project.getDynamicCompileSourceRoots(),
                                                             project.getOriginalInterpolatedCompileSourceRoots(),
                                                             project.getCompileSourceRoots(),
                                                             project,
                                                             config,
                                                             debugMessages ) );

        project.setTestCompileSourceRoots( restoreListOfStrings( project.getDynamicTestCompileSourceRoots(),
                                                                 project.getOriginalInterpolatedTestCompileSourceRoots(),
                                                                 project.getTestCompileSourceRoots(),
                                                                 project,
                                                                 config,
                                                                 debugMessages ) );

        project.setScriptSourceRoots( restoreListOfStrings( project.getDynamicScriptSourceRoots(),
                                                            project.getOriginalInterpolatedScriptSourceRoots(),
                                                            project.getScriptSourceRoots(),
                                                            project,
                                                            config,
                                                            debugMessages ) );

        project.clearRestorableRoots();
    }

    private void restoreModelBuildSection( MavenProject project,
                                           ProjectBuilderConfiguration config,
                                           boolean debugMessages )
        throws ModelInterpolationException
    {
        Build changedBuild = project.getBuild();
        Build dynamicBuild = project.getDynamicBuild();
        Build originalInterpolatedBuild = project.getOriginalInterpolatedBuild();

        dynamicBuild.setResources( restoreResources( dynamicBuild.getResources(),
                                                         originalInterpolatedBuild.getResources(),
                                                         changedBuild.getResources(),
                                                         project,
                                                         config,
                                                         debugMessages ) );

        dynamicBuild.setTestResources( restoreResources( dynamicBuild.getTestResources(),
                                                         originalInterpolatedBuild.getTestResources(),
                                                         changedBuild.getTestResources(),
                                                         project,
                                                         config,
                                                         debugMessages ) );

        dynamicBuild.setFilters( restoreListOfStrings( dynamicBuild.getFilters(),
                                                           originalInterpolatedBuild.getFilters(),
                                                           changedBuild.getFilters(),
                                                           project,
                                                           config,
                                                           debugMessages ) );

        dynamicBuild.setFinalName( restoreString( dynamicBuild.getFinalName(),
                                                  originalInterpolatedBuild.getFinalName(),
                                                  changedBuild.getFinalName(),
                                                  project,
                                                  config,
                                                  debugMessages ) );

        dynamicBuild.setDefaultGoal( restoreString( dynamicBuild.getDefaultGoal(),
                                                  originalInterpolatedBuild.getDefaultGoal(),
                                                  changedBuild.getDefaultGoal(),
                                                  project,
                                                  config,
                                                  debugMessages ) );

        dynamicBuild.setSourceDirectory( restoreString( dynamicBuild.getSourceDirectory(),
                                                            originalInterpolatedBuild.getSourceDirectory(),
                                                            changedBuild.getSourceDirectory(),
                                                            project,
                                                            config,
                                                            debugMessages ) );

        dynamicBuild.setTestSourceDirectory( restoreString( dynamicBuild.getTestSourceDirectory(),
                                                                originalInterpolatedBuild.getTestSourceDirectory(),
                                                                changedBuild.getTestSourceDirectory(),
                                                                project,
                                                                config,
                                                                debugMessages ) );

        dynamicBuild.setScriptSourceDirectory( restoreString( dynamicBuild.getScriptSourceDirectory(),
                                                                  originalInterpolatedBuild.getScriptSourceDirectory(),
                                                                  changedBuild.getScriptSourceDirectory(),
                                                                  project,
                                                                  config,
                                                                  debugMessages ) );

        dynamicBuild.setOutputDirectory( restoreString( dynamicBuild.getOutputDirectory(),
                                                            originalInterpolatedBuild.getOutputDirectory(),
                                                            changedBuild.getOutputDirectory(),
                                                            project,
                                                            config,
                                                            debugMessages ) );

        dynamicBuild.setTestOutputDirectory( restoreString( dynamicBuild.getTestOutputDirectory(),
                                                                originalInterpolatedBuild.getTestOutputDirectory(),
                                                                changedBuild.getTestOutputDirectory(),
                                                                project,
                                                                config,
                                                                debugMessages ) );

        dynamicBuild.setDirectory( restoreString( dynamicBuild.getDirectory(),
                                                      originalInterpolatedBuild.getDirectory(),
                                                      changedBuild.getDirectory(),
                                                      project,
                                                      config,
                                                      debugMessages ) );

        propagateNewPlugins( project );
        
        project.setBuild( dynamicBuild );

        project.clearRestorableBuild();
    }

    private List interpolateListOfStrings( List originalStrings,
                                           Model model,
                                           File projectDir,
                                           ProjectBuilderConfiguration config,
                                           boolean debugMessages )
        throws ModelInterpolationException
    {
        if ( originalStrings == null )
        {
            return null;
        }

        List result = new ArrayList();

        for ( Iterator it = originalStrings.iterator(); it.hasNext(); )
        {
            String original = (String) it.next();
            String interpolated = modelInterpolator.interpolate( original, model, projectDir, config, debugMessages );

            result.add( interpolated );
        }

        return result;
    }

    private String restoreString( String originalString,
                                      String originalInterpolatedString,
                                      String changedString,
                                      MavenProject project,
                                      ProjectBuilderConfiguration config,
                                      boolean debugMessages )
        throws ModelInterpolationException
    {
        if ( originalString == null )
        {
            return changedString;
        }
        else if ( changedString == null )
        {
            return originalString;
        }

        Model model = project.getModel();

        String relativeChangedString;
        if ( project.getBasedir() != null )
        {
            relativeChangedString = pathTranslator.unalignFromBaseDirectory( changedString, project.getBasedir() );
        }
        else
        {
            relativeChangedString = changedString;
        }

        String interpolatedOriginal = modelInterpolator.interpolate( originalString,
                                                                     model,
                                                                     project.getBasedir(),
                                                                     config,
                                                                     debugMessages );
        
        interpolatedOriginal = pathTranslator.unalignFromBaseDirectory( interpolatedOriginal, project.getBasedir() );

        String interpolatedOriginal2 = modelInterpolator.interpolate( originalInterpolatedString,
                                                                      model,
                                                                      project.getBasedir(),
                                                                      config,
                                                                      debugMessages );
        
        interpolatedOriginal2 = pathTranslator.alignToBaseDirectory( interpolatedOriginal2, project.getBasedir() );

        String interpolatedChanged = modelInterpolator.interpolate( changedString,
                                                                    model,
                                                                    project.getBasedir(),
                                                                    config,
                                                                    debugMessages );
        
        interpolatedChanged = pathTranslator.alignToBaseDirectory( interpolatedChanged, project.getBasedir() );

        String relativeInterpolatedChanged = modelInterpolator.interpolate( relativeChangedString,
                                                                            model,
                                                                            project.getBasedir(),
                                                                            config,
                                                                            debugMessages );

        if ( interpolatedOriginal.equals( interpolatedChanged ) || interpolatedOriginal2.equals( interpolatedChanged ) )
        {
            return originalString;
        }
        else if ( interpolatedOriginal.equals( relativeInterpolatedChanged )
            || interpolatedOriginal2.equals( relativeInterpolatedChanged ) )
        {
            return originalString;
        }

        return relativeChangedString;
    }

    private List restoreListOfStrings( List originalStrings,
                                           List originalInterpolatedStrings,
                                           List changedStrings,
                                           MavenProject project,
                                           ProjectBuilderConfiguration config,
                                           boolean debugMessages )
        throws ModelInterpolationException
    {
        if ( originalStrings == null )
        {
            return changedStrings;
        }
        else if ( changedStrings == null )
        {
            return originalStrings;
        }

        List result = new ArrayList();

        Map orig = new HashMap();
        for ( int idx = 0; idx < originalStrings.size(); idx++ )
        {
            String[] permutations = new String[2];

            permutations[0] = pathTranslator.alignToBaseDirectory( (String) originalInterpolatedStrings.get( idx ), project.getBasedir() );
            permutations[1] = (String) originalStrings.get( idx );

            orig.put( permutations[0], permutations );
        }

        for ( Iterator it = changedStrings.iterator(); it.hasNext(); )
        {
            String changedString = (String) it.next();
            String relativeChangedString;
            if ( project.getBasedir() != null )
            {
                relativeChangedString = pathTranslator.unalignFromBaseDirectory( changedString, project.getBasedir() );
            }
            else
            {
                relativeChangedString = changedString;
            }

            String interpolated = modelInterpolator.interpolate( changedString,
                                                                 project.getModel(),
                                                                 project.getBasedir(),
                                                                 config,
                                                                 debugMessages );
            
            interpolated = pathTranslator.alignToBaseDirectory( interpolated, project.getBasedir() );

            String relativeInterpolated = modelInterpolator.interpolate( relativeChangedString,
                                                                         project.getModel(),
                                                                         project.getBasedir(),
                                                                         config,
                                                                         debugMessages );
            
            String[] original = (String[]) orig.get( interpolated );
            if ( original == null )
            {
                original = (String[]) orig.get( relativeInterpolated );
            }

            if ( original == null )
            {
                result.add( relativeChangedString );
            }
            else
            {
                result.add( original[1] );
            }
        }

        return result;
    }

    private List restoreResources( List originalResources,
                                       List originalInterpolatedResources,
                                       List changedResources,
                                       MavenProject project,
                                       ProjectBuilderConfiguration config,
                                       boolean debugMessages )
        throws ModelInterpolationException
    {
        if ( originalResources == null || changedResources == null )
        {
            return originalResources;
        }

        List result = new ArrayList();

        Map originalResourcesByMergeId = new HashMap();
        for ( int idx = 0; idx < originalResources.size(); idx++ )
        {
            Resource[] permutations = new Resource[2];

            permutations[0] = (Resource) originalInterpolatedResources.get( idx );
            permutations[1] = (Resource) originalResources.get( idx );

            originalResourcesByMergeId.put( permutations[0].getMergeId(), permutations );
        }

        for ( Iterator it = changedResources.iterator(); it.hasNext(); )
        {
            Resource resource = (Resource) it.next();
            String mergeId = resource.getMergeId();
            if ( mergeId == null || !originalResourcesByMergeId.containsKey( mergeId ) )
            {
                result.add( resource );
            }
            else
            {
                Resource originalInterpolatedResource = ( (Resource[]) originalResourcesByMergeId.get( mergeId ) )[0];
                Resource originalResource = ( (Resource[]) originalResourcesByMergeId.get( mergeId ) )[1];

                String dir = modelInterpolator.interpolate( resource.getDirectory(), project.getModel(), project.getBasedir(), config, getLogger().isDebugEnabled() );
                String oDir = originalInterpolatedResource.getDirectory();

                if ( !dir.equals( oDir ) )
                {
                    originalResource.setDirectory( pathTranslator.unalignFromBaseDirectory( dir, project.getBasedir() ) );
                }

                if ( resource.getTargetPath() != null )
                {
                    String target = modelInterpolator.interpolate( resource.getTargetPath(), project.getModel(), project.getBasedir(), config, getLogger().isDebugEnabled() );

                    String oTarget = originalInterpolatedResource.getTargetPath();

                    if ( !target.equals( oTarget ) )
                    {
                        originalResource.setTargetPath( pathTranslator.unalignFromBaseDirectory( target, project.getBasedir() ) );
                    }
                }

                originalResource.setFiltering( resource.isFiltering() );

                originalResource.setExcludes( collectRestoredListOfPatterns( resource.getExcludes(),
                                                                             originalResource.getExcludes(),
                                                                             originalInterpolatedResource.getExcludes() ) );

                originalResource.setIncludes( collectRestoredListOfPatterns( resource.getIncludes(),
                                                                             originalResource.getIncludes(),
                                                                             originalInterpolatedResource.getIncludes() ) );

                result.add( originalResource );
            }
        }

        return result;
    }

    private List collectRestoredListOfPatterns( List patterns,
                                                        List originalPatterns,
                                                        List originalInterpolatedPatterns )
    {
        LinkedHashSet collectedPatterns = new LinkedHashSet();

        collectedPatterns.addAll( originalPatterns );

        for ( Iterator it = patterns.iterator(); it.hasNext(); )
        {
            String pattern = (String) it.next();
            if ( !originalInterpolatedPatterns.contains( pattern ) )
            {
                collectedPatterns.add( pattern );
            }
        }

        return collectedPatterns.isEmpty() ? Collections.EMPTY_LIST
                        : new ArrayList( collectedPatterns );
    }

}
