/*
* Copyright (c) 2010 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* <http://www.gnu.org/licenses>.
*/
package com.redhat.rcm.version.mgr.capture;
import static org.apache.commons.io.IOUtils.closeQuietly;
import static org.apache.commons.lang.StringUtils.isEmpty;
import static org.apache.commons.lang.StringUtils.isNotEmpty;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.maven.mae.project.key.FullProjectKey;
import org.apache.maven.mae.project.key.VersionlessProjectKey;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginManagement;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.util.WriterFactory;
import org.sonatype.aether.artifact.ArtifactType;
import org.sonatype.aether.util.version.GenericVersionScheme;
import org.sonatype.aether.version.InvalidVersionSpecificationException;
import org.sonatype.aether.version.Version;
import org.sonatype.aether.version.VersionScheme;
import com.redhat.rcm.version.Cli;
import com.redhat.rcm.version.VManException;
import com.redhat.rcm.version.mgr.session.VersionManagerSession;
import com.redhat.rcm.version.model.DependencyManagementKey;
import com.redhat.rcm.version.model.Project;
@Component( role = MissingInfoCapture.class )
public class MissingInfoCapture
{
private static final String VERSION_DATE_PATTERN = "yyyyMMdd.HHmm";
public void captureMissing( final VersionManagerSession session )
{
final Map<VersionlessProjectKey, Set<Dependency>> missingDeps = session.getMissingDependencies();
final Map<VersionlessProjectKey, Set<Plugin>> missingPlugins = session.getUnmanagedPluginRefs();
final Set<Project> missingParents = session.getProjectsWithMissingParent();
final Map<String, String> missingVersionProps = session.getMissingVersionProperties();
final boolean procDeps = notEmpty( missingDeps );
final boolean procPlugs = notEmpty( missingPlugins );
final boolean procParents = notEmpty( missingParents );
final boolean procProps = notEmpty( missingVersionProps );
if ( procProps || procDeps || procPlugs )
{
final SimpleDateFormat format = new SimpleDateFormat( VERSION_DATE_PATTERN );
final Model model = new Model();
model.setModelVersion( "4.0.0" );
model.setGroupId( Cli.class.getPackage()
.getName() );
model.setArtifactId( "vman-missing-capture" );
model.setVersion( format.format( new Date() ) );
model.setPackaging( "pom" );
boolean write = false;
write = processCurrentProjects( session, model ) || write;
if ( procProps )
{
write = processProperties( missingVersionProps, model ) || write;
}
if ( procDeps )
{
write = processDependencies( missingDeps, model ) || write;
}
if ( procPlugs )
{
write = processPlugins( missingPlugins, model ) || write;
}
if ( procParents )
{
write = processParents( missingParents, model ) || write;
}
if ( write )
{
final File capturePom = session.getCapturePom();
Writer writer = null;
try
{
final File dir = capturePom.getAbsoluteFile()
.getParentFile();
if ( dir != null && !dir.exists() )
{
dir.mkdirs();
}
writer = WriterFactory.newXmlWriter( capturePom );
new MavenXpp3Writer().write( writer, model );
}
catch ( final IOException e )
{
session.addError( new VManException( "Failed to write capture POM: %s. Reason: %s", e, capturePom,
e.getMessage() ) );
}
finally
{
closeQuietly( writer );
}
}
}
}
private boolean processProperties( final Map<String, String> props, final Model model )
{
for ( final Map.Entry<String, String> entry : props.entrySet() )
{
model.getProperties()
.setProperty( entry.getKey(), entry.getValue() );
}
return true;
}
private boolean processCurrentProjects( final VersionManagerSession session, final Model model )
{
final DependencyManagement dm = getDependencyManagement( model );
boolean changed = false;
for ( final Project project : session.getCurrentProjects() )
{
final Dependency managed =
session.getManagedDependency( new DependencyManagementKey( project.getGroupId(),
project.getArtifactId(), "pom", null ) );
if ( managed == null )
{
final Dependency dep = new Dependency();
dep.setGroupId( project.getGroupId() );
dep.setArtifactId( project.getArtifactId() );
dep.setVersion( project.getVersion() );
final ArtifactType at = session.getArtifactType( project );
if ( at != null )
{
dep.setType( at.getExtension() );
dep.setClassifier( at.getClassifier() );
}
else
{
dep.setType( project.getPackaging() );
}
dm.addDependency( dep );
changed = true;
}
}
return changed;
}
private boolean processParents( final Set<Project> missingParents, final Model model )
{
final DependencyManagement dm = getDependencyManagement( model );
final Map<FullProjectKey, Dependency> parents = new HashMap<FullProjectKey, Dependency>();
for ( final Project project : missingParents )
{
final Parent parent = project.getParent();
final FullProjectKey key = new FullProjectKey( parent );
if ( !parents.containsKey( key ) )
{
final Dependency dep = new Dependency();
dep.setGroupId( parent.getGroupId() );
dep.setArtifactId( parent.getArtifactId() );
dep.setVersion( parent.getVersion() );
parents.put( key, dep );
}
}
final Set<FullProjectKey> depKeys = new HashSet<FullProjectKey>();
for ( final Dependency dep : dm.getDependencies() )
{
depKeys.add( new FullProjectKey( dep ) );
}
boolean result = false;
for ( final Map.Entry<FullProjectKey, Dependency> entry : parents.entrySet() )
{
if ( !depKeys.contains( entry.getKey() ) )
{
dm.addDependency( entry.getValue() );
result = true;
}
}
return result;
}
private DependencyManagement getDependencyManagement( final Model model )
{
DependencyManagement dm = model.getDependencyManagement();
if ( dm == null )
{
dm = new DependencyManagement();
model.setDependencyManagement( dm );
}
return dm;
}
private boolean processPlugins( final Map<VersionlessProjectKey, Set<Plugin>> missingPlugins, final Model model )
{
final Build build = new Build();
final PluginManagement pm = new PluginManagement();
build.setPluginManagement( pm );
for ( final Map.Entry<VersionlessProjectKey, Set<Plugin>> entry : missingPlugins.entrySet() )
{
final Map<String, Set<Plugin>> mks = new HashMap<String, Set<Plugin>>();
for ( final Plugin plugin : entry.getValue() )
{
final String key = plugin.getKey();
Set<Plugin> ds = mks.get( key );
if ( ds == null )
{
ds = new HashSet<Plugin>();
mks.put( key, ds );
}
ds.add( plugin );
}
for ( final Set<Plugin> ds : mks.values() )
{
if ( ds == null || ds.isEmpty() )
{
continue;
}
final List<Plugin> pluginList = new ArrayList<Plugin>( ds );
Collections.sort( pluginList, new PluginVersionComparator() );
pm.addPlugin( pluginList.get( 0 ) );
}
}
if ( pm.getPlugins() != null && !pm.getPlugins()
.isEmpty() )
{
Collections.sort( pm.getPlugins(), new PluginArtifactIdComparator() );
model.setBuild( build );
return true;
}
return false;
}
private boolean processDependencies( final Map<VersionlessProjectKey, Set<Dependency>> missingDeps,
final Model model )
{
final DependencyManagement dm = getDependencyManagement( model );
for ( final Map.Entry<VersionlessProjectKey, Set<Dependency>> entry : missingDeps.entrySet() )
{
final Map<String, Set<Dependency>> mks = new HashMap<String, Set<Dependency>>();
for ( final Dependency dep : entry.getValue() )
{
final String key = dep.getManagementKey();
Set<Dependency> ds = mks.get( key );
if ( ds == null )
{
ds = new HashSet<Dependency>();
mks.put( key, ds );
}
ds.add( dep );
}
for ( final Set<Dependency> ds : mks.values() )
{
if ( ds == null || ds.isEmpty() )
{
continue;
}
final List<Dependency> depList = new ArrayList<Dependency>( ds );
Collections.sort( depList, new DependencyVersionComparator() );
dm.addDependency( depList.get( 0 ) );
}
}
if ( dm.getDependencies() != null && !dm.getDependencies()
.isEmpty() )
{
Collections.sort( dm.getDependencies(), new DependencyArtifactIdComparator() );
model.setDependencyManagement( dm );
return true;
}
return false;
}
private boolean notEmpty( final Map<?, ?> map )
{
return map != null && !map.isEmpty();
}
private boolean notEmpty( final Collection<?> coll )
{
return coll != null && !coll.isEmpty();
}
public static final class DependencyVersionComparator
implements Comparator<Dependency>
{
private final VersionScheme versionScheme = new GenericVersionScheme();
@Override
public int compare( final Dependency o1, final Dependency o2 )
{
int result = 0;
if ( isEmpty( o1.getVersion() ) && isNotEmpty( o2.getVersion() ) )
{
result = -1;
}
else if ( isNotEmpty( o1.getVersion() ) && isEmpty( o2.getVersion() ) )
{
result = 1;
}
else
{
Version v1 = null;
try
{
v1 = versionScheme.parseVersion( o1.getVersion() );
}
catch ( final InvalidVersionSpecificationException e )
{
result = -1;
}
if ( v1 != null )
{
try
{
final Version v2 = versionScheme.parseVersion( o2.getVersion() );
result = v1.compareTo( v2 );
}
catch ( final InvalidVersionSpecificationException e )
{
result = 1;
}
}
}
return result;
}
}
public static final class PluginVersionComparator
implements Comparator<Plugin>
{
private final VersionScheme versionScheme = new GenericVersionScheme();
@Override
public int compare( final Plugin o1, final Plugin o2 )
{
int result = 0;
if ( isEmpty( o1.getVersion() ) && isNotEmpty( o2.getVersion() ) )
{
result = -1;
}
else if ( isNotEmpty( o1.getVersion() ) && isEmpty( o2.getVersion() ) )
{
result = 1;
}
else
{
Version v1 = null;
try
{
v1 = versionScheme.parseVersion( o1.getVersion() );
}
catch ( final InvalidVersionSpecificationException e )
{
result = -1;
}
if ( v1 != null )
{
try
{
final Version v2 = versionScheme.parseVersion( o2.getVersion() );
result = v1.compareTo( v2 );
}
catch ( final InvalidVersionSpecificationException e )
{
result = 1;
}
}
}
return result;
}
}
public class DependencyArtifactIdComparator
implements Comparator<Dependency>
{
@Override
public int compare( final Dependency o1, final Dependency o2 )
{
return o1.getArtifactId()
.compareTo( o2.getArtifactId() );
}
}
public class PluginArtifactIdComparator
implements Comparator<Plugin>
{
@Override
public int compare( final Plugin o1, final Plugin o2 )
{
return o1.getArtifactId()
.compareTo( o2.getArtifactId() );
}
}
}