package net.sourceforge.javautil.classloader.resolver.impl;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.sourceforge.javautil.classloader.resolver.IClassDependency;
import net.sourceforge.javautil.classloader.resolver.IClassPackage;
import net.sourceforge.javautil.classloader.resolver.IClassPackageDescriptor;
import net.sourceforge.javautil.classloader.resolver.IClassPackageReference;
import net.sourceforge.javautil.classloader.resolver.IClassPackageRepositoryLocal;
import net.sourceforge.javautil.classloader.resolver.IClassPackageResolver;
import net.sourceforge.javautil.classloader.resolver.IClassPackage.IVersion.Type;
import net.sourceforge.javautil.classloader.source.ClassSource;
import net.sourceforge.javautil.common.CollectionUtil;
import net.sourceforge.javautil.common.StringUtil;
import net.sourceforge.javautil.common.exception.ThrowableManagerRegistry;
/**
* This is the default implementation of a {@link IClassPackage}.
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: ClassPackageImpl.java 2728 2011-01-31 00:12:30Z ponderator $
*/
public class ClassPackageImpl extends ClassPackageReferenceImpl implements IClassPackage, IClassPackageReference {
/**
* @see #isSelfPackage(String, String)
*/
public static boolean isSelfPackage (IClassPackage pkg) {
return isSelfPackage(pkg.getGroupId(), pkg.getArtifactId());
}
/**
* @param groupId The group id
* @param artifactId The artifact id
* @return True if the group and artifact refers the package that is this library, otherwise false
*/
public static boolean isSelfPackage (String groupId, String artifactId) {
return SELF_GROUPID.equals(groupId) && SELF_ARTIFACTID.equals(artifactId);
}
/**
* This will translate some of the standard forms of a version string into
* a {@link IVersion} instance.<br/><br/>
*
* <table border="0" width="90%" style="border: solid black 1px;">
* <caption style="background-color: lightgray; font-weight: bold;">Examples of valid version expresions</caption>
* <tr><th>Version String</th><th>Translation</th></tr>
* <tr><td>1.0</td><td>Stable Major: 1, Minor: 0</tr>
* <tr><td>1.0.1</td><td>Stable Major: 1, Minor: 0, Micro 1</tr>
* <tr><td>0.2</td><td>Alpha Major: 0, Minor: 2</tr>
* <tr><td>0.6</td><td>Beta Major: 0, Minor: 6</tr>
* <tr><td>1.0.2.Alpha1</td><td>Alpha Major: 1, Minor: 0, Micro: 2, Type Version: 1</tr>
* <tr><td>1.0.3.Beta3</td><td>Beta Major: 1, Minor: 0, Micro: 3, Type Version: 3</tr>
* <tr><td>1.0.4.RC2</td><td>ReleaseCandidate Major: 1, Minor: 0, Micro: 4, Type Version: 2</tr>
* <tr><td>2.1.0.GA</td><td>Stable Major: 2, Minor: 1, Micro 0</tr>
* <tr><td>2.1.0.M1</td><td>Maintenance Major: 2, Minor: 1, Micro 0, Type Version: 1</tr>
* </table>
*
* @param versionStr The version string that represents a set of version number components
* @return A {@link IVersion} implementation built from parsed information of the version string
* @throws NumberFormatException {@link IllegalArgumentException}
*/
public static IVersion decode (String versionStr) {
String[] versionParts = versionStr.split("[^0-9A-Za-z]");
while (versionParts.length > 2) {
if (!versionParts[0].matches("[0-9]+")) {
versionParts = CollectionUtil.shift(versionParts);
} else if (!versionParts[1].matches("[0-9]+")) {
versionParts = CollectionUtil.remove(versionParts, 1, 1);
} else if (!versionParts[2].matches("[0-9]+")) {
versionParts = CollectionUtil.remove(versionParts, 2, 1);
} else break;
}
int major = 0;
int minor = 0;
int micro = 0;
try {
major = detect(versionParts[0]);
} catch (NumberFormatException e) {}
if (versionParts.length >= 2)
try {
minor = detect(versionParts[1]);
} catch (NumberFormatException e) {}
if (versionParts.length >= 3)
try {
micro = versionParts.length == 2 ? -1 : detect(versionParts[2]);
} catch (NumberFormatException e) {}
Type type = major == 0 ? (minor < 5 ? Type.Alpha : Type.Beta) : Type.Stable;
if (versionParts.length < 4) return new VersionImpl(versionStr, major, minor, micro, null, type);
long typeVersion = -1;
String typeString = "";
if (versionParts[3].matches("^[0-9]+$")) {
type = Type.Build;
typeVersion = Long.parseLong(versionParts[3]);
} else {
String tv = versionParts[3].toLowerCase();
typeString = versionParts[3];
if (tv.startsWith("m")) {
type = Type.Maintenance;
if (tv.length() > 1) typeVersion = Long.parseLong( tv.substring(1) );
} else if (tv.startsWith("sp")) {
type = Type.Maintenance;
if (tv.length() > 2) typeVersion = Long.parseLong( tv.substring(2) );
} else if (tv.startsWith("ga")) {
type = Type.Stable;
if (tv.length() > 2) typeVersion = Long.parseLong( tv.substring(2) );
} else if (tv.startsWith("alpha")) {
type = Type.Alpha;
if (tv.length() > 5) typeVersion = Long.parseLong( tv.substring(5) );
} else if (tv.startsWith("beta")) {
type = Type.Beta;
if (tv.length() > 4) typeVersion = Long.parseLong( tv.substring(4) );
} else if (tv.startsWith("rc") || tv.startsWith("cr")) {
type = Type.ReleaseCandidate;
if (tv.length() > 2) typeVersion = Long.parseLong( tv.substring(2) );
} else
typeString = tv;
}
return new VersionImpl(versionStr, versionParts.length > 4 ?
StringUtil.build(CollectionUtil.shift(versionParts, 4)) : "", major, minor, micro, typeString, type, typeVersion);
}
private static int detect (String part) {
String digits = part.replaceAll("[^0-9]", "");
long number = Long.parseLong(digits);
return number > Integer.MAX_VALUE ? Integer.parseInt(digits.substring(0, 9)) : (int) number;
}
protected final IClassPackageRepositoryLocal repository;
protected final ClassSource classSource;
protected final IClassPackageDescriptor descriptor;
protected final Map<String, IClassDependency> dependencies = new LinkedHashMap<String, IClassDependency>();
protected Date buildDate = null;
protected long buildNumber = -1;
/**
* Here you can specify a package passing the unique package identifier and
* the particular version for this package.
*
* @param resolver The resolver that generated this package, or null if it is an artificial package
* @param file The file that points to the package
* @param groupId The group id of the package this represents
* @param artifactId The artifact id of the package this represents
* @param version The version of the package
*/
public ClassPackageImpl(IClassPackageRepositoryLocal repository, IClassPackageDescriptor descriptor, ClassSource classSource) {
super(descriptor);
this.classSource = new ClassPackageClassSource(classSource);
this.repository = repository;
this.descriptor = descriptor;
}
public ClassSource getMainJarSource() {
try {
return this.classSource.clone();
} catch (CloneNotSupportedException e) {
throw ThrowableManagerRegistry.caught(e);
}
}
public Date getBuildDate() { return this.buildDate; }
/**
* You can set the build date for this package here.
*
* @param buildDate The build date for this package.
*/
public void setBuildDate(Date buildDate) { this.buildDate = buildDate; }
public long getBuildNumber() { return this.buildNumber; }
/**
* You can set the build number for this package here.
*
* @param buildNumber The builder number for this package
*/
public void setBuildNumber(int buildNumber) { this.buildNumber = buildNumber; }
public List<IClassDependency> getDependencies() { return Collections.unmodifiableList( new ArrayList<IClassDependency>(dependencies.values()) ); }
public IClassPackageRepositoryLocal getRepository() { return repository; }
public IClassPackageDescriptor getDescriptor() { return this.descriptor; }
@Override public IClassPackageReference getReference() { return this.descriptor; }
public boolean equalsArtifactId(IClassPackage pkg) {
return this.groupId.equals(pkg.getGroupId()) && this.artifactId.equals(pkg.getArtifactId());
}
public boolean equalsGroupId(IClassPackage pkg) { return this.groupId.equals(pkg.getGroupId()); }
/**
* This allows on to add a dependency without the need for creating an implementation of {@link IClassDependency}.
*
* @param pkg The package to add as a dependency
* @param resolutionDepth The depth of resolution. (see {@link IClassDependency#getResolutionDepth()})
* @param type The type of dependency.
*
* @see #addDependency(IClassDependency)
*/
public void addDependency (IClassPackage pkg, int resolutionDepth, net.sourceforge.javautil.classloader.resolver.IClassDependency.Scope scope) {
this.addDependency(new ClassDependencyImpl(pkg, resolutionDepth, scope));
}
/**
* This will use the {@link IClassPackage#getUniqueId()} on {@link IClassDependency#getPackage()} to make sure that
* only one version of a particular package can be added to the collection of dependencies.
*
* @param dependency The dependency to add.
*/
public void addDependency (IClassDependency dependency) {
if (this.equalsArtifactId(dependency.getPackage()))
throw new IllegalArgumentException("A package cannot be a dependency of itself");
this.dependencies.put(dependency.getPackage().getGroupId() + ":" + dependency.getPackage().getArtifactId(), dependency);
}
public String toString () { return "Package[" + this.groupId + ":" + this.artifactId + ", version: " + this.version + "]"; }
/**
* This implementation attempts to fit the common standard version number formats and attempts to conserve
* enough original information to reproduce the same version string that was used to create the
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: ClassPackageImpl.java 2728 2011-01-31 00:12:30Z ponderator $
*/
private static class VersionImpl implements IVersion {
protected final int major;
protected final int minor;
protected final int micro;
protected final Type type;
protected final String suffix;
protected final long typeVersion;
protected final String original;
protected String typeString = null;
public VersionImpl(String original, int major, int minor) { this(original, "", major, minor, 0, null, Type.Stable, -1); }
public VersionImpl(String original, int major, int minor, int micro) { this(original, "", major, minor, micro, null, Type.Stable, -1); }
public VersionImpl(String original, int major, int minor, int micro, String typeString, Type type) { this(original, "", major, minor, micro, typeString, type, -1); }
public VersionImpl(String original, String suffix, int major, int minor, int micro, String typeString, Type type, long typeVersion) {
this.major = major;
this.minor = minor;
this.micro = micro;
this.typeString = typeString;
this.type = type;
this.typeVersion = typeVersion;
this.original = original;
this.suffix = suffix;
}
public int getMajorVersion() { return this.major; }
public int getMinorVersion() { return this.minor; }
public int getMicroVersion() { return this.micro; }
public Type getType() { return this.type; }
public long getTypeVersion() { return this.typeVersion; }
public String getSuffix() { return suffix; }
/**
* @return The type string that was used to determine the version type, or null if the version type is assumed.
*/
public String getTypeString() { return typeString; }
public int compareTo(IVersion version) {
if (version == null) return 1;
if (version.getMajorVersion() > major) return -1;
if (version.getMajorVersion() < major) return 1;
if (version.getMinorVersion() > minor) return -1;
if (version.getMinorVersion() < minor) return 1;
if (version.getMicroVersion() > micro) return -1;
if (version.getMicroVersion() < micro) return 1;
int tr = type.compareTo(version.getType());
if (tr != 0) return tr;
if (version.getTypeVersion() > typeVersion) return -1;
if (version.getTypeVersion() < typeVersion) return 1;
return 0;
}
public String toVersionString() { return original; }
public String toString () { return toVersionString(); }
@Override public boolean equals(Object obj) {
if (!(obj instanceof IVersion)) return false;
return this.compareTo( (IVersion) obj ) == 0;
}
}
}