diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java b/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java index d76efff780..c26cedf9ed 100644 --- a/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java +++ b/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java @@ -80,7 +80,7 @@ public class DefaultArtifact this.artifactId = artifactId; // TODO: this would be where we might have a min/max instead - this.version = versionRange.getVersion(); + this.version = versionRange != null ? versionRange.getRecommendedVersion() : null; this.artifactHandler = artifactHandler; @@ -188,8 +188,7 @@ public class DefaultArtifact public String getId() { - return getDependencyConflictId() + ( hasClassifier() ? ( ":" + getClassifier() ) : "" ) + ":" + - getBaseVersion(); + return getDependencyConflictId() + ( hasClassifier() ? ":" + getClassifier() : "" ) + ":" + getBaseVersion(); } public String getDependencyConflictId() diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/factory/DefaultArtifactFactory.java b/maven-artifact/src/main/java/org/apache/maven/artifact/factory/DefaultArtifactFactory.java index f9864e609b..e5f310df55 100644 --- a/maven-artifact/src/main/java/org/apache/maven/artifact/factory/DefaultArtifactFactory.java +++ b/maven-artifact/src/main/java/org/apache/maven/artifact/factory/DefaultArtifactFactory.java @@ -101,14 +101,10 @@ public class DefaultArtifactFactory String classifier, String inheritedScope ) { // TODO: better constructor - VersionRange versionRange; + VersionRange versionRange = null; if ( version != null ) { - versionRange = new VersionRange( "[" + version + "]" ); - } - else - { - versionRange = new VersionRange( null ); + versionRange = VersionRange.createFromVersion( version ); } return createArtifact( groupId, artifactId, versionRange, scope, type, classifier, inheritedScope ); } @@ -127,13 +123,11 @@ public class DefaultArtifactFactory { return null; } - - // vvv added to retain compile scope. Remove if you want compile inherited as runtime else if ( Artifact.SCOPE_COMPILE.equals( scope ) && Artifact.SCOPE_COMPILE.equals( inheritedScope ) ) { + // added to retain compile scope. Remove if you want compile inherited as runtime desiredScope = Artifact.SCOPE_COMPILE; } - // ^^^ added to retain compile scope. Remove if you want compile inherited as runtime if ( Artifact.SCOPE_TEST.equals( inheritedScope ) ) { diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/InvalidVersionSpecificationException.java b/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/InvalidVersionSpecificationException.java new file mode 100644 index 0000000000..1f518da941 --- /dev/null +++ b/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/InvalidVersionSpecificationException.java @@ -0,0 +1,32 @@ +package org.apache.maven.artifact.versioning; + +/* + * Copyright 2001-2005 The Apache Software Foundation. + * + * Licensed 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 Brett Porter + * @version $Id$ + */ +public class InvalidVersionSpecificationException + extends Exception +{ + public InvalidVersionSpecificationException( String message ) + { + super( message ); + } +} diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/Restriction.java b/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/Restriction.java new file mode 100644 index 0000000000..d186e633e8 --- /dev/null +++ b/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/Restriction.java @@ -0,0 +1,62 @@ +package org.apache.maven.artifact.versioning; + +/* + * Copyright 2001-2005 The Apache Software Foundation. + * + * Licensed 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 Brett Porter + * @version $Id$ + */ +public class Restriction +{ + private final String lowerBound; + + private final boolean lowerBoundInclusive; + + private final String upperBound; + + private final boolean upperBoundInclusive; + + public Restriction( String lowerBound, boolean lowerBoundInclusive, String upperBound, boolean upperBoundInclusive ) + { + this.lowerBound = lowerBound; + this.lowerBoundInclusive = lowerBoundInclusive; + this.upperBound = upperBound; + this.upperBoundInclusive = upperBoundInclusive; + } + + public String getLowerBound() + { + return lowerBound; + } + + public boolean isLowerBoundInclusive() + { + return lowerBoundInclusive; + } + + public String getUpperBound() + { + return upperBound; + } + + public boolean isUpperBoundInclusive() + { + return upperBoundInclusive; + } +} diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java b/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java index 1883dd440e..80363b4ae7 100644 --- a/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java +++ b/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java @@ -16,6 +16,10 @@ package org.apache.maven.artifact.versioning; * limitations under the License. */ +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * Construct a version range from a specification. * @@ -24,24 +28,124 @@ package org.apache.maven.artifact.versioning; */ public class VersionRange { - private String version; + private final String recommendedVersion; - public VersionRange( String spec ) + private final List restrictions; + + private VersionRange( String recommendedVersion, List restrictions ) { - if ( spec != null ) + this.recommendedVersion = recommendedVersion; + this.restrictions = restrictions; + } + + public String getRecommendedVersion() + { + return recommendedVersion; + } + + public List getRestrictions() + { + return restrictions; + } + + public static VersionRange createFromVersionSpec( String spec ) + throws InvalidVersionSpecificationException + { + List exclusions = new ArrayList(); + String process = spec; + String version = null; + + while ( process.startsWith( "[" ) || process.startsWith( "(" ) ) { - // temporary! - if ( spec.startsWith( "[" ) ) + int index1 = process.indexOf( ")" ); + int index2 = process.indexOf( "]" ); + + int index = index2; + if ( index2 < 0 || index1 < index2 ) { - spec = spec.substring( 1, spec.length() - 1 ); + if ( index1 >= 0 ) + { + index = index1; + } + } + + if ( index < 0 ) + { + throw new InvalidVersionSpecificationException( "Unbounded range: " + spec ); + } + + exclusions.add( parseRestriction( process.substring( 0, index + 1 ) ) ); + + process = process.substring( index + 1 ).trim(); + + if ( process.length() > 0 && process.startsWith( "," ) ) + { + process = process.substring( 1 ).trim(); } } - this.version = spec; + if ( process.length() > 0 ) + { + if ( exclusions.size() > 0 ) + { + throw new InvalidVersionSpecificationException( + "Only fully-qualified sets allowed in multiple set scenario: " + spec ); + } + else + { + version = process; + } + } + + return new VersionRange( version, exclusions ); } - public String getVersion() + private static Restriction parseRestriction( String spec ) + throws InvalidVersionSpecificationException { - return version; + 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 ); + } + restriction = new Restriction( process, lowerBoundInclusive, process, 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 ); + } + + if ( lowerBound.length() == 0 ) + { + lowerBound = null; + } + if ( upperBound.length() == 0 ) + { + upperBound = null; + } + + restriction = new Restriction( lowerBound, lowerBoundInclusive, upperBound, upperBoundInclusive ); + } + + return restriction; + } + + public static VersionRange createFromVersion( String version ) + { + return new VersionRange( version, Collections.EMPTY_LIST ); } } diff --git a/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/VersionRangeTest.java b/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/VersionRangeTest.java new file mode 100644 index 0000000000..4841098b58 --- /dev/null +++ b/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/VersionRangeTest.java @@ -0,0 +1,148 @@ +package org.apache.maven.artifact.versioning; + +/* + * Copyright 2001-2005 The Apache Software Foundation. + * + * Licensed 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 junit.framework.TestCase; + +import java.util.List; + +/** + * Tests version range construction. + * + * @author Brett Porter + * @version $Id$ + */ +public class VersionRangeTest + extends TestCase +{ + private static final String CHECK_NUM_RESTRICTIONS = "check number of restrictions"; + + private static final String CHECK_UPPER_BOUND = "check upper bound"; + + private static final String CHECK_UPPER_BOUND_INCLUSIVE = "check upper bound is inclusive"; + + private static final String CHECK_LOWER_BOUND = "check lower bound"; + + private static final String CHECK_LOWER_BOUND_INCLUSIVE = "check lower bound is inclusive"; + + private static final String CHECK_VERSION_RECOMMENDATION = "check version recommended"; + + public void testRange() + throws InvalidVersionSpecificationException + { + VersionRange range = VersionRange.createFromVersionSpec( "(,1.0]" ); + List restrictions = range.getRestrictions(); + assertEquals( CHECK_NUM_RESTRICTIONS, 1, restrictions.size() ); + Restriction restriction = (Restriction) restrictions.get( 0 ); + assertNull( CHECK_LOWER_BOUND, restriction.getLowerBound() ); + assertFalse( CHECK_LOWER_BOUND_INCLUSIVE, restriction.isLowerBoundInclusive() ); + assertEquals( CHECK_UPPER_BOUND, "1.0", restriction.getUpperBound() ); + assertTrue( CHECK_UPPER_BOUND_INCLUSIVE, restriction.isUpperBoundInclusive() ); + assertNull( CHECK_VERSION_RECOMMENDATION, range.getRecommendedVersion() ); + + range = VersionRange.createFromVersionSpec( "1.0" ); + restrictions = range.getRestrictions(); + assertEquals( CHECK_NUM_RESTRICTIONS, 0, restrictions.size() ); + assertEquals( CHECK_VERSION_RECOMMENDATION, "1.0", range.getRecommendedVersion() ); + + range = VersionRange.createFromVersionSpec( "[1.0]" ); + restrictions = range.getRestrictions(); + assertEquals( CHECK_NUM_RESTRICTIONS, 1, restrictions.size() ); + restriction = (Restriction) restrictions.get( 0 ); + assertEquals( CHECK_LOWER_BOUND, "1.0", restriction.getLowerBound() ); + assertTrue( CHECK_LOWER_BOUND_INCLUSIVE, restriction.isLowerBoundInclusive() ); + assertEquals( CHECK_UPPER_BOUND, "1.0", restriction.getUpperBound() ); + assertTrue( CHECK_UPPER_BOUND_INCLUSIVE, restriction.isUpperBoundInclusive() ); + assertNull( CHECK_VERSION_RECOMMENDATION, range.getRecommendedVersion() ); + + range = VersionRange.createFromVersionSpec( "[1.2,1.3]" ); + restrictions = range.getRestrictions(); + assertEquals( CHECK_NUM_RESTRICTIONS, 1, restrictions.size() ); + restriction = (Restriction) restrictions.get( 0 ); + assertEquals( CHECK_LOWER_BOUND, "1.2", restriction.getLowerBound() ); + assertTrue( CHECK_LOWER_BOUND_INCLUSIVE, restriction.isLowerBoundInclusive() ); + assertEquals( CHECK_UPPER_BOUND, "1.3", restriction.getUpperBound() ); + assertTrue( CHECK_UPPER_BOUND_INCLUSIVE, restriction.isUpperBoundInclusive() ); + assertNull( CHECK_VERSION_RECOMMENDATION, range.getRecommendedVersion() ); + + range = VersionRange.createFromVersionSpec( "[1.0,2.0)" ); + restrictions = range.getRestrictions(); + assertEquals( CHECK_NUM_RESTRICTIONS, 1, restrictions.size() ); + restriction = (Restriction) restrictions.get( 0 ); + assertEquals( CHECK_LOWER_BOUND, "1.0", restriction.getLowerBound() ); + assertTrue( CHECK_LOWER_BOUND_INCLUSIVE, restriction.isLowerBoundInclusive() ); + assertEquals( CHECK_UPPER_BOUND, "2.0", restriction.getUpperBound() ); + assertFalse( CHECK_UPPER_BOUND_INCLUSIVE, restriction.isUpperBoundInclusive() ); + assertNull( CHECK_VERSION_RECOMMENDATION, range.getRecommendedVersion() ); + + range = VersionRange.createFromVersionSpec( "[1.5,)" ); + restrictions = range.getRestrictions(); + assertEquals( CHECK_NUM_RESTRICTIONS, 1, restrictions.size() ); + restriction = (Restriction) restrictions.get( 0 ); + assertEquals( CHECK_LOWER_BOUND, "1.5", restriction.getLowerBound() ); + assertTrue( CHECK_LOWER_BOUND_INCLUSIVE, restriction.isLowerBoundInclusive() ); + assertNull( CHECK_UPPER_BOUND, restriction.getUpperBound() ); + assertFalse( CHECK_UPPER_BOUND_INCLUSIVE, restriction.isUpperBoundInclusive() ); + assertNull( CHECK_VERSION_RECOMMENDATION, range.getRecommendedVersion() ); + + range = VersionRange.createFromVersionSpec( "(,1.0],[1.2,)" ); + restrictions = range.getRestrictions(); + assertEquals( CHECK_NUM_RESTRICTIONS, 2, restrictions.size() ); + restriction = (Restriction) restrictions.get( 0 ); + assertNull( CHECK_LOWER_BOUND, restriction.getLowerBound() ); + assertFalse( CHECK_LOWER_BOUND_INCLUSIVE, restriction.isLowerBoundInclusive() ); + assertEquals( CHECK_UPPER_BOUND, "1.0", restriction.getUpperBound() ); + assertTrue( CHECK_UPPER_BOUND_INCLUSIVE, restriction.isUpperBoundInclusive() ); + assertNull( CHECK_VERSION_RECOMMENDATION, range.getRecommendedVersion() ); + restriction = (Restriction) restrictions.get( 1 ); + assertEquals( CHECK_LOWER_BOUND, "1.2", restriction.getLowerBound() ); + assertTrue( CHECK_LOWER_BOUND_INCLUSIVE, restriction.isLowerBoundInclusive() ); + assertNull( CHECK_UPPER_BOUND, restriction.getUpperBound() ); + assertFalse( CHECK_UPPER_BOUND_INCLUSIVE, restriction.isUpperBoundInclusive() ); + assertNull( CHECK_VERSION_RECOMMENDATION, range.getRecommendedVersion() ); + } + + public void testInvalidRanges() + { + checkInvalidRange( "(1.0)" ); + checkInvalidRange( "[1.0)" ); + checkInvalidRange( "(1.0]" ); + checkInvalidRange( "(1.0,1.0]" ); + checkInvalidRange( "[1.0,1.0)" ); + checkInvalidRange( "(1.0,1.0)" ); + checkInvalidRange( "[1.0,1.2),1.3" ); +/* TODO: not testing this presently + // overlap + checkInvalidRange( "[1.0,1.2),(1.1,1.3]" ); + // overlap + checkInvalidRange( "[1.1,1.3),(1.0,1.2]" ); +*/ + } + + private void checkInvalidRange( String version ) + { + try + { + VersionRange.createFromVersionSpec( version ); + fail( "Version " + version + " should have failed to construct" ); + } + catch ( InvalidVersionSpecificationException expected ) + { + // expected + } + } +}