Add in the ability to create versioned dependencies for mod relationships

BaseMod versions are loaded too late so will never work as a versioned
dependency requirement, but can have versioned deps of others
This commit is contained in:
Christian 2012-07-30 17:01:27 -04:00
parent d1d12e4ebd
commit e001103ed8
17 changed files with 1573 additions and 72 deletions

View File

@ -3,6 +3,12 @@ under the GNU LGPL v2.1 or later.
Homepage: https://github.com/cpw/FML
This software includes portions from the Apache Maven project at
http://maven.apache.org/ specifically the ComparableVersion.java code. It is
included based on guidelines at
http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html
with notices intact. The only change is a non-functional change of package name.
========
GNU LESSER GENERAL PUBLIC LICENSE

View File

@ -9,16 +9,17 @@ import com.google.common.eventbus.EventBus;
import cpw.mods.fml.common.LoaderState.ModState;
import cpw.mods.fml.common.discovery.ContainerType;
import cpw.mods.fml.common.versioning.ArtifactVersion;
public class DummyModContainer implements ModContainer
{
private ModMetadata md;
public DummyModContainer(ModMetadata md)
{
this.md = md;
}
public DummyModContainer()
{
}
@ -35,19 +36,19 @@ public class DummyModContainer implements ModContainer
}
@Override
public List<String> getDependants()
public List<ArtifactVersion> getDependants()
{
return Collections.emptyList();
}
@Override
public List<String> getDependencies()
public List<ArtifactVersion> getDependencies()
{
return Collections.emptyList();
}
@Override
public List<String> getRequirements()
public List<ArtifactVersion> getRequirements()
{
return Collections.emptyList();
}
@ -110,4 +111,10 @@ public class DummyModContainer implements ModContainer
return false;
}
@Override
public ArtifactVersion getProcessedVersion()
{
return null;
}
}

View File

@ -39,6 +39,8 @@ import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.FMLRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.versioning.ArtifactVersion;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
public class FMLModContainer implements ModContainer
{
@ -47,16 +49,16 @@ public class FMLModContainer implements ModContainer
private File source;
private ModMetadata modMetadata;
private String className;
private String modId;
private Map<String, Object> descriptor;
private boolean enabled = true;
private List<String> requirements;
private List<String> dependencies;
private List<String> dependants;
private List<ArtifactVersion> requirements;
private List<ArtifactVersion> dependencies;
private List<ArtifactVersion> dependants;
private boolean overridesMetadata;
private EventBus eventBus;
private LoadController controller;
private Multimap<Class<? extends Annotation>, Object> annotations;
private DefaultArtifactVersion processedVersion;
public FMLModContainer(String className, File modSource, Map<String,Object> modDescriptor)
{
@ -107,9 +109,9 @@ public class FMLModContainer implements ModContainer
if (overridesMetadata || !modMetadata.useDependencyInformation)
{
List<String> requirements = Lists.newArrayList();
List<String> dependencies = Lists.newArrayList();
List<String> dependants = Lists.newArrayList();
List<ArtifactVersion> requirements = Lists.newArrayList();
List<ArtifactVersion> dependencies = Lists.newArrayList();
List<ArtifactVersion> dependants = Lists.newArrayList();
Loader.instance().computeDependencies((String) descriptor.get("dependencies"), requirements, dependencies, dependants);
modMetadata.requiredMods = requirements;
modMetadata.dependencies = dependencies;
@ -124,19 +126,19 @@ public class FMLModContainer implements ModContainer
}
@Override
public List<String> getRequirements()
public List<ArtifactVersion> getRequirements()
{
return modMetadata.requiredMods;
}
@Override
public List<String> getDependencies()
public List<ArtifactVersion> getDependencies()
{
return modMetadata.dependencies;
}
@Override
public List<String> getDependants()
public List<ArtifactVersion> getDependants()
{
return modMetadata.dependants;
}
@ -325,4 +327,14 @@ public class FMLModContainer implements ModContainer
Throwables.propagateIfPossible(t);
}
}
@Override
public ArtifactVersion getProcessedVersion()
{
if (processedVersion == null)
{
processedVersion = new DefaultArtifactVersion(getModId(), getVersion());
}
return processedVersion;
}
}

View File

@ -49,6 +49,8 @@ import cpw.mods.fml.common.functions.ModIdFunction;
import cpw.mods.fml.common.toposort.ModSorter;
import cpw.mods.fml.common.toposort.ModSortingException;
import cpw.mods.fml.common.toposort.TopologicalSort;
import cpw.mods.fml.common.versioning.ArtifactVersion;
import cpw.mods.fml.common.versioning.VersionParser;
/**
* The loader class performs the actual loading of the mod code from disk.
@ -180,12 +182,25 @@ public class Loader
FMLLog.fine("Verifying mod requirements are satisfied");
try
{
Map<String, ArtifactVersion> modVersions = Maps.newHashMap();
for (ModContainer mod : mods)
{
if (!namedMods.keySet().containsAll(mod.getRequirements()))
modVersions.put(mod.getModId(), mod.getProcessedVersion());
}
for (ModContainer mod : mods)
{
ImmutableList<ArtifactVersion> allDeps = ImmutableList.<ArtifactVersion>builder().addAll(mod.getDependants()).addAll(mod.getDependencies()).build();
for (ArtifactVersion v : allDeps)
{
FMLLog.log(Level.SEVERE, "The mod %s (%s) requires mods %s to be available, one or more are not", mod.getModId(), mod.getName(), mod.getRequirements());
throw new LoaderException();
if (modVersions.containsKey(v.getLabel()))
{
if (!v.containsVersion(modVersions.get(v.getLabel())))
{
FMLLog.log(Level.SEVERE, "The mod %s (%s) requires mods %s to be available, one or more are not", mod.getModId(), mod.getName(), allDeps);
throw new LoaderException();
}
}
}
}
@ -340,11 +355,11 @@ public class Loader
mods = new ArrayList<ModContainer>();
namedMods = new HashMap<String, ModContainer>();
state = LoaderState.LOADING;
modController = new LoadController(this);
identifyMods();
disableRequestedMods();
sortModList();
mods = ImmutableList.copyOf(mods);
modController = new LoadController(this);
// Mod controller state : CONSTRUCTION
modController.distributeStateMessage(modClassLoader);
modController.transition(LoaderState.PREINITIALIZATION);
@ -448,7 +463,7 @@ public class Loader
return modClassLoader;
}
public void computeDependencies(String dependencyString, List<String> requirements, List<String> dependencies, List<String> dependants)
public void computeDependencies(String dependencyString, List<ArtifactVersion> requirements, List<ArtifactVersion> dependencies, List<ArtifactVersion> dependants)
{
if (dependencyString == null || dependencyString.length() == 0)
{
@ -460,6 +475,7 @@ public class Loader
for (String dep : DEPENDENCYSPLITTER.split(dependencyString))
{
List<String> depparts = Lists.newArrayList(DEPENDENCYPARTSPLITTER.split(dep));
// Need two parts to the string
if (depparts.size() != 2)
{
parseFailure=true;
@ -467,8 +483,14 @@ public class Loader
}
String instruction = depparts.get(0);
String target = depparts.get(1);
boolean targetIsAll = target.equals("*");
// If we don't have two parts to each substring, this is an invalid dependency string
boolean targetIsAll = target.startsWith("*");
// Cannot have an "all" relationship with anything except pure *
if (targetIsAll && target.length()>1)
{
parseFailure = true;
continue;
}
// If this is a required element, add it to the required list
if ("required-before".equals(instruction) || "required-after".equals(instruction))
@ -476,7 +498,7 @@ public class Loader
// You can't require everything
if (!targetIsAll)
{
requirements.add(target);
requirements.add(VersionParser.parseVersionReference(target));
}
else
{
@ -485,15 +507,21 @@ public class Loader
}
}
// You cannot have a versioned dependency on everything
if (targetIsAll && target.indexOf('@')>-1)
{
parseFailure = true;
continue;
}
// before elements are things we are loaded before (so they are our dependants)
if ("required-before".equals(instruction) || "before".equals(instruction))
{
dependants.add(target);
dependants.add(VersionParser.parseVersionReference(target));
}
// after elements are things that load before we do (so they are out dependencies)
else if ("required-after".equals(instruction) || "after".equals(instruction))
{
dependencies.add(target);
dependencies.add(VersionParser.parseVersionReference(target));
}
else
{

View File

@ -19,6 +19,7 @@ import java.util.List;
import com.google.common.eventbus.EventBus;
import cpw.mods.fml.common.LoaderState.ModState;
import cpw.mods.fml.common.versioning.ArtifactVersion;
/**
* The container that wraps around mods in the system.
@ -28,23 +29,23 @@ import cpw.mods.fml.common.LoaderState.ModState;
* a mechanism by which we can wrap actual mod code so that the loader and other
* facilities can treat mods at arms length.
* </p>
*
*
* @author cpw
*
*
*/
public interface ModContainer
{
/**
* The globally unique modid for this mod
*
*
* @return
*/
String getModId();
/**
* A human readable name
*
*
* @return
*/
@ -52,86 +53,86 @@ public interface ModContainer
/**
* A human readable version identifier
*
*
* @return
*/
String getVersion();
/**
* The location on the file system which this mod came from
*
*
* @return
*/
File getSource();
/**
* The metadata for this mod
*
*
* @return
*/
ModMetadata getMetadata();
/**
* Attach this mod to it's metadata from the supplied metadata collection
*
*
* @param mc
*/
void bindMetadata(MetadataCollection mc);
/**
* Set the enabled/disabled state of this mod
*
*
* @param enabled
*/
void setEnabledState(boolean enabled);
/**
* A list of the modids that this mod requires loaded prior to loading
*
*
* @return
*/
List<String> getRequirements();
List<ArtifactVersion> getRequirements();
/**
* A list of modids that should be loaded prior to this one. The special
* value <strong>*</strong> indicates to load <em>before</em> any other mod.
*
*
* @return
*/
List<String> getDependencies();
List<ArtifactVersion> getDependencies();
/**
* A list of modids that should be loaded <em>after</em> this one. The
* special value <strong>*</strong> indicates to load <em>after</em> any
* other mod.
*
*
* @return
*/
List<String> getDependants();
List<ArtifactVersion> getDependants();
/**
* A representative string encapsulating the sorting preferences for this
* mod
*
*
* @return
*/
String getSortingRules();
/**
* Register the event bus for the mod and the controller for error handling
* Returns if this bus was successfully registered - disabled mods and other
* Returns if this bus was successfully registered - disabled mods and other
* mods that don't need real events should return false and avoid further
* processing
*
*
* @param bus
* @param controller
* @return
* @return
*/
boolean registerBus(EventBus bus, LoadController controller);
/**
* Does this mod match the supplied mod
*
*
* @param mod
* @return
*/
@ -139,10 +140,12 @@ public interface ModContainer
/**
* Get the actual mod object
*
*
* @return
*/
Object getMod();
ArtifactVersion getProcessedVersion();
ProxyInjector findSidedProxy();
}

View File

@ -24,8 +24,8 @@ public class ModContainerFactory
FMLLog.fine("Identified a BaseMod type mod %s", className);
return new ModLoaderModContainer(className, modSource, modParser.getBaseModProperties());
}
for (ModAnnotation ann : modParser.getAnnotations())
for (ModAnnotation ann : modParser.getAnnotations())
{
if (ann.getASMType().equals(Type.getType(Mod.class)))
{
@ -33,7 +33,7 @@ public class ModContainerFactory
return new FMLModContainer(className, modSource, ann.getValues());
}
}
return null;
}
}

View File

@ -36,12 +36,14 @@ import com.google.common.collect.Maps;
import cpw.mods.fml.common.functions.ModNameFunction;
import cpw.mods.fml.common.modloader.ModLoaderModContainer;
import cpw.mods.fml.common.versioning.ArtifactVersion;
import cpw.mods.fml.common.versioning.VersionParser;
import static argo.jdom.JsonNodeBuilders.*;
/**
* @author cpw
*
*
*/
public class ModMetadata
{
@ -86,9 +88,9 @@ public class ModMetadata
public List<ModContainer> childMods = Lists.newArrayList();
public boolean useDependencyInformation;
public List<String> requiredMods = Lists.newArrayList();
public List<String> dependencies = Lists.newArrayList();
public List<String> dependants = Lists.newArrayList();
public List<ArtifactVersion> requiredMods;
public List<ArtifactVersion> dependencies;
public List<ArtifactVersion> dependants;
public boolean autogenerated;
/**
@ -108,9 +110,9 @@ public class ModMetadata
credits = Strings.nullToEmpty((String)processedFields.get(aStringBuilder("credits")));
parent = Strings.nullToEmpty((String)processedFields.get(aStringBuilder("parent")));
authorList = Optional.fromNullable((List<String>)processedFields.get(aStringBuilder("authorList"))).or(authorList);
requiredMods = Optional.fromNullable((List<String>)processedFields.get(aStringBuilder("requiredMods"))).or(requiredMods);
dependencies = Optional.fromNullable((List<String>)processedFields.get(aStringBuilder("dependencies"))).or(dependencies);
dependants = Optional.fromNullable((List<String>)processedFields.get(aStringBuilder("dependants"))).or(dependants);
requiredMods = processReferences((List<String>)processedFields.get(aStringBuilder("requiredMods")));
dependencies = processReferences((List<String>)processedFields.get(aStringBuilder("dependencies")));
dependants = processReferences((List<String>)processedFields.get(aStringBuilder("dependants")));
useDependencyInformation = Boolean.parseBoolean(Strings.nullToEmpty((String)processedFields.get(aStringBuilder("useDependencyInformation"))));
}
@ -118,6 +120,19 @@ public class ModMetadata
{
}
private List<ArtifactVersion> processReferences(List<String> refs)
{
List<ArtifactVersion> res = Lists.newArrayList();
if (refs == null)
{
return res;
}
for (String ref : refs)
{
res.add(VersionParser.parseVersionReference(ref));
}
return res;
}
/**
* @return
*/

View File

@ -61,6 +61,8 @@ import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.TickRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.versioning.ArtifactVersion;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.ModMetadata;
import cpw.mods.fml.common.ProxyInjector;
@ -71,9 +73,9 @@ public class ModLoaderModContainer implements ModContainer
private static final ProxyInjector NULLPROXY = new ProxyInjector("","","",null);
public BaseMod mod;
private File modSource;
public ArrayList<String> requirements = Lists.newArrayList();
public ArrayList<String> dependencies = Lists.newArrayList();
public ArrayList<String> dependants = Lists.newArrayList();
public List<ArtifactVersion> requirements = Lists.newArrayList();
public ArrayList<ArtifactVersion> dependencies = Lists.newArrayList();
public ArrayList<ArtifactVersion> dependants = Lists.newArrayList();
private ContainerType sourceType;
private ModMetadata metadata;
private ProxyInjector sidedProxy;
@ -85,6 +87,7 @@ public class ModLoaderModContainer implements ModContainer
private LoadController controller;
private boolean enabled = true;
private String sortingProperties;
private ArtifactVersion processedVersion;
public ModLoaderModContainer(String className, File modSource, String sortingProperties)
{
@ -372,19 +375,19 @@ public class ModLoaderModContainer implements ModContainer
}
@Override
public List<String> getRequirements()
public List<ArtifactVersion> getRequirements()
{
return requirements;
}
@Override
public List<String> getDependants()
public List<ArtifactVersion> getDependants()
{
return dependants;
}
@Override
public List<String> getDependencies()
public List<ArtifactVersion> getDependencies()
{
return dependencies;
}
@ -545,4 +548,14 @@ public class ModLoaderModContainer implements ModContainer
Throwables.propagateIfPossible(t);
}
}
@Override
public ArtifactVersion getProcessedVersion()
{
if (processedVersion == null)
{
processedVersion = new DefaultArtifactVersion(modId, getVersion());
}
return processedVersion;
}
}

View File

@ -20,6 +20,7 @@ import cpw.mods.fml.common.DummyModContainer;
import cpw.mods.fml.common.FMLModContainer;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.toposort.TopologicalSort.DirectedGraph;
import cpw.mods.fml.common.versioning.ArtifactVersion;
/**
* @author cpw
@ -60,11 +61,11 @@ public class ModSorter
boolean preDepAdded = false;
boolean postDepAdded = false;
for (String dep : mod.getDependencies())
for (ArtifactVersion dep : mod.getDependencies())
{
preDepAdded = true;
if (dep.equals("*"))
if (dep.getLabel().equals("*"))
{
// We are "after" everything
modGraph.addEdge(mod, afterAll);
@ -74,17 +75,17 @@ public class ModSorter
else
{
modGraph.addEdge(before, mod);
if (nameLookup.containsKey(dep)) {
modGraph.addEdge(nameLookup.get(dep), mod);
if (nameLookup.containsKey(dep.getLabel())) {
modGraph.addEdge(nameLookup.get(dep.getLabel()), mod);
}
}
}
for (String dep : mod.getDependants())
for (ArtifactVersion dep : mod.getDependants())
{
postDepAdded = true;
if (dep.equals("*"))
if (dep.getLabel().equals("*"))
{
// We are "before" everything
modGraph.addEdge(beforeAll, mod);
@ -94,8 +95,8 @@ public class ModSorter
else
{
modGraph.addEdge(mod, after);
if (nameLookup.containsKey(dep)) {
modGraph.addEdge(mod, nameLookup.get(dep));
if (nameLookup.containsKey(dep.getLabel())) {
modGraph.addEdge(mod, nameLookup.get(dep.getLabel()));
}
}
}

View File

@ -0,0 +1,34 @@
package cpw.mods.fml.common.versioning;
/*
* 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.
*/
/**
* Describes an artifact version in terms of its components, converts it to/from a string and
* compares two versions.
*
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
*/
public interface ArtifactVersion
extends Comparable<ArtifactVersion>
{
String getLabel();
boolean containsVersion(ArtifactVersion source);
}

View File

@ -0,0 +1,467 @@
package cpw.mods.fml.common.versioning;
/*
* 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 java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Properties;
import java.util.Stack;
/**
* Generic implementation of version comparison.
*
* <p>Features:
* <ul>
* <li>mixing of '<code>-</code>' (dash) and '<code>.</code>' (dot) separators,</li>
* <li>transition between characters and digits also constitutes a separator:
* <code>1.0alpha1 =&gt; [1, 0, alpha, 1]</code></li>
* <li>unlimited number of version components,</li>
* <li>version components in the text can be digits or strings,</li>
* <li>strings are checked for well-known qualifiers and the qualifier ordering is used for version ordering.
* Well-known qualifiers (case insensitive) are:<ul>
* <li><code>snapshot</code></li>
* <li><code>alpha</code> or <code>a</code></li>
* <li><code>beta</code> or <code>b</code></li>
* <li><code>milestone</code> or <code>m</code></li>
* <li><code>rc</code> or <code>cr</code></li>
* <li><code>(the empty string)</code> or <code>ga</code> or <code>final</code></li>
* <li><code>sp</code></li>
* </ul>
* Unknown qualifiers are considered after known qualifiers, with lexical order (always case insensitive),
* </li>
* <li>a dash usually precedes a qualifier, and is always less important than something preceded with a dot.</li>
* </ul></p>
*
* @see <a href="https://cwiki.apache.org/confluence/display/MAVENOLD/Versioning">"Versioning" on Maven Wiki</a>
* @author <a href="mailto:kenney@apache.org">Kenney Westerhof</a>
* @author <a href="mailto:hboutemy@apache.org">Hervé Boutemy</a>
*/
public class ComparableVersion
implements Comparable<ComparableVersion>
{
private String value;
private String canonical;
private ListItem items;
private interface Item
{
final int INTEGER_ITEM = 0;
final int STRING_ITEM = 1;
final int LIST_ITEM = 2;
int compareTo( Item item );
int getType();
boolean isNull();
}
/**
* Represents a numeric item in the version item list.
*/
private static class IntegerItem
implements Item
{
private static final BigInteger BigInteger_ZERO = new BigInteger( "0" );
private final BigInteger value;
public static final IntegerItem ZERO = new IntegerItem();
private IntegerItem()
{
this.value = BigInteger_ZERO;
}
public IntegerItem( String str )
{
this.value = new BigInteger( str );
}
public int getType()
{
return INTEGER_ITEM;
}
public boolean isNull()
{
return BigInteger_ZERO.equals( value );
}
public int compareTo( Item item )
{
if ( item == null )
{
return BigInteger_ZERO.equals( value ) ? 0 : 1; // 1.0 == 1, 1.1 > 1
}
switch ( item.getType() )
{
case INTEGER_ITEM:
return value.compareTo( ( (IntegerItem) item ).value );
case STRING_ITEM:
return 1; // 1.1 > 1-sp
case LIST_ITEM:
return 1; // 1.1 > 1-1
default:
throw new RuntimeException( "invalid item: " + item.getClass() );
}
}
public String toString()
{
return value.toString();
}
}
/**
* Represents a string in the version item list, usually a qualifier.
*/
private static class StringItem
implements Item
{
private static final String[] QUALIFIERS = { "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" };
private static final List<String> _QUALIFIERS = Arrays.asList( QUALIFIERS );
private static final Properties ALIASES = new Properties();
static
{
ALIASES.put( "ga", "" );
ALIASES.put( "final", "" );
ALIASES.put( "cr", "rc" );
}
/**
* A comparable value for the empty-string qualifier. This one is used to determine if a given qualifier makes
* the version older than one without a qualifier, or more recent.
*/
private static final String RELEASE_VERSION_INDEX = String.valueOf( _QUALIFIERS.indexOf( "" ) );
private String value;
public StringItem( String value, boolean followedByDigit )
{
if ( followedByDigit && value.length() == 1 )
{
// a1 = alpha-1, b1 = beta-1, m1 = milestone-1
switch ( value.charAt( 0 ) )
{
case 'a':
value = "alpha";
break;
case 'b':
value = "beta";
break;
case 'm':
value = "milestone";
break;
}
}
this.value = ALIASES.getProperty( value , value );
}
public int getType()
{
return STRING_ITEM;
}
public boolean isNull()
{
return ( comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX ) == 0 );
}
/**
* Returns a comparable value for a qualifier.
*
* This method takes into account the ordering of known qualifiers then unknown qualifiers with lexical ordering.
*
* just returning an Integer with the index here is faster, but requires a lot of if/then/else to check for -1
* or QUALIFIERS.size and then resort to lexical ordering. Most comparisons are decided by the first character,
* so this is still fast. If more characters are needed then it requires a lexical sort anyway.
*
* @param qualifier
* @return an equivalent value that can be used with lexical comparison
*/
public static String comparableQualifier( String qualifier )
{
int i = _QUALIFIERS.indexOf( qualifier );
return i == -1 ? ( _QUALIFIERS.size() + "-" + qualifier ) : String.valueOf( i );
}
public int compareTo( Item item )
{
if ( item == null )
{
// 1-rc < 1, 1-ga > 1
return comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX );
}
switch ( item.getType() )
{
case INTEGER_ITEM:
return -1; // 1.any < 1.1 ?
case STRING_ITEM:
return comparableQualifier( value ).compareTo( comparableQualifier( ( (StringItem) item ).value ) );
case LIST_ITEM:
return -1; // 1.any < 1-1
default:
throw new RuntimeException( "invalid item: " + item.getClass() );
}
}
public String toString()
{
return value;
}
}
/**
* Represents a version list item. This class is used both for the global item list and for sub-lists (which start
* with '-(number)' in the version specification).
*/
private static class ListItem
extends ArrayList<Item>
implements Item
{
public int getType()
{
return LIST_ITEM;
}
public boolean isNull()
{
return ( size() == 0 );
}
void normalize()
{
for( ListIterator<Item> iterator = listIterator( size() ); iterator.hasPrevious(); )
{
Item item = iterator.previous();
if ( item.isNull() )
{
iterator.remove(); // remove null trailing items: 0, "", empty list
}
else
{
break;
}
}
}
public int compareTo( Item item )
{
if ( item == null )
{
if ( size() == 0 )
{
return 0; // 1-0 = 1- (normalize) = 1
}
Item first = get( 0 );
return first.compareTo( null );
}
switch ( item.getType() )
{
case INTEGER_ITEM:
return -1; // 1-1 < 1.0.x
case STRING_ITEM:
return 1; // 1-1 > 1-sp
case LIST_ITEM:
Iterator<Item> left = iterator();
Iterator<Item> right = ( (ListItem) item ).iterator();
while ( left.hasNext() || right.hasNext() )
{
Item l = left.hasNext() ? left.next() : null;
Item r = right.hasNext() ? right.next() : null;
// if this is shorter, then invert the compare and mul with -1
int result = l == null ? -1 * r.compareTo( l ) : l.compareTo( r );
if ( result != 0 )
{
return result;
}
}
return 0;
default:
throw new RuntimeException( "invalid item: " + item.getClass() );
}
}
public String toString()
{
StringBuilder buffer = new StringBuilder( "(" );
for( Iterator<Item> iter = iterator(); iter.hasNext(); )
{
buffer.append( iter.next() );
if ( iter.hasNext() )
{
buffer.append( ',' );
}
}
buffer.append( ')' );
return buffer.toString();
}
}
public ComparableVersion( String version )
{
parseVersion( version );
}
public final void parseVersion( String version )
{
this.value = version;
items = new ListItem();
version = version.toLowerCase( Locale.ENGLISH );
ListItem list = items;
Stack<Item> stack = new Stack<Item>();
stack.push( list );
boolean isDigit = false;
int startIndex = 0;
for ( int i = 0; i < version.length(); i++ )
{
char c = version.charAt( i );
if ( c == '.' )
{
if ( i == startIndex )
{
list.add( IntegerItem.ZERO );
}
else
{
list.add( parseItem( isDigit, version.substring( startIndex, i ) ) );
}
startIndex = i + 1;
}
else if ( c == '-' )
{
if ( i == startIndex )
{
list.add( IntegerItem.ZERO );
}
else
{
list.add( parseItem( isDigit, version.substring( startIndex, i ) ) );
}
startIndex = i + 1;
if ( isDigit )
{
list.normalize(); // 1.0-* = 1-*
if ( ( i + 1 < version.length() ) && Character.isDigit( version.charAt( i + 1 ) ) )
{
// new ListItem only if previous were digits and new char is a digit,
// ie need to differentiate only 1.1 from 1-1
list.add( list = new ListItem() );
stack.push( list );
}
}
}
else if ( Character.isDigit( c ) )
{
if ( !isDigit && i > startIndex )
{
list.add( new StringItem( version.substring( startIndex, i ), true ) );
startIndex = i;
}
isDigit = true;
}
else
{
if ( isDigit && i > startIndex )
{
list.add( parseItem( true, version.substring( startIndex, i ) ) );
startIndex = i;
}
isDigit = false;
}
}
if ( version.length() > startIndex )
{
list.add( parseItem( isDigit, version.substring( startIndex ) ) );
}
while ( !stack.isEmpty() )
{
list = (ListItem) stack.pop();
list.normalize();
}
canonical = items.toString();
}
private static Item parseItem( boolean isDigit, String buf )
{
return isDigit ? new IntegerItem( buf ) : new StringItem( buf, false );
}
public int compareTo( ComparableVersion o )
{
return items.compareTo( o.items );
}
public String toString()
{
return value;
}
public boolean equals( Object o )
{
return ( o instanceof ComparableVersion ) && canonical.equals( ( (ComparableVersion) o ).canonical );
}
public int hashCode()
{
return canonical.hashCode();
}
}

View File

@ -0,0 +1,78 @@
package cpw.mods.fml.common.versioning;
public class DefaultArtifactVersion implements ArtifactVersion
{
private ComparableVersion comparableVersion;
private String label;
private boolean unbounded;
private VersionRange range;
public DefaultArtifactVersion(String versionNumber)
{
comparableVersion = new ComparableVersion(versionNumber);
range = VersionRange.createFromVersion(versionNumber, this);
}
public DefaultArtifactVersion(String label, VersionRange range)
{
this.label = label;
this.range = range;
}
public DefaultArtifactVersion(String label, String version)
{
this(version);
this.label = label;
}
public DefaultArtifactVersion(String string, boolean unbounded)
{
this.label = string;
this.unbounded = true;
}
@Override
public boolean equals(Object obj)
{
return ((DefaultArtifactVersion)obj).containsVersion(this);
}
@Override
public int compareTo(ArtifactVersion o)
{
return unbounded ? 0 : this.comparableVersion.compareTo(((DefaultArtifactVersion)o).comparableVersion);
}
@Override
public String getLabel()
{
return label;
}
@Override
public boolean containsVersion(ArtifactVersion source)
{
if (!source.getLabel().equals(getLabel()))
{
return false;
}
if (unbounded)
{
return true;
}
if (range != null)
{
return range.containsVersion(source);
}
else
{
return false;
}
}
@Override
public String toString()
{
return label == null ? comparableVersion.toString() : label + ( unbounded ? "" : "@" + range);
}
}

View File

@ -0,0 +1,34 @@
package cpw.mods.fml.common.versioning;
/*
* 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.
*/
/**
* Occurs when a version is invalid.
*
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
*/
public class InvalidVersionSpecificationException
extends Exception
{
public InvalidVersionSpecificationException( String message )
{
super( message );
}
}

View File

@ -0,0 +1,199 @@
package cpw.mods.fml.common.versioning;
/*
* 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.
*/
/**
* Describes a restriction in versioning.
*
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
*/
public class Restriction
{
private final ArtifactVersion lowerBound;
private final boolean lowerBoundInclusive;
private final ArtifactVersion upperBound;
private final boolean upperBoundInclusive;
public static final Restriction EVERYTHING = new Restriction( null, false, null, false );
public Restriction( ArtifactVersion lowerBound, boolean lowerBoundInclusive, ArtifactVersion upperBound,
boolean upperBoundInclusive )
{
this.lowerBound = lowerBound;
this.lowerBoundInclusive = lowerBoundInclusive;
this.upperBound = upperBound;
this.upperBoundInclusive = upperBoundInclusive;
}
public ArtifactVersion getLowerBound()
{
return lowerBound;
}
public boolean isLowerBoundInclusive()
{
return lowerBoundInclusive;
}
public ArtifactVersion getUpperBound()
{
return upperBound;
}
public boolean isUpperBoundInclusive()
{
return upperBoundInclusive;
}
public boolean containsVersion( ArtifactVersion version )
{
if ( lowerBound != null )
{
int comparison = lowerBound.compareTo( version );
if ( ( comparison == 0 ) && !lowerBoundInclusive )
{
return false;
}
if ( comparison > 0 )
{
return false;
}
}
if ( upperBound != null )
{
int comparison = upperBound.compareTo( version );
if ( ( comparison == 0 ) && !upperBoundInclusive )
{
return false;
}
if ( comparison < 0 )
{
return false;
}
}
return true;
}
@Override
public int hashCode()
{
int result = 13;
if ( lowerBound == null )
{
result += 1;
}
else
{
result += lowerBound.hashCode();
}
result *= lowerBoundInclusive ? 1 : 2;
if ( upperBound == null )
{
result -= 3;
}
else
{
result -= upperBound.hashCode();
}
result *= upperBoundInclusive ? 2 : 3;
return result;
}
@Override
public boolean equals( Object other )
{
if ( this == other )
{
return true;
}
if ( !( other instanceof Restriction ) )
{
return false;
}
Restriction restriction = (Restriction) other;
if ( lowerBound != null )
{
if ( !lowerBound.equals( restriction.lowerBound ) )
{
return false;
}
}
else if ( restriction.lowerBound != null )
{
return false;
}
if ( lowerBoundInclusive != restriction.lowerBoundInclusive )
{
return false;
}
if ( upperBound != null )
{
if ( !upperBound.equals( restriction.upperBound ) )
{
return false;
}
}
else if ( restriction.upperBound != null )
{
return false;
}
if ( upperBoundInclusive != restriction.upperBoundInclusive )
{
return false;
}
return true;
}
public String toString()
{
StringBuilder buf = new StringBuilder();
buf.append( isLowerBoundInclusive() ? "[" : "(" );
if ( getLowerBound() != null )
{
buf.append( getLowerBound().toString() );
}
buf.append( "," );
if ( getUpperBound() != null )
{
buf.append( getUpperBound().toString() );
}
buf.append( isUpperBoundInclusive() ? "]" : ")" );
return buf.toString();
}
}

View File

@ -0,0 +1,64 @@
package cpw.mods.fml.common.versioning;
import java.util.List;
import java.util.logging.Level;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.LoaderException;
/**
* Parses version strings according to the specification here:
* http://docs.codehaus.org/display/MAVEN/Versioning
* and allows for comparison of versions based on that document.
* Bounded version specifications are defined as
* http://maven.apache.org/plugins/maven-enforcer-plugin/rules/versionRanges.html
*
* Borrows heavily from maven version range management code
*
* @author cpw
*
*/
public class VersionParser
{
private static final Splitter SEPARATOR = Splitter.on('@').omitEmptyStrings().trimResults();
public static ArtifactVersion parseVersionReference(String labelledRef)
{
if (Strings.isNullOrEmpty(labelledRef))
{
throw new RuntimeException(String.format("Empty reference %s", labelledRef));
}
List<String> parts = Lists.newArrayList(SEPARATOR.split(labelledRef));
if (parts.size()>2)
{
throw new RuntimeException(String.format("Invalid versioned reference %s", labelledRef));
}
if (parts.size()==1)
{
return new DefaultArtifactVersion(parts.get(0), true);
}
return new DefaultArtifactVersion(parts.get(0),parseRange(parts.get(1)));
}
public static boolean satisfies(ArtifactVersion target, ArtifactVersion source)
{
return target.containsVersion(source);
}
public static VersionRange parseRange(String range)
{
try
{
return VersionRange.createFromVersionSpec(range);
}
catch (InvalidVersionSpecificationException e)
{
FMLLog.log(Level.SEVERE, e, "Unable to parse a version range specification successfully %s", range);
throw new LoaderException(e);
}
}
}

View File

@ -0,0 +1,540 @@
package cpw.mods.fml.common.versioning;
/*
* Modifications by cpw under LGPL 2.1 or later
*/
/*
* 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 java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import com.google.common.base.Joiner;
/**
* Construct a version range from a specification.
*
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
*/
public class VersionRange
{
private final ArtifactVersion recommendedVersion;
private final List<Restriction> restrictions;
private VersionRange( ArtifactVersion recommendedVersion,
List<Restriction> restrictions )
{
this.recommendedVersion = recommendedVersion;
this.restrictions = restrictions;
}
public ArtifactVersion getRecommendedVersion()
{
return recommendedVersion;
}
public List<Restriction> getRestrictions()
{
return restrictions;
}
public VersionRange cloneOf()
{
List<Restriction> copiedRestrictions = null;
if ( restrictions != null )
{
copiedRestrictions = new ArrayList<Restriction>();
if ( !restrictions.isEmpty() )
{
copiedRestrictions.addAll( restrictions );
}
}
return new VersionRange( recommendedVersion, copiedRestrictions );
}
/**
* Create a version range from a string representation
* <p/>
* Some spec examples are
* <ul>
* <li><code>1.0</code> Version 1.0</li>
* <li><code>[1.0,2.0)</code> Versions 1.0 (included) to 2.0 (not included)</li>
* <li><code>[1.0,2.0]</code> Versions 1.0 to 2.0 (both included)</li>
* <li><code>[1.5,)</code> Versions 1.5 and higher</li>
* <li><code>(,1.0],[1.2,)</code> Versions up to 1.0 (included) and 1.2 or higher</li>
* </ul>
*
* @param spec string representation of a version or version range
* @return a new {@link VersionRange} object that represents the spec
* @throws InvalidVersionSpecificationException
*
*/
public static VersionRange createFromVersionSpec( String spec )
throws InvalidVersionSpecificationException
{
if ( spec == null )
{
return null;
}
List<Restriction> restrictions = new ArrayList<Restriction>();
String process = spec;
ArtifactVersion version = null;
ArtifactVersion upperBound = null;
ArtifactVersion lowerBound = null;
while ( process.startsWith( "[" ) || process.startsWith( "(" ) )
{
int index1 = process.indexOf( ")" );
int index2 = process.indexOf( "]" );
int index = index2;
if ( index2 < 0 || index1 < index2 )
{
if ( index1 >= 0 )
{
index = index1;
}
}
if ( index < 0 )
{
throw new InvalidVersionSpecificationException( "Unbounded range: " + spec );
}
Restriction restriction = parseRestriction( process.substring( 0, index + 1 ) );
if ( lowerBound == null )
{
lowerBound = restriction.getLowerBound();
}
if ( upperBound != null )
{
if ( restriction.getLowerBound() == null || restriction.getLowerBound().compareTo( upperBound ) < 0 )
{
throw new InvalidVersionSpecificationException( "Ranges overlap: " + spec );
}
}
restrictions.add( restriction );
upperBound = restriction.getUpperBound();
process = process.substring( index + 1 ).trim();
if ( process.length() > 0 && process.startsWith( "," ) )
{
process = process.substring( 1 ).trim();
}
}
if ( process.length() > 0 )
{
if ( restrictions.size() > 0 )
{
throw new InvalidVersionSpecificationException(
"Only fully-qualified sets allowed in multiple set scenario: " + spec );
}
else
{
version = new DefaultArtifactVersion( process );
restrictions.add( Restriction.EVERYTHING );
}
}
return new VersionRange( version, restrictions );
}
private static Restriction parseRestriction( String spec )
throws InvalidVersionSpecificationException
{
boolean lowerBoundInclusive = spec.startsWith( "[" );
boolean upperBoundInclusive = spec.endsWith( "]" );
String process = spec.substring( 1, spec.length() - 1 ).trim();
Restriction restriction;
int index = process.indexOf( "," );
if ( index < 0 )
{
if ( !lowerBoundInclusive || !upperBoundInclusive )
{
throw new InvalidVersionSpecificationException( "Single version must be surrounded by []: " + spec );
}
ArtifactVersion version = new DefaultArtifactVersion( process );
restriction = new Restriction( version, lowerBoundInclusive, version, upperBoundInclusive );
}
else
{
String lowerBound = process.substring( 0, index ).trim();
String upperBound = process.substring( index + 1 ).trim();
if ( lowerBound.equals( upperBound ) )
{
throw new InvalidVersionSpecificationException( "Range cannot have identical boundaries: " + spec );
}
ArtifactVersion lowerVersion = null;
if ( lowerBound.length() > 0 )
{
lowerVersion = new DefaultArtifactVersion( lowerBound );
}
ArtifactVersion upperVersion = null;
if ( upperBound.length() > 0 )
{
upperVersion = new DefaultArtifactVersion( upperBound );
}
if ( upperVersion != null && lowerVersion != null && upperVersion.compareTo( lowerVersion ) < 0 )
{
throw new InvalidVersionSpecificationException( "Range defies version ordering: " + spec );
}
restriction = new Restriction( lowerVersion, lowerBoundInclusive, upperVersion, upperBoundInclusive );
}
return restriction;
}
public static VersionRange createFromVersion( String version , ArtifactVersion existing)
{
List<Restriction> restrictions = Collections.emptyList();
if (existing == null)
{
existing = new DefaultArtifactVersion( version );
}
return new VersionRange(existing , restrictions );
}
/**
* Creates and returns a new <code>VersionRange</code> that is a restriction of this
* version range and the specified version range.
* <p>
* Note: Precedence is given to the recommended version from this version range over the
* recommended version from the specified version range.
* </p>
*
* @param restriction the <code>VersionRange</code> that will be used to restrict this version
* range.
* @return the <code>VersionRange</code> that is a restriction of this version range and the
* specified version range.
* <p>
* The restrictions of the returned version range will be an intersection of the restrictions
* of this version range and the specified version range if both version ranges have
* restrictions. Otherwise, the restrictions on the returned range will be empty.
* </p>
* <p>
* The recommended version of the returned version range will be the recommended version of
* this version range, provided that ranges falls within the intersected restrictions. If
* the restrictions are empty, this version range's recommended version is used if it is not
* <code>null</code>. If it is <code>null</code>, the specified version range's recommended
* version is used (provided it is non-<code>null</code>). If no recommended version can be
* obtained, the returned version range's recommended version is set to <code>null</code>.
* </p>
* @throws NullPointerException if the specified <code>VersionRange</code> is
* <code>null</code>.
*/
public VersionRange restrict( VersionRange restriction )
{
List<Restriction> r1 = this.restrictions;
List<Restriction> r2 = restriction.restrictions;
List<Restriction> restrictions;
if ( r1.isEmpty() || r2.isEmpty() )
{
restrictions = Collections.emptyList();
}
else
{
restrictions = intersection( r1, r2 );
}
ArtifactVersion version = null;
if ( restrictions.size() > 0 )
{
for ( Restriction r : restrictions )
{
if ( recommendedVersion != null && r.containsVersion( recommendedVersion ) )
{
// if we find the original, use that
version = recommendedVersion;
break;
}
else if ( version == null && restriction.getRecommendedVersion() != null
&& r.containsVersion( restriction.getRecommendedVersion() ) )
{
// use this if we can, but prefer the original if possible
version = restriction.getRecommendedVersion();
}
}
}
// Either the original or the specified version ranges have no restrictions
else if ( recommendedVersion != null )
{
// Use the original recommended version since it exists
version = recommendedVersion;
}
else if ( restriction.recommendedVersion != null )
{
// Use the recommended version from the specified VersionRange since there is no
// original recommended version
version = restriction.recommendedVersion;
}
/* TODO: should throw this immediately, but need artifact
else
{
throw new OverConstrainedVersionException( "Restricting incompatible version ranges" );
}
*/
return new VersionRange( version, restrictions );
}
private List<Restriction> intersection( List<Restriction> r1, List<Restriction> r2 )
{
List<Restriction> restrictions = new ArrayList<Restriction>( r1.size() + r2.size() );
Iterator<Restriction> i1 = r1.iterator();
Iterator<Restriction> i2 = r2.iterator();
Restriction res1 = i1.next();
Restriction res2 = i2.next();
boolean done = false;
while ( !done )
{
if ( res1.getLowerBound() == null || res2.getUpperBound() == null
|| res1.getLowerBound().compareTo( res2.getUpperBound() ) <= 0 )
{
if ( res1.getUpperBound() == null || res2.getLowerBound() == null
|| res1.getUpperBound().compareTo( res2.getLowerBound() ) >= 0 )
{
ArtifactVersion lower;
ArtifactVersion upper;
boolean lowerInclusive;
boolean upperInclusive;
// overlaps
if ( res1.getLowerBound() == null )
{
lower = res2.getLowerBound();
lowerInclusive = res2.isLowerBoundInclusive();
}
else if ( res2.getLowerBound() == null )
{
lower = res1.getLowerBound();
lowerInclusive = res1.isLowerBoundInclusive();
}
else
{
int comparison = res1.getLowerBound().compareTo( res2.getLowerBound() );
if ( comparison < 0 )
{
lower = res2.getLowerBound();
lowerInclusive = res2.isLowerBoundInclusive();
}
else if ( comparison == 0 )
{
lower = res1.getLowerBound();
lowerInclusive = res1.isLowerBoundInclusive() && res2.isLowerBoundInclusive();
}
else
{
lower = res1.getLowerBound();
lowerInclusive = res1.isLowerBoundInclusive();
}
}
if ( res1.getUpperBound() == null )
{
upper = res2.getUpperBound();
upperInclusive = res2.isUpperBoundInclusive();
}
else if ( res2.getUpperBound() == null )
{
upper = res1.getUpperBound();
upperInclusive = res1.isUpperBoundInclusive();
}
else
{
int comparison = res1.getUpperBound().compareTo( res2.getUpperBound() );
if ( comparison < 0 )
{
upper = res1.getUpperBound();
upperInclusive = res1.isUpperBoundInclusive();
}
else if ( comparison == 0 )
{
upper = res1.getUpperBound();
upperInclusive = res1.isUpperBoundInclusive() && res2.isUpperBoundInclusive();
}
else
{
upper = res2.getUpperBound();
upperInclusive = res2.isUpperBoundInclusive();
}
}
// don't add if they are equal and one is not inclusive
if ( lower == null || upper == null || lower.compareTo( upper ) != 0 )
{
restrictions.add( new Restriction( lower, lowerInclusive, upper, upperInclusive ) );
}
else if ( lowerInclusive && upperInclusive )
{
restrictions.add( new Restriction( lower, lowerInclusive, upper, upperInclusive ) );
}
//noinspection ObjectEquality
if ( upper == res2.getUpperBound() )
{
// advance res2
if ( i2.hasNext() )
{
res2 = i2.next();
}
else
{
done = true;
}
}
else
{
// advance res1
if ( i1.hasNext() )
{
res1 = i1.next();
}
else
{
done = true;
}
}
}
else
{
// move on to next in r1
if ( i1.hasNext() )
{
res1 = i1.next();
}
else
{
done = true;
}
}
}
else
{
// move on to next in r2
if ( i2.hasNext() )
{
res2 = i2.next();
}
else
{
done = true;
}
}
}
return restrictions;
}
public String toString()
{
if ( recommendedVersion != null )
{
return recommendedVersion.toString();
}
else
{
return Joiner.on(',').join(restrictions);
}
}
public ArtifactVersion matchVersion( List<ArtifactVersion> versions )
{
// TODO: could be more efficient by sorting the list and then moving along the restrictions in order?
ArtifactVersion matched = null;
for ( ArtifactVersion version : versions )
{
if ( containsVersion( version ) )
{
// valid - check if it is greater than the currently matched version
if ( matched == null || version.compareTo( matched ) > 0 )
{
matched = version;
}
}
}
return matched;
}
public boolean containsVersion( ArtifactVersion version )
{
for ( Restriction restriction : restrictions )
{
if ( restriction.containsVersion( version ) )
{
return true;
}
}
return false;
}
public boolean hasRestrictions()
{
return !restrictions.isEmpty() && recommendedVersion == null;
}
public boolean equals( Object obj )
{
if ( this == obj )
{
return true;
}
if ( !( obj instanceof VersionRange ) )
{
return false;
}
VersionRange other = (VersionRange) obj;
boolean equals =
recommendedVersion == other.recommendedVersion
|| ( ( recommendedVersion != null ) && recommendedVersion.equals( other.recommendedVersion ) );
equals &=
restrictions == other.restrictions
|| ( ( restrictions != null ) && restrictions.equals( other.restrictions ) );
return equals;
}
public int hashCode()
{
int hash = 7;
hash = 31 * hash + ( recommendedVersion == null ? 0 : recommendedVersion.hashCode() );
hash = 31 * hash + ( restrictions == null ? 0 : restrictions.hashCode() );
return hash;
}
}

View File

@ -92,10 +92,10 @@ public class mod_testMod extends BaseMod {
return interval;
}
}
@Override
public String getPriorities()
{
return "after:MockMod";
return "after:MockMod@[1.1,1.2),[1.3,)";
}
}