View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.test.util;
20  
21  import java.util.Objects;
22  
23  import org.eclipse.aether.version.InvalidVersionSpecificationException;
24  import org.eclipse.aether.version.Version;
25  import org.eclipse.aether.version.VersionRange;
26  
27  /**
28   * A version range inspired by mathematical range syntax. For example, "[1.0,2.0)", "[1.0,)" or "[1.0]".
29   */
30  final class TestVersionRange implements VersionRange {
31  
32      private final Version lowerBound;
33  
34      private final boolean lowerBoundInclusive;
35  
36      private final Version upperBound;
37  
38      private final boolean upperBoundInclusive;
39  
40      /**
41       * Creates a version range from the specified range specification.
42       *
43       * @param range The range specification to parse, must not be {@code null}.
44       * @throws InvalidVersionSpecificationException If the range could not be parsed.
45       */
46      TestVersionRange(String range) throws InvalidVersionSpecificationException {
47          String process = range;
48  
49          if (range.startsWith("[")) {
50              lowerBoundInclusive = true;
51          } else if (range.startsWith("(")) {
52              lowerBoundInclusive = false;
53          } else {
54              throw new InvalidVersionSpecificationException(
55                      range, "Invalid version range " + range + ", a range must start with either [ or (");
56          }
57  
58          if (range.endsWith("]")) {
59              upperBoundInclusive = true;
60          } else if (range.endsWith(")")) {
61              upperBoundInclusive = false;
62          } else {
63              throw new InvalidVersionSpecificationException(
64                      range, "Invalid version range " + range + ", a range must end with either [ or (");
65          }
66  
67          process = process.substring(1, process.length() - 1);
68  
69          int index = process.indexOf(",");
70  
71          if (index < 0) {
72              if (!lowerBoundInclusive || !upperBoundInclusive) {
73                  throw new InvalidVersionSpecificationException(
74                          range, "Invalid version range " + range + ", single version must be surrounded by []");
75              }
76  
77              lowerBound = new TestVersion(process.trim());
78              upperBound = new TestVersion(process.trim());
79          } else {
80              String parsedLowerBound = process.substring(0, index).trim();
81              String parsedUpperBound = process.substring(index + 1).trim();
82  
83              // more than two bounds, e.g. (1,2,3)
84              if (parsedUpperBound.contains(",")) {
85                  throw new InvalidVersionSpecificationException(
86                          range, "Invalid version range " + range + ", bounds may not contain additional ','");
87              }
88  
89              lowerBound = parsedLowerBound.length() > 0 ? new TestVersion(parsedLowerBound) : null;
90              upperBound = parsedUpperBound.length() > 0 ? new TestVersion(parsedUpperBound) : null;
91  
92              if (upperBound != null && lowerBound != null) {
93                  if (upperBound.compareTo(lowerBound) < 0) {
94                      throw new InvalidVersionSpecificationException(
95                              range,
96                              "Invalid version range " + range + ", lower bound must not be greater than upper bound");
97                  }
98              }
99          }
100     }
101 
102     public Bound getLowerBound() {
103         return new Bound(lowerBound, lowerBoundInclusive);
104     }
105 
106     public Bound getUpperBound() {
107         return new Bound(upperBound, upperBoundInclusive);
108     }
109 
110     public boolean acceptsSnapshots() {
111         return isSnapshot(lowerBound) || isSnapshot(upperBound);
112     }
113 
114     public boolean containsVersion(Version version) {
115         boolean snapshot = isSnapshot(version);
116 
117         if (lowerBound != null) {
118             int comparison = lowerBound.compareTo(version);
119 
120             if (snapshot && comparison == 0) {
121                 return true;
122             }
123 
124             if (comparison == 0 && !lowerBoundInclusive) {
125                 return false;
126             }
127             if (comparison > 0) {
128                 return false;
129             }
130         }
131 
132         if (upperBound != null) {
133             int comparison = upperBound.compareTo(version);
134 
135             if (snapshot && comparison == 0) {
136                 return true;
137             }
138 
139             if (comparison == 0 && !upperBoundInclusive) {
140                 return false;
141             }
142             if (comparison < 0) {
143                 return false;
144             }
145         }
146 
147         if (lowerBound != null || upperBound != null) {
148             return !snapshot;
149         }
150 
151         return true;
152     }
153 
154     private boolean isSnapshot(Version version) {
155         return version != null && version.toString().endsWith("SNAPSHOT");
156     }
157 
158     @Override
159     public boolean equals(Object obj) {
160         if (obj == this) {
161             return true;
162         } else if (obj == null || !getClass().equals(obj.getClass())) {
163             return false;
164         }
165 
166         TestVersionRange that = (TestVersionRange) obj;
167 
168         return upperBoundInclusive == that.upperBoundInclusive
169                 && lowerBoundInclusive == that.lowerBoundInclusive
170                 && Objects.equals(upperBound, that.upperBound)
171                 && Objects.equals(lowerBound, that.lowerBound);
172     }
173 
174     @Override
175     public int hashCode() {
176         int hash = 17;
177         hash = hash * 31 + hash(upperBound);
178         hash = hash * 31 + (upperBoundInclusive ? 1 : 0);
179         hash = hash * 31 + hash(lowerBound);
180         hash = hash * 31 + (lowerBoundInclusive ? 1 : 0);
181         return hash;
182     }
183 
184     private static int hash(Object obj) {
185         return obj != null ? obj.hashCode() : 0;
186     }
187 
188     @Override
189     public String toString() {
190         StringBuilder buffer = new StringBuilder(64);
191         buffer.append(lowerBoundInclusive ? '[' : '(');
192         if (lowerBound != null) {
193             buffer.append(lowerBound);
194         }
195         buffer.append(',');
196         if (upperBound != null) {
197             buffer.append(upperBound);
198         }
199         buffer.append(upperBoundInclusive ? ']' : ')');
200         return buffer.toString();
201     }
202 }