package org.jboss.windup.rules.apps.java.archives.ignore;
import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.jboss.forge.addon.dependencies.Coordinate;
import org.jboss.forge.addon.dependencies.builder.CoordinateBuilder;
import org.jboss.forge.furnace.util.Assert;
import org.jboss.forge.furnace.versions.EmptyVersionRange;
import org.jboss.forge.furnace.versions.SingleVersion;
import org.jboss.forge.furnace.versions.SingleVersionRange;
import org.jboss.forge.furnace.versions.VersionRange;
import org.jboss.forge.furnace.versions.Versions;
import org.jboss.windup.rules.apps.java.archives.model.ArchiveCoordinateModel;
import org.jboss.windup.util.exception.WindupException;
/**
* A service class keeping the set of skipped archives and handling their lookup.
*
* Data file format: GROUP_ID:ARTIFACT_ID:VERSION_OR_RANGE[:CLASSIFIER]
*
* Examples: org.apache.commons.*:*:* org.hibernate.*:hibernate-core:(3.2,4.0]:jar
*
* @author <a href="mailto:ozizka@redhat.com">Ondrej Zizka</a>
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
public class SkippedArchives
{
private static final Map<Coordinate, VersionRange> map = new HashMap<>();
/**
* Load the given configuration file.
*/
public static void load(File file)
{
try(FileInputStream inputStream = new FileInputStream(file))
{
LineIterator it = IOUtils.lineIterator(inputStream, "UTF-8");
while (it.hasNext())
{
String line = it.next();
if (!line.startsWith("#") && !line.trim().isEmpty())
{
add(line);
}
}
}
catch (Exception e)
{
throw new WindupException("Failed loading archive ignore patterns from [" + file.toString() + "]", e);
}
}
public static void add(String line)
{
Assert.notNull(line, "Archive coordinate pattern must not be null.");
CoordinatePattern pattern = fromCoordinatePattern(line);
map.put(pattern.getCoordinate(), pattern.getVersion());
}
static CoordinatePattern fromCoordinatePattern(String coordinates)
{
String[] parts = coordinates.split("\\s*:\\s*");
if (parts.length < 3)
throw new IllegalArgumentException("Expected GAV definition format is 'GROUP_ID:ARTIFACT_ID:VERSION_OR_RANGE[:CLASSIFIER]', was: "
+ coordinates);
CoordinateBuilder coordinate = CoordinateBuilder.create()
.setGroupId(parts[0])
.setArtifactId(parts[1]);
VersionRange version = null;
if (parts[2].equals("*"))
version = new EmptyVersionRange();
// Range - (1.0,2.0] or [1.0,2.0) etc.
else if (parts[2].matches("^(\\[|\\()[^,]+(,[^,]+)?(\\]|\\))$"))
version = Versions.parseMultipleVersionRange(parts[2]);
else
version = new SingleVersionRange(new SingleVersion(parts[2]));
if (parts.length >= 4)
coordinate.setClassifier(parts[3]);
return new CoordinatePattern(coordinate, version);
}
public static boolean isSkipped(ArchiveCoordinateModel coordinate)
{
return isSkipped(CoordinateBuilder.create()
.setArtifactId(coordinate.getArtifactId())
.setGroupId(coordinate.getGroupId())
.setClassifier(coordinate.getClassifier())
.setVersion(coordinate.getVersion()));
}
/*
* Public for testing purposes
*/
public static boolean isSkipped(Coordinate coordinate)
{
for (Entry<Coordinate, VersionRange> entry : map.entrySet())
{
Coordinate pattern = entry.getKey();
VersionRange range = entry.getValue();
if (isPatternMatch(pattern.getGroupId(), coordinate.getGroupId())
&& isPatternMatch(pattern.getArtifactId(), coordinate.getArtifactId())
&& isPatternMatch(pattern.getClassifier(), coordinate.getClassifier()))
{
if (range.isEmpty() || range.includes(new SingleVersion(coordinate.getVersion())))
return true;
}
}
return false;
}
private static boolean isPatternMatch(String pattern, String value)
{
if ("*".equals(pattern))
return true;
if(pattern == value)
return true;
if (pattern != null && pattern.equals(value))
return true;
if (pattern != null && pattern.endsWith("*"))
if (value != null && value.startsWith(pattern.substring(0, pattern.length() - 1)))
return true;
return false;
}
/*
* Public for testing purposes.
*/
public static int getCount()
{
return map.size();
}
private static class CoordinatePattern
{
private final CoordinateBuilder coordinate;
private final VersionRange version;
public CoordinatePattern(CoordinateBuilder coordinate, VersionRange version)
{
this.coordinate = coordinate;
this.version = version;
}
public CoordinateBuilder getCoordinate()
{
return coordinate;
}
public VersionRange getVersion()
{
return version;
}
}
}