1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.extension.version.internal

File DefaultVersionRange.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

96
123
19
1
468
268
76
0.62
6.47
19
4

Classes

Class Line # Actions
DefaultVersionRange 48 123 0% 76 44
0.8151260681.5%
 

Contributing tests

This file is covered by 21 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.extension.version.internal;
21   
22    import java.io.IOException;
23    import java.io.ObjectInputStream;
24    import java.io.ObjectOutputStream;
25    import java.text.MessageFormat;
26    import java.util.Objects;
27   
28    import org.apache.commons.lang3.StringUtils;
29    import org.apache.commons.lang3.builder.HashCodeBuilder;
30    import org.xwiki.extension.version.InvalidVersionRangeException;
31    import org.xwiki.extension.version.Version;
32    import org.xwiki.extension.version.VersionRange;
33   
34    /**
35    * Default implementation of {@link VersionRange}.
36    * <p>
37    * Based on AETHER implementation which is itself based on Maven specifications
38    * <p>
39    * (,1.0]
40    * <p>
41    * org.sonatype.aether.util.version.GenericVersionRange has been rewritten because it's impossible to extends it or even
42    * access its details to properly implements {@link #isCompatible(VersionRange)} for example.
43    *
44    * @see org.sonatype.aether.util.version.GenericVersionRange
45    * @version $Id: b0589978bc3247fc57a4c82c266ae09a7e9eab71 $
46    * @since 4.0M1
47    */
 
48    public class DefaultVersionRange implements VersionRange
49    {
50    /**
51    * Serialization identifier.
52    */
53    private static final long serialVersionUID = 1L;
54   
55    /**
56    * The character used to separated version ranges.
57    */
58    private static final char RANGE_SEPARATOR = ',';
59   
60    /**
61    * The minimum version.
62    */
63    private Version lowerBound;
64   
65    /**
66    * Indicate if the minimum version is included in the range.
67    */
68    private boolean lowerBoundInclusive;
69   
70    /**
71    * The maximum version.
72    */
73    private Version upperBound;
74   
75    /**
76    * Indicate if the maximum version is included in the range.
77    */
78    private boolean upperBoundInclusive;
79   
80    /**
81    * The string representation of the range.
82    */
83    private String value;
84   
85    /**
86    * @param rawRange the version range to parse
87    * @throws InvalidVersionRangeException error when parsing version range
88    */
 
89  70 toggle public DefaultVersionRange(String rawRange) throws InvalidVersionRangeException
90    {
91  70 setRange(rawRange);
92    }
93   
94    /**
95    * @param lowerBound the minimum version
96    * @param lowerBoundInclusive indicate if the minimum version is included in the range
97    * @param upperBound the maximum version
98    * @param upperBoundInclusive indicate if the maximum version is included in the range
99    */
 
100  9 toggle public DefaultVersionRange(Version lowerBound, boolean lowerBoundInclusive, Version upperBound,
101    boolean upperBoundInclusive)
102    {
103  9 this.lowerBound = lowerBound;
104  9 this.lowerBoundInclusive = lowerBoundInclusive;
105  9 this.upperBound = upperBound;
106  9 this.upperBoundInclusive = upperBoundInclusive;
107    }
108   
109    /**
110    * @param rawRange the version range to parse
111    * @throws InvalidVersionRangeException error when parsing version range
112    */
 
113  70 toggle private void setRange(String rawRange) throws InvalidVersionRangeException
114    {
115  70 this.value = rawRange;
116   
117    // parse
118   
119  70 String range = this.value;
120   
121  70 this.lowerBoundInclusive = findLowerBoundInclusive(range);
122  69 this.upperBoundInclusive = findUpperBoundInclusive(range);
123   
124  68 range = range.substring(1, range.length() - 1);
125   
126  68 int index = range.indexOf(RANGE_SEPARATOR);
127   
128  68 if (index < 0) {
129  23 if (!this.lowerBoundInclusive || !this.upperBoundInclusive) {
130  0 throw new InvalidVersionRangeException(MessageFormat.format(
131    "Invalid version range [{0}], single version must be surrounded by []", rawRange));
132    }
133   
134  23 this.upperBound = new DefaultVersion(range.trim());
135  23 this.lowerBound = this.upperBound;
136    } else {
137  45 String parsedLowerBound = range.substring(0, index).trim();
138  45 String parsedUpperBound = range.substring(index + 1).trim();
139   
140    // more than two bounds, e.g. (1,2,3)
141  45 if (StringUtils.contains(parsedUpperBound, RANGE_SEPARATOR)) {
142  1 throw new InvalidVersionRangeException(MessageFormat.format(
143    "Invalid version range [{0}], bounds may not contain additional ','", rawRange));
144    }
145   
146  44 this.lowerBound = parsedLowerBound.length() > 0 ? new DefaultVersion(parsedLowerBound) : null;
147  44 this.upperBound = parsedUpperBound.length() > 0 ? new DefaultVersion(parsedUpperBound) : null;
148   
149  44 if (this.upperBound != null && this.lowerBound != null) {
150  26 if (this.upperBound.compareTo(this.lowerBound) < 0) {
151  1 throw new InvalidVersionRangeException(MessageFormat.format(
152    "Invalid version range [{0}], lower bound must not be greater than upper bound", rawRange));
153    }
154    }
155    }
156    }
157   
158    /**
159    * @param range the range to parse
160    * @return true if the provided range has the minimum version included
161    * @throws InvalidVersionRangeException invalid range
162    */
 
163  70 toggle private boolean findLowerBoundInclusive(String range) throws InvalidVersionRangeException
164    {
165  70 if (VersionUtils.startsWith(range, '[')) {
166  59 return true;
167  11 } else if (VersionUtils.startsWith(range, '(')) {
168  10 return false;
169    } else {
170  1 throw new InvalidVersionRangeException(MessageFormat.format(
171    "Invalid version range [{0}], a range must start with either [ or (", range));
172    }
173    }
174   
175    /**
176    * @param range the range to parse
177    * @return true if the provided range has the maximum version included
178    * @throws InvalidVersionRangeException invalid range
179    */
 
180  69 toggle private boolean findUpperBoundInclusive(String range) throws InvalidVersionRangeException
181    {
182  69 if (VersionUtils.endsWith(range, ']')) {
183  45 return true;
184  24 } else if (VersionUtils.endsWith(range, ')')) {
185  23 return false;
186    } else {
187  1 throw new InvalidVersionRangeException(MessageFormat.format(
188    "Invalid version range [{0}], a range must end with either [ or (", range));
189    }
190    }
191   
 
192  26 toggle @Override
193    public boolean containsVersion(Version version)
194    {
195  26 if (version instanceof DefaultVersion) {
196  26 return containsVersion((DefaultVersion) version);
197    } else {
198  0 return containsVersion(new DefaultVersion(version.getValue()));
199    }
200    }
201   
202    /**
203    * @param version the version to search
204    * @return true if the version is part of the range, false otherwise
205    */
 
206  32 toggle public boolean containsVersion(DefaultVersion version)
207    {
208  32 if (this.lowerBound != null) {
209  26 int comparison = this.lowerBound.compareTo(version);
210   
211  26 if (comparison == 0 && !this.lowerBoundInclusive) {
212  1 return false;
213    }
214  25 if (comparison > 0) {
215  3 return false;
216    }
217    }
218   
219  28 if (this.upperBound != null) {
220  14 int comparison = this.upperBound.compareTo(version);
221   
222  14 if (comparison == 0 && !this.upperBoundInclusive) {
223  3 return false;
224    }
225  11 if (comparison < 0) {
226  5 return false;
227    }
228    }
229   
230  20 return true;
231    }
232   
 
233  9 toggle @Override
234    public String getValue()
235    {
236  9 if (this.value == null) {
237  9 StringBuilder buffer = new StringBuilder();
238   
239  9 buffer.append(this.lowerBoundInclusive ? '[' : '(');
240  9 if (this.lowerBound != null) {
241  9 buffer.append(this.lowerBound);
242    }
243  9 buffer.append(RANGE_SEPARATOR);
244  9 if (this.upperBound != null) {
245  5 buffer.append(this.upperBound);
246    }
247  9 buffer.append(this.upperBoundInclusive ? ']' : ')');
248   
249  9 this.value = buffer.toString();
250    }
251   
252  9 return this.value;
253    }
254   
 
255  2 toggle @Override
256    public boolean isCompatible(VersionRange otherRange)
257    {
258  2 boolean compatible;
259   
260  2 if (equals(otherRange)) {
261  0 compatible = true;
262    } else {
263  2 if (otherRange instanceof DefaultVersionRange) {
264  2 compatible = isCompatible((DefaultVersionRange) otherRange);
265    } else {
266  0 try {
267  0 compatible = isCompatible(new DefaultVersionRange(otherRange.getValue()));
268    } catch (InvalidVersionRangeException e) {
269  0 compatible = false;
270    }
271    }
272    }
273   
274  2 return compatible;
275    }
276   
277    /**
278    * Indicate if the provided version range is compatible with the provided version range.
279    *
280    * @param otherRange the version range to compare
281    * @return true if the two version ranges are compatibles, false otherwise
282    */
 
283  12 toggle public boolean isCompatible(DefaultVersionRange otherRange)
284    {
285  12 int lowerCompare =
286    compareTo(this.lowerBound, this.lowerBoundInclusive, otherRange.lowerBound, otherRange.lowerBoundInclusive,
287    false);
288  12 int upperCompare =
289    compareTo(this.upperBound, this.upperBoundInclusive, otherRange.upperBound, otherRange.upperBoundInclusive,
290    true);
291   
292    // Both ranges have one bound in common
293  12 if (lowerCompare == 0 || upperCompare == 0) {
294  4 return true;
295    }
296   
297    // This range is included in the provided range
298  8 if (lowerCompare > 0 && upperCompare < 0) {
299  0 return true;
300    }
301   
302    // The provided range is included in this range
303  8 if (lowerCompare < 0 && upperCompare > 0) {
304  0 return true;
305    }
306   
307    // Validate intersections
308  8 return lowerCompare < 0 ? isCompatible(this.upperBound, this.upperBoundInclusive, otherRange.lowerBound,
309    otherRange.lowerBoundInclusive) : isCompatible(otherRange.upperBound, otherRange.upperBoundInclusive,
310    this.lowerBound, this.lowerBoundInclusive);
311    }
312   
313    /**
314    * @param version1 the left range version
315    * @param included1 indicate of the left range version is included
316    * @param version2 the right range version
317    * @param included2 indicate of the right range version is included
318    * @param upper indicate the provided version are upper or lower bounds
319    * @return a negative integer, zero, or a positive integer as the left version is less than, equal to, or greater
320    * than the right version
321    */
 
322  24 toggle private int compareTo(Version version1, boolean included1, Version version2, boolean included2, boolean upper)
323    {
324  24 int compare;
325   
326  24 if (version1 == null) {
327  4 compare = version2 == null ? 0 : (upper ? 1 : -1);
328    } else {
329  20 if (version2 == null) {
330  4 compare = upper ? -1 : 1;
331    } else {
332  16 compare = compareNotNull(version1, included1, version2, included2, upper);
333    }
334    }
335   
336  24 return compare;
337    }
338   
339    /**
340    * @param version1 the left range version
341    * @param included1 indicate of the left range version is included
342    * @param version2 the right range version
343    * @param included2 indicate of the right range version is included
344    * @param upper indicate the provided version are upper or lower bounds
345    * @return a negative integer, zero, or a positive integer as the left version is less than, equal to, or greater
346    * than the right version
347    */
 
348  16 toggle private int compareNotNull(Version version1, boolean included1, Version version2, boolean included2, boolean upper)
349    {
350  16 int compare = version1.compareTo(version2);
351   
352  16 if (compare == 0) {
353  5 if (included1 != included2) {
354  0 compare = included1 ? (upper ? -1 : 1) : (upper ? 1 : -1);
355    }
356    }
357   
358  16 return compare;
359    }
360   
361    /**
362    * @param upper maximum version of the right range
363    * @param upperInclusive indicate if maximum version is included in the range
364    * @param lower minimum version of the left range
365    * @param lowerInclusive indicate if the minimum version is included in the range
366    * @return true if the two version ranges are compatibles, false otherwise
367    */
 
368  8 toggle private boolean isCompatible(Version upper, boolean upperInclusive, Version lower, boolean lowerInclusive)
369    {
370  8 boolean compatible = true;
371   
372  8 if (upper != null) {
373  8 if (lower != null) {
374  8 int comparison = upper.compareTo(lower);
375   
376  8 if (comparison > 0) {
377  2 compatible = true;
378  6 } else if (comparison < 0) {
379  2 compatible = false;
380    } else {
381  4 compatible = upperInclusive && lowerInclusive;
382    }
383    }
384    }
385   
386  8 return compatible;
387    }
388   
389    // Object
390   
 
391  1 toggle @Override
392    public String toString()
393    {
394  1 return getValue();
395    }
396   
 
397  7 toggle @Override
398    public int hashCode()
399    {
400  7 HashCodeBuilder builder = new HashCodeBuilder(17, 31);
401   
402  7 builder.append(this.upperBound);
403  7 builder.append(this.upperBoundInclusive ? 1 : 0);
404  7 builder.append(this.lowerBound);
405  7 builder.append(this.lowerBoundInclusive ? 1 : 0);
406   
407  7 return builder.toHashCode();
408    }
409   
 
410  14 toggle @Override
411    public boolean equals(Object obj)
412    {
413  14 if (obj == this) {
414  0 return true;
415    }
416   
417  14 boolean equals;
418   
419  14 if (obj instanceof DefaultVersionRange) {
420  14 equals = equals((DefaultVersionRange) obj);
421  0 } else if (obj instanceof VersionRange) {
422  0 try {
423  0 equals = equals(new DefaultVersionRange(((VersionRange) obj).getValue()));
424    } catch (InvalidVersionRangeException e) {
425  0 equals = false;
426    }
427    } else {
428  0 equals = false;
429    }
430   
431  14 return equals;
432    }
433   
434    /**
435    * @param version the version
436    * @return true if the provided version is equals to this version
437    */
 
438  14 toggle public boolean equals(DefaultVersionRange version)
439    {
440  14 return this.upperBoundInclusive == version.upperBoundInclusive
441    && this.lowerBoundInclusive == version.lowerBoundInclusive
442    && Objects.equals(this.upperBound, version.upperBound)
443    && Objects.equals(this.lowerBound, version.lowerBound);
444    }
445   
446    // Serializable
447   
448    /**
449    * @param out the stream
450    * @throws IOException error when serializing the version
451    */
 
452  0 toggle private void writeObject(ObjectOutputStream out) throws IOException
453    {
454  0 out.writeObject(getValue());
455    }
456   
457    /**
458    * @param in the stream
459    * @throws IOException error when unserializing the version
460    * @throws ClassNotFoundException error when unserializing the version
461    * @throws InvalidVersionRangeException error when unserializing the version
462    */
 
463  0 toggle private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException,
464    InvalidVersionRangeException
465    {
466  0 setRange((String) in.readObject());
467    }
468    }