/*******************************************************************************
 * Copyright (c) 2010, 2014 Sonatype, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Sonatype, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.aether.internal.ant;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.maven.model.Model;
import org.apache.maven.model.building.DefaultModelBuilderFactory;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.building.FileModelSource;
import org.apache.maven.model.building.ModelBuilder;
import org.apache.maven.model.building.ModelBuildingException;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.model.resolution.ModelResolver;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuilder;
import org.apache.maven.settings.building.SettingsBuildingException;
import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
import org.apache.maven.settings.crypto.SettingsDecrypter;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.condition.Os;
import org.apache.tools.ant.types.Reference;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.ConfigurationProperties;
import org.eclipse.aether.DefaultRepositoryCache;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.CollectResult;
import org.eclipse.aether.collection.DependencyCollectionException;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.deployment.DeployRequest;
import org.eclipse.aether.deployment.DeploymentException;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.impl.RemoteRepositoryManager;
import org.eclipse.aether.installation.InstallRequest;
import org.eclipse.aether.installation.InstallationException;
import org.eclipse.aether.internal.ant.types.Artifact;
import org.eclipse.aether.internal.ant.types.Artifacts;
import org.eclipse.aether.internal.ant.types.Authentication;
import org.eclipse.aether.internal.ant.types.Dependencies;
import org.eclipse.aether.internal.ant.types.Dependency;
import org.eclipse.aether.internal.ant.types.DependencyContainer;
import org.eclipse.aether.internal.ant.types.Exclusion;
import org.eclipse.aether.internal.ant.types.LocalRepository;
import org.eclipse.aether.internal.ant.types.Mirror;
import org.eclipse.aether.internal.ant.types.Pom;
import org.eclipse.aether.internal.ant.types.Proxy;
import org.eclipse.aether.internal.ant.types.RemoteRepositories;
import org.eclipse.aether.internal.ant.types.RemoteRepository;
import org.eclipse.aether.repository.AuthenticationSelector;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.eclipse.aether.repository.MirrorSelector;
import org.eclipse.aether.repository.ProxySelector;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.spi.log.Logger;
import org.eclipse.aether.transport.classpath.ClasspathTransporterFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.util.repository.ConservativeAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultMirrorSelector;
import org.eclipse.aether.util.repository.DefaultProxySelector;

/**
 */
public class AntRepoSys
{

    private static boolean OS_WINDOWS = Os.isFamily( "windows" );

    private static final ModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();

    private static final SettingsBuilder settingsBuilder = new DefaultSettingsBuilderFactory().newInstance();

    private static final SettingsDecrypter settingsDecrypter = new AntSettingsDecryptorFactory().newInstance();

    private final Project project;

    private final DefaultServiceLocator locator;

    private RepositorySystem repoSys;

    private RemoteRepositoryManager remoteRepoMan;

    private File userSettings;

    private File globalSettings;

    private Settings settings;

    private final List<Mirror> mirrors = new CopyOnWriteArrayList<Mirror>();

    private final List<Proxy> proxies = new CopyOnWriteArrayList<Proxy>();

    private final List<Authentication> authentications = new CopyOnWriteArrayList<Authentication>();

    private LocalRepository localRepository;

    private Pom defaultPom;

    private static <T> boolean eq( T o1, T o2 )
    {
        return ( o1 == null ) ? o2 == null : o1.equals( o2 );
    }

    public static synchronized AntRepoSys getInstance( Project project )
    {
        Object obj = project.getReference( Names.ID );
        if ( obj instanceof AntRepoSys )
        {
            return (AntRepoSys) obj;
        }
        AntRepoSys instance = new AntRepoSys( project );
        project.addReference( Names.ID, instance );
        instance.initDefaults();
        return instance;
    }

    private AntRepoSys( Project project )
    {
        this.project = project;

        locator = MavenRepositorySystemUtils.newServiceLocator();
        locator.setErrorHandler( new AntServiceLocatorErrorHandler( project ) );
        locator.setServices( Logger.class, new AntLogger( project ) );
        locator.setServices( ModelBuilder.class, modelBuilder );
        locator.addService( RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class );
        locator.addService( TransporterFactory.class, FileTransporterFactory.class );
        locator.addService( TransporterFactory.class, HttpTransporterFactory.class );
        locator.addService( TransporterFactory.class, ClasspathTransporterFactory.class );
    }

    private void initDefaults()
    {
        RemoteRepository repo = new RemoteRepository();
        repo.setProject( project );
        repo.setId( "central" );
        repo.setUrl( "http://repo1.maven.org/maven2/" );
        project.addReference( Names.ID_CENTRAL, repo );

        repo = new RemoteRepository();
        repo.setProject( project );
        repo.setRefid( new Reference( project, Names.ID_CENTRAL ) );
        RemoteRepositories repos = new RemoteRepositories();
        repos.setProject( project );
        repos.addRemoterepo( repo );
        project.addReference( Names.ID_DEFAULT_REPOS, repos );
    }

    public synchronized RepositorySystem getSystem()
    {
        if ( repoSys == null )
        {
            repoSys = locator.getService( RepositorySystem.class );
            if ( repoSys == null )
            {
                throw new BuildException( "The repository system could not be initialized" );
            }
        }
        return repoSys;
    }

    private synchronized RemoteRepositoryManager getRemoteRepoMan()
    {
        if ( remoteRepoMan == null )
        {
            remoteRepoMan = locator.getService( RemoteRepositoryManager.class );
            if ( remoteRepoMan == null )
            {
                throw new BuildException( "The repository system could not be initialized" );
            }
        }
        return remoteRepoMan;
    }

    public RepositorySystemSession getSession( Task task, LocalRepository localRepo )
    {
        DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();

        Map<Object, Object> configProps = new LinkedHashMap<Object, Object>();
        configProps.put( ConfigurationProperties.USER_AGENT, getUserAgent() );
        configProps.putAll( (Map<?, ?>) project.getProperties() );
        processServerConfiguration( configProps );
        session.setConfigProperties( configProps );

        session.setOffline( isOffline() );
        session.setUserProperties( project.getUserProperties() );

        session.setProxySelector( getProxySelector() );
        session.setMirrorSelector( getMirrorSelector() );
        session.setAuthenticationSelector( getAuthSelector() );

        session.setCache( new DefaultRepositoryCache() );

        session.setRepositoryListener( new AntRepositoryListener( task ) );
        session.setTransferListener( new AntTransferListener( task ) );

        session.setLocalRepositoryManager( getLocalRepoMan( session, localRepo ) );

        session.setWorkspaceReader( ProjectWorkspaceReader.getInstance() );

        return session;
    }

    private String getUserAgent()
    {
        StringBuilder buffer = new StringBuilder( 128 );

        buffer.append( "Apache-Ant/" ).append( project.getProperty( "ant.version" ) );
        buffer.append( " (" );
        buffer.append( "Java " ).append( System.getProperty( "java.version" ) );
        buffer.append( "; " );
        buffer.append( System.getProperty( "os.name" ) ).append( " " ).append( System.getProperty( "os.version" ) );
        buffer.append( ")" );
        buffer.append( " Aether" );

        return buffer.toString();
    }

    private boolean isOffline()
    {
        String prop = project.getProperty( Names.PROPERTY_OFFLINE );
        if ( prop != null )
        {
            return Boolean.parseBoolean( prop );
        }
        return getSettings().isOffline();
    }

    private void processServerConfiguration( Map<Object, Object> configProps )
    {
        Settings settings = getSettings();
        for ( Server server : settings.getServers() )
        {
            if ( server.getConfiguration() != null )
            {
                Xpp3Dom dom = (Xpp3Dom) server.getConfiguration();
                for ( int i = dom.getChildCount() - 1; i >= 0; i-- )
                {
                    Xpp3Dom child = dom.getChild( i );
                    if ( "wagonProvider".equals( child.getName() ) )
                    {
                        dom.removeChild( i );
                    }
                    else if ( "httpHeaders".equals( child.getName() ) )
                    {
                        configProps.put( ConfigurationProperties.HTTP_HEADERS + "." + server.getId(),
                                         getHttpHeaders( child ) );
                    }
                }

                configProps.put( "aether.connector.wagon.config." + server.getId(), dom );
            }

            configProps.put( "aether.connector.perms.fileMode." + server.getId(), server.getFilePermissions() );
            configProps.put( "aether.connector.perms.dirMode." + server.getId(), server.getDirectoryPermissions() );
        }
    }

    private Map<String, String> getHttpHeaders( Xpp3Dom dom )
    {
        Map<String, String> headers = new HashMap<String, String>();
        for ( int i = 0; i < dom.getChildCount(); i++ )
        {
            Xpp3Dom child = dom.getChild( i );
            Xpp3Dom name = child.getChild( "name" );
            Xpp3Dom value = child.getChild( "value" );
            if ( name != null && name.getValue() != null )
            {
                headers.put( name.getValue(), ( value != null ) ? value.getValue() : null );
            }
        }
        return Collections.unmodifiableMap( headers );
    }

    private File getDefaultLocalRepoDir()
    {
        String dir = project.getProperty( "maven.repo.local" );
        if ( dir != null )
        {
            return project.resolveFile( dir );
        }

        Settings settings = getSettings();
        if ( settings.getLocalRepository() != null )
        {
            return new File( settings.getLocalRepository() );
        }

        return new File( new File( project.getProperty( "user.home" ), ".m2" ), "repository" );
    }

    private LocalRepositoryManager getLocalRepoMan( RepositorySystemSession session, LocalRepository localRepo )
    {
        if ( localRepo == null )
        {
            localRepo = localRepository;
        }

        File repoDir;
        if ( localRepo != null && localRepo.getDir() != null )
        {
            repoDir = localRepo.getDir();
        }
        else
        {
            repoDir = getDefaultLocalRepoDir();
        }

        org.eclipse.aether.repository.LocalRepository repo =
            new org.eclipse.aether.repository.LocalRepository( repoDir );

        return getSystem().newLocalRepositoryManager( session, repo );
    }

    private synchronized Settings getSettings()
    {
        if ( settings == null )
        {
            DefaultSettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
            request.setUserSettingsFile( getUserSettings() );
            request.setGlobalSettingsFile( getGlobalSettings() );
            request.setSystemProperties( getSystemProperties() );
            request.setUserProperties( getUserProperties() );

            try
            {
                settings = settingsBuilder.build( request ).getEffectiveSettings();
            }
            catch ( SettingsBuildingException e )
            {
                project.log( "Could not process settings.xml: " + e.getMessage(), e, Project.MSG_WARN );
            }

            SettingsDecryptionResult result =
                settingsDecrypter.decrypt( new DefaultSettingsDecryptionRequest( settings ) );
            settings.setServers( result.getServers() );
            settings.setProxies( result.getProxies() );
        }
        return settings;
    }

    private ProxySelector getProxySelector()
    {
        DefaultProxySelector selector = new DefaultProxySelector();

        for ( Proxy proxy : proxies )
        {
            selector.add( ConverterUtils.toProxy( proxy ), proxy.getNonProxyHosts() );
        }

        Settings settings = getSettings();
        for ( org.apache.maven.settings.Proxy proxy : settings.getProxies() )
        {
            AuthenticationBuilder auth = new AuthenticationBuilder();
            auth.addUsername( proxy.getUsername() ).addPassword( proxy.getPassword() );
            selector.add( new org.eclipse.aether.repository.Proxy( proxy.getProtocol(), proxy.getHost(),
                                                                   proxy.getPort(), auth.build() ),
                          proxy.getNonProxyHosts() );
        }

        return selector;
    }

    private MirrorSelector getMirrorSelector()
    {
        DefaultMirrorSelector selector = new DefaultMirrorSelector();

        for ( Mirror mirror : mirrors )
        {
            selector.add( mirror.getId(), mirror.getUrl(), mirror.getType(), false, mirror.getMirrorOf(), null );
        }

        Settings settings = getSettings();
        for ( org.apache.maven.settings.Mirror mirror : settings.getMirrors() )
        {
            selector.add( String.valueOf( mirror.getId() ), mirror.getUrl(), mirror.getLayout(), false,
                          mirror.getMirrorOf(), mirror.getMirrorOfLayouts() );
        }

        return selector;
    }

    private AuthenticationSelector getAuthSelector()
    {
        DefaultAuthenticationSelector selector = new DefaultAuthenticationSelector();

        Collection<String> ids = new HashSet<String>();
        for ( Authentication auth : authentications )
        {
            List<String> servers = auth.getServers();
            if ( !servers.isEmpty() )
            {
                org.eclipse.aether.repository.Authentication a = ConverterUtils.toAuthentication( auth );
                for ( String server : servers )
                {
                    if ( ids.add( server ) )
                    {
                        selector.add( server, a );
                    }
                }
            }
        }

        Settings settings = getSettings();
        for ( Server server : settings.getServers() )
        {
            AuthenticationBuilder auth = new AuthenticationBuilder();
            auth.addUsername( server.getUsername() ).addPassword( server.getPassword() );
            auth.addPrivateKey( server.getPrivateKey(), server.getPassphrase() );
            selector.add( server.getId(), auth.build() );
        }

        return new ConservativeAuthenticationSelector( selector );
    }

    public synchronized void setUserSettings( File file )
    {
        if ( !eq( this.userSettings, file ) )
        {
            settings = null;
        }
        this.userSettings = file;
    }

    /* UT */File getUserSettings()
    {
        if ( userSettings == null )
        {
            userSettings = AetherUtils.findUserSettings( project );
        }
        return userSettings;
    }

    public void setGlobalSettings( File file )
    {
        if ( !eq( this.globalSettings, file ) )
        {
            settings = null;
        }
        this.globalSettings = file;
    }

    /* UT */File getGlobalSettings()
    {
        if ( globalSettings == null )
        {
            globalSettings = AetherUtils.findGlobalSettings( project );
        }
        return globalSettings;
    }

    public void addProxy( Proxy proxy )
    {
        proxies.add( proxy );
    }

    public void addMirror( Mirror mirror )
    {
        mirrors.add( mirror );
    }

    public void addAuthentication( Authentication authentication )
    {
        authentications.add( authentication );
    }

    public void setLocalRepository( LocalRepository localRepository )
    {
        this.localRepository = localRepository;
    }

    public Model loadModel( Task task, File pomFile, boolean local, RemoteRepositories remoteRepositories )
    {
        RepositorySystemSession session = getSession( task, null );

        remoteRepositories =
            remoteRepositories == null ? AetherUtils.getDefaultRepositories( project ) : remoteRepositories;

        List<org.eclipse.aether.repository.RemoteRepository> repositories =
            ConverterUtils.toRepositories( task.getProject(), session, remoteRepositories, getRemoteRepoMan() );

        ModelResolver modelResolver =
            new AntModelResolver( session, "project", getSystem(), getRemoteRepoMan(), repositories );

        Settings settings = getSettings();

        try
        {
            DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
            request.setLocationTracking( true );
            request.setProcessPlugins( false );
            if ( local )
            {
                request.setPomFile( pomFile );
                request.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_STRICT );
            }
            else
            {
                request.setModelSource( new FileModelSource( pomFile ) );
                request.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
            }
            request.setSystemProperties( getSystemProperties() );
            request.setUserProperties( getUserProperties() );
            request.setProfiles( SettingsUtils.convert( settings.getProfiles() ) );
            request.setActiveProfileIds( settings.getActiveProfiles() );
            request.setModelResolver( modelResolver );
            return modelBuilder.build( request ).getEffectiveModel();
        }
        catch ( ModelBuildingException e )
        {
            throw new BuildException( "Could not load POM " + pomFile + ": " + e.getMessage(), e );
        }
    }

    private Properties getSystemProperties()
    {
        Properties props = new Properties();
        getEnvProperties( props );
        props.putAll( System.getProperties() );
        ConverterUtils.addProperties( props, project.getProperties() );
        return props;
    }

    private Properties getEnvProperties( Properties props )
    {
        if ( props == null )
        {
            props = new Properties();
        }
        boolean envCaseInsensitive = OS_WINDOWS;
        for ( Map.Entry<String, String> entry : System.getenv().entrySet() )
        {
            String key = entry.getKey();
            if ( envCaseInsensitive )
            {
                key = key.toUpperCase( Locale.ENGLISH );
            }
            key = "env." + key;
            props.put( key, entry.getValue() );
        }
        return props;
    }

    private Properties getUserProperties()
    {
        return ConverterUtils.addProperties( null, project.getUserProperties() );
    }

    /**
     * Sets the default POM.
     */
    public void setDefaultPom( Pom pom )
    {
        this.defaultPom = pom;
    }

    /**
     * Returns the current default POM.
     */
    public Pom getDefaultPom()
    {
        return defaultPom;
    }

    public CollectResult collectDependencies( Task task, Dependencies dependencies, LocalRepository localRepository,
                                              RemoteRepositories remoteRepositories )
    {
        RepositorySystemSession session = getSession( task, localRepository );

        remoteRepositories =
            remoteRepositories == null ? AetherUtils.getDefaultRepositories( project ) : remoteRepositories;

        List<org.eclipse.aether.repository.RemoteRepository> repos =
            ConverterUtils.toRepositories( project, session, remoteRepositories, getRemoteRepoMan() );

        CollectRequest collectRequest = new CollectRequest();
        collectRequest.setRequestContext( "project" );

        for ( org.eclipse.aether.repository.RemoteRepository repo : repos )
        {
            task.getProject().log( "Using remote repository " + repo, Project.MSG_VERBOSE );
            collectRequest.addRepository( repo );
        }

        if ( dependencies != null )
        {
            populateCollectRequest( collectRequest, task, session, dependencies, Collections.<Exclusion> emptyList() );
        }

        task.getProject().log( "Collecting dependencies", Project.MSG_VERBOSE );

        CollectResult result;
        try
        {
            result = getSystem().collectDependencies( session, collectRequest );
        }
        catch ( DependencyCollectionException e )
        {
            throw new BuildException( "Could not collect dependencies: " + e.getMessage(), e );
        }

        return result;
    }

    private void populateCollectRequest( CollectRequest collectRequest, Task task, RepositorySystemSession session,
                                         Dependencies dependencies, List<Exclusion> exclusions )
    {
        List<Exclusion> globalExclusions = exclusions;
        if ( !dependencies.getExclusions().isEmpty() )
        {
            globalExclusions = new ArrayList<Exclusion>( exclusions );
            globalExclusions.addAll( dependencies.getExclusions() );
        }

        Collection<String> ids = new HashSet<String>();

        for ( DependencyContainer container : dependencies.getDependencyContainers() )
        {
            if ( container instanceof Dependency )
            {
                Dependency dep = (Dependency) container;
                ids.add( dep.getVersionlessKey() );
                collectRequest.addDependency( ConverterUtils.toDependency( dep, globalExclusions, session ) );
            }
            else
            {
                populateCollectRequest( collectRequest, task, session, (Dependencies) container, globalExclusions );
            }
        }

        if ( dependencies.getPom() != null )
        {
            Model model = dependencies.getPom().getModel( task );
            for ( org.apache.maven.model.Dependency dep : model.getDependencies() )
            {
                Dependency dependency = new Dependency();
                dependency.setArtifactId( dep.getArtifactId() );
                dependency.setClassifier( dep.getClassifier() );
                dependency.setGroupId( dep.getGroupId() );
                dependency.setScope( dep.getScope() );
                dependency.setType( dep.getType() );
                dependency.setVersion( dep.getVersion() );
                if ( ids.contains( dependency.getVersionlessKey() ) )
                {
                    project.log( "Ignoring dependency " + dependency.getVersionlessKey() + " from " + model.getId()
                        + ", already declared locally", Project.MSG_VERBOSE );
                    continue;
                }
                if ( dep.getSystemPath() != null && dep.getSystemPath().length() > 0 )
                {
                    dependency.setSystemPath( task.getProject().resolveFile( dep.getSystemPath() ) );
                }
                for ( org.apache.maven.model.Exclusion exc : dep.getExclusions() )
                {
                    Exclusion exclusion = new Exclusion();
                    exclusion.setGroupId( exc.getGroupId() );
                    exclusion.setArtifactId( exc.getArtifactId() );
                    exclusion.setClassifier( "*" );
                    exclusion.setExtension( "*" );
                    dependency.addExclusion( exclusion );
                }
                collectRequest.addDependency( ConverterUtils.toDependency( dependency, globalExclusions, session ) );
            }
        }

        if ( dependencies.getFile() != null )
        {
            List<Dependency> deps = readDependencies( dependencies.getFile() );
            for ( Dependency dependency : deps )
            {
                if ( ids.contains( dependency.getVersionlessKey() ) )
                {
                    project.log( "Ignoring dependency " + dependency.getVersionlessKey() + " from "
                                     + dependencies.getFile() + ", already declared locally", Project.MSG_VERBOSE );
                    continue;
                }
                collectRequest.addDependency( ConverterUtils.toDependency( dependency, globalExclusions, session ) );
            }
        }
    }

    private List<Dependency> readDependencies( File file )
    {
        List<Dependency> dependencies = new ArrayList<Dependency>();
        try
        {
            BufferedReader reader = new BufferedReader( new InputStreamReader( new FileInputStream( file ), "UTF-8" ) );
            try
            {
                for ( String line = reader.readLine(); line != null; line = reader.readLine() )
                {
                    int comment = line.indexOf( '#' );
                    if ( comment >= 0 )
                    {
                        line = line.substring( 0, comment );
                    }
                    line = line.trim();
                    if ( line.length() <= 0 )
                    {
                        continue;
                    }
                    Dependency dependency = new Dependency();
                    dependency.setCoords( line );
                    dependencies.add( dependency );
                }
            }
            finally
            {
                reader.close();
            }
        }
        catch ( IOException e )
        {
            throw new BuildException( "Cannot read " + file, e );
        }
        return dependencies;
    }

    public void install( Task task, Pom pom, Artifacts artifacts )
    {
        RepositorySystemSession session = getSession( task, null );

        InstallRequest request = new InstallRequest();
        request.setArtifacts( toArtifacts( task, session, pom, artifacts ) );

        try
        {
            getSystem().install( session, request );
        }
        catch ( InstallationException e )
        {
            throw new BuildException( "Could not install artifacts: " + e.getMessage(), e );
        }
    }

    public void deploy( Task task, Pom pom, Artifacts artifacts, RemoteRepository releaseRepository,
                        RemoteRepository snapshotRepository )
    {
        RepositorySystemSession session = getSession( task, null );

        DeployRequest request = new DeployRequest();
        request.setArtifacts( toArtifacts( task, session, pom, artifacts ) );
        boolean snapshot = request.getArtifacts().iterator().next().isSnapshot();
        RemoteRepository distRepo = ( snapshot && snapshotRepository != null ) ? snapshotRepository : releaseRepository;
        request.setRepository( ConverterUtils.toDistRepository( distRepo, session ) );

        try
        {
            getSystem().deploy( session, request );
        }
        catch ( DeploymentException e )
        {
            throw new BuildException( "Could not deploy artifacts: " + e.getMessage(), e );
        }
    }

    private List<org.eclipse.aether.artifact.Artifact> toArtifacts( Task task, RepositorySystemSession session,
                                                                    Pom pom, Artifacts artifacts )
    {
        Model model = pom.getModel( task );
        File pomFile = pom.getFile();

        List<org.eclipse.aether.artifact.Artifact> results = new ArrayList<org.eclipse.aether.artifact.Artifact>();

        org.eclipse.aether.artifact.Artifact pomArtifact =
            new DefaultArtifact( model.getGroupId(), model.getArtifactId(), "pom", model.getVersion() ).setFile( pomFile );
        results.add( pomArtifact );

        for ( Artifact artifact : artifacts.getArtifacts() )
        {
            org.eclipse.aether.artifact.Artifact buildArtifact =
                new DefaultArtifact( model.getGroupId(), model.getArtifactId(), artifact.getClassifier(),
                                     artifact.getType(), model.getVersion() ).setFile( artifact.getFile() );
            results.add( buildArtifact );
        }

        return results;
    }

}
