001/* 002 * Extra Bnd Repository Plugins 003 * Copyright (C) 2017 Michael N. Lipp 004 * 005 * This program is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU Affero General Public License as published by 007 * the Free Software Foundation; either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, but 011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 013 * for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License along 016 * with this program; if not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package de.mnl.osgi.bnd.maven; 020 021import java.util.Comparator; 022import java.util.List; 023import org.apache.maven.artifact.versioning.ArtifactVersion; 024import org.apache.maven.artifact.versioning.DefaultArtifactVersion; 025import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; 026import org.apache.maven.artifact.versioning.Restriction; 027import org.apache.maven.artifact.versioning.VersionRange; 028 029/** 030 * Provides a representation of a maven version range. The implementation 031 * is a small wrapper around 032 * {@link org.apache.maven.artifact.versioning.VersionRange}. 033 * <P> 034 * Because {@link org.apache.maven.artifact.versioning.VersionRange} has 035 * only a private constructor and cannot be extended, the wrapper 036 * delegates to an instance of 037 * {@link org.apache.maven.artifact.versioning.VersionRange}. 038 */ 039@SuppressWarnings("PMD.DataflowAnomalyAnalysis") 040public class MavenVersionRange extends MavenVersionSpecification { 041 042 public static final MavenVersionRange ALL; 043 public static final MavenVersionRange NONE; 044 private static final ArtifactVersion ZERO = new DefaultArtifactVersion("0"); 045 private VersionRange range; 046 047 static { 048 VersionRange range; 049 try { 050 range = VersionRange.createFromVersionSpec("[0,)"); 051 } catch (InvalidVersionSpecificationException e) { 052 // Won't happen (checked). 053 range = null; 054 } 055 ALL = new MavenVersionRange(range); 056 try { 057 range = VersionRange.createFromVersionSpec("[,0)"); 058 } catch (InvalidVersionSpecificationException e) { 059 // Won't happen (checked). 060 range = null; 061 } 062 NONE = new MavenVersionRange(range); 063 } 064 065 /** 066 * Instantiates a new maven version range from the given range. 067 * 068 * @param range the range 069 */ 070 public MavenVersionRange(VersionRange range) { 071 this.range = range; 072 } 073 074 /** 075 * Instantiates a new maven version range from the given representation. 076 * If {@code version} is {@code} null it is considered to be 077 * the "all inclusive range" ("[0,)"). 078 * 079 * @param range the range 080 */ 081 public MavenVersionRange(String range) { 082 if (range == null) { 083 this.range = ALL.range; 084 return; 085 } 086 try { 087 this.range = VersionRange.createFromVersionSpec(range); 088 } catch (InvalidVersionSpecificationException e) { 089 throw new IllegalArgumentException(e); 090 } 091 } 092 093 /** 094 * Returns the version range that this instance delegates to. 095 * 096 * @return the org.apache.maven.artifact.versioning. version range 097 */ 098 public VersionRange versionRange() { 099 return range; 100 } 101 102 /** 103 * Returns the complementary version rang. 104 * 105 * @return the maven version range 106 */ 107 @SuppressWarnings({ "PMD.CyclomaticComplexity", "PMD.CognitiveComplexity" }) 108 public MavenVersionRange complement() { 109 List<Restriction> restrictions = range.getRestrictions(); 110 restrictions.sort(Comparator.comparing(Restriction::getLowerBound)); 111 ArtifactVersion lastVersion = new DefaultArtifactVersion("0"); 112 boolean lastUpperInclusive = false; 113 StringBuilder cmpl = new StringBuilder(); 114 for (Restriction rstrct : restrictions) { 115 ArtifactVersion rstrctLower = rstrct.getLowerBound(); 116 if (rstrctLower == null) { 117 rstrctLower = ZERO; 118 } 119 int cmp = lastVersion.compareTo(rstrctLower); 120 if (cmp < 0 || cmp == 0 121 && !(lastUpperInclusive || rstrct.isLowerBoundInclusive())) { 122 // Not overlap or continuation. 123 if (cmpl.length() > 0) { 124 cmpl.append(','); 125 } 126 cmpl.append(lastUpperInclusive ? '(' : '['); 127 cmpl.append(lastVersion.toString()); 128 cmpl.append(','); 129 cmpl.append(rstrct.getLowerBound().toString()); 130 cmpl.append(rstrct.isLowerBoundInclusive() ? ')' : ']'); 131 } 132 lastVersion = rstrct.getUpperBound(); 133 lastUpperInclusive = rstrct.isUpperBoundInclusive(); 134 if (lastVersion == null) { 135 // Any restriction with open upper end is final 136 // (cannot add range to maximum range). 137 break; 138 } 139 } 140 if (lastVersion == null) { 141 // Open ended, check if it was "all" ("[0,)") 142 if (cmpl.length() == 0) { 143 cmpl.append("[,0)"); 144 } 145 } else { 146 // Not open ended, so we must provide the last restriction. 147 if (cmpl.length() > 0) { 148 cmpl.append(','); 149 } 150 cmpl.append(lastUpperInclusive ? '(' : '['); 151 cmpl.append(lastVersion.toString()); 152 cmpl.append(",)"); 153 } 154 return new MavenVersionRange(cmpl.toString()); 155 } 156 157 /** 158 * Checks if this version range includes the specified version 159 * or range. A range is included if it is fully included. 160 * 161 * @param mavenVersion the maven version 162 * @return the result 163 */ 164 public boolean includes(MavenVersionSpecification mavenVersion) { 165 if (mavenVersion instanceof MavenVersion) { 166 return range.containsVersion((MavenVersion) mavenVersion); 167 } 168 return restrict((MavenVersionRange) mavenVersion).range 169 .getRestrictions().isEmpty(); 170 } 171 172 /** 173 * Creates and returns a new VersionRange that is a restriction 174 * of this version range and the specified version range. 175 * 176 * @see VersionRange#restrict 177 * 178 * @param restriction the restriction 179 * @return the maven version range 180 */ 181 public MavenVersionRange restrict(MavenVersionRange restriction) { 182 return new MavenVersionRange(range.restrict(restriction.range)); 183 } 184 185 /** 186 * Creates a new maven version range from the given representation. 187 * 188 * @param version the string representation 189 * @return the maven version range 190 */ 191 public static MavenVersionRange parseRange(String version) { 192 return new MavenVersionRange(version); 193 } 194 195 /** 196 * Checks if is the provided version representation is a range. 197 * If {@code version} is {@code} null it is considered to be 198 * the "all inclusive range" ("[0,)"). 199 * 200 * @param version the version 201 * @return true, if is range 202 * @deprecated Use {@link MavenVersionSpecification#isRange(String)} instead 203 */ 204 @Deprecated 205 public static boolean isRange(String version) { 206 return MavenVersionSpecification.isRange(version); 207 } 208 209 @Override 210 public String toString() { 211 return range.toString(); 212 } 213 214 /* 215 * (non-Javadoc) 216 * 217 * @see java.lang.Object#hashCode() 218 */ 219 @Override 220 public int hashCode() { 221 return range.hashCode(); 222 } 223 224 /* 225 * (non-Javadoc) 226 * 227 * @see java.lang.Object#equals(java.lang.Object) 228 */ 229 @Override 230 public boolean equals(Object obj) { 231 if (obj instanceof MavenVersionRange) { 232 return range.equals(((MavenVersionRange) obj).range); 233 } 234 if (obj instanceof VersionRange) { 235 return range.equals((VersionRange) obj); 236 } 237 return false; 238 } 239 240}