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