View Javadoc
1   package org.apache.maven.shared.release.versions;
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 java.util.Arrays;
23  import java.util.List;
24  import java.util.regex.Matcher;
25  import java.util.regex.Pattern;
26  
27  import org.apache.maven.artifact.ArtifactUtils;
28  import org.codehaus.plexus.util.StringUtils;
29  
30  /**
31   * 
32   */
33  public class Version
34      implements Comparable<Version>
35  {
36      private final AetherVersion aetherVersion;
37  
38      private final MavenArtifactVersion mavenArtifactVersion;
39  
40      private final String strVersion;
41  
42      private final List<String> digits;
43  
44      private String annotation;
45  
46      private String annotationRevision;
47  
48      private final String buildSpecifier;
49  
50      private String annotationSeparator;
51  
52      private String annotationRevSeparator;
53  
54      private final String buildSeparator;
55  
56      private static final int DIGITS_INDEX = 1;
57  
58      private static final int ANNOTATION_SEPARATOR_INDEX = 2;
59  
60      private static final int ANNOTATION_INDEX = 3;
61  
62      private static final int ANNOTATION_REV_SEPARATOR_INDEX = 4;
63  
64      private static final int ANNOTATION_REVISION_INDEX = 5;
65  
66      private static final int BUILD_SEPARATOR_INDEX = 6;
67  
68      private static final int BUILD_SPECIFIER_INDEX = 7;
69  
70      private static final String SNAPSHOT_IDENTIFIER = "SNAPSHOT";
71  
72      private static final String DIGIT_SEPARATOR_STRING = ".";
73  
74      public static final Pattern STANDARD_PATTERN = Pattern.compile( "^((?:\\d+\\.)*\\d+)" // digit(s) and '.' repeated -
75                                                                                            // followed by digit (version
76                                                                                            // digits 1.22.0, etc)
77          + "([-_])?" // optional - or _ (annotation separator)
78          + "([a-zA-Z]*)" // alpha characters (looking for annotation - alpha, beta, RC, etc.)
79          + "([-_])?" // optional - or _ (annotation revision separator)
80          + "(\\d*)" // digits (any digits after rc or beta is an annotation revision)
81          + "(?:([-_])?(.*?))?$" ); // - or _ followed everything else (build specifier)
82  
83      /* *
84       * cmaki 02242009 FIX for non-digit release numbers, e.g. trunk-SNAPSHOT or just SNAPSHOT This alternate pattern
85       * supports version numbers like: trunk-SNAPSHOT branchName-SNAPSHOT SNAPSHOT
86       */
87      // for SNAPSHOT releases only (possible versions include: trunk-SNAPSHOT or SNAPSHOT)
88      public static final Pattern ALTERNATE_PATTERN = Pattern.compile( "^(SNAPSHOT|[a-zA-Z]+[_-]SNAPSHOT)" );
89  
90      public Version( String version )
91          throws VersionParseException
92      {
93          this.strVersion = version;
94          this.aetherVersion = new AetherVersion( version );
95          this.mavenArtifactVersion = new MavenArtifactVersion( version );
96  
97          // FIX for non-digit release numbers, e.g. trunk-SNAPSHOT or just SNAPSHOT
98          Matcher matcher = ALTERNATE_PATTERN.matcher( strVersion );
99          // TODO: hack because it didn't support "SNAPSHOT"
100         if ( matcher.matches() )
101         {
102             annotation = null;
103             digits = null;
104             buildSpecifier = version;
105             buildSeparator = null;
106             return;
107         }
108 
109         Matcher m = STANDARD_PATTERN.matcher( strVersion );
110         if ( m.matches() )
111         {
112             digits = parseDigits( m.group( DIGITS_INDEX ) );
113             if ( !SNAPSHOT_IDENTIFIER.equals( m.group( ANNOTATION_INDEX ) ) )
114             {
115                 annotationSeparator = m.group( ANNOTATION_SEPARATOR_INDEX );
116                 annotation = nullIfEmpty( m.group( ANNOTATION_INDEX ) );
117 
118                 if ( StringUtils.isNotEmpty( m.group( ANNOTATION_REV_SEPARATOR_INDEX ) )
119                     && StringUtils.isEmpty( m.group( ANNOTATION_REVISION_INDEX ) ) )
120                 {
121                     // The build separator was picked up as the annotation revision separator
122                     buildSeparator = m.group( ANNOTATION_REV_SEPARATOR_INDEX );
123                     buildSpecifier = nullIfEmpty( m.group( BUILD_SPECIFIER_INDEX ) );
124                 }
125                 else
126                 {
127                     annotationRevSeparator = m.group( ANNOTATION_REV_SEPARATOR_INDEX );
128                     annotationRevision = nullIfEmpty( m.group( ANNOTATION_REVISION_INDEX ) );
129 
130                     buildSeparator = m.group( BUILD_SEPARATOR_INDEX );
131                     buildSpecifier = nullIfEmpty( m.group( BUILD_SPECIFIER_INDEX ) );
132                 }
133             }
134             else
135             {
136                 // Annotation was "SNAPSHOT" so populate the build specifier with that data
137                 buildSeparator = m.group( ANNOTATION_SEPARATOR_INDEX );
138                 buildSpecifier = nullIfEmpty( m.group( ANNOTATION_INDEX ) );
139             }
140         }
141         else
142         {
143             throw new VersionParseException( "Unable to parse the version string: \"" + version + "\"" );
144         }
145     }
146 
147     public boolean isSnapshot()
148     {
149         return ArtifactUtils.isSnapshot( strVersion );
150     }
151 
152     public String toString()
153     {
154         return strVersion;
155     }
156 
157     protected static String getVersionString( Version info, String buildSpecifier, String buildSeparator )
158     {
159         StringBuilder sb = new StringBuilder();
160 
161         if ( info.digits != null )
162         {
163             sb.append( joinDigitString( info.digits ) );
164         }
165 
166         if ( StringUtils.isNotEmpty( info.annotation ) )
167         {
168             sb.append( StringUtils.defaultString( info.annotationSeparator ) );
169             sb.append( info.annotation );
170         }
171 
172         if ( StringUtils.isNotEmpty( info.annotationRevision ) )
173         {
174             if ( StringUtils.isEmpty( info.annotation ) )
175             {
176                 sb.append( StringUtils.defaultString( info.annotationSeparator ) );
177             }
178             else
179             {
180                 sb.append( StringUtils.defaultString( info.annotationRevSeparator ) );
181             }
182             sb.append( info.annotationRevision );
183         }
184 
185         if ( StringUtils.isNotEmpty( buildSpecifier ) )
186         {
187             sb.append( StringUtils.defaultString( buildSeparator ) );
188             sb.append( buildSpecifier );
189         }
190 
191         return sb.toString();
192     }
193 
194     /**
195      * Simply joins the items in the list with "." period
196      * 
197      * @param digits
198      */
199     protected static String joinDigitString( List<String> digits )
200     {
201         return digits != null ? StringUtils.join( digits.iterator(), DIGIT_SEPARATOR_STRING ) : null;
202     }
203 
204     /**
205      * Splits the string on "." and returns a list containing each digit.
206      * 
207      * @param strDigits
208      */
209     private List<String> parseDigits( String strDigits )
210     {
211         return Arrays.asList( StringUtils.split( strDigits, DIGIT_SEPARATOR_STRING ) );
212     }
213 
214     private static String nullIfEmpty( String s )
215     {
216         return StringUtils.isEmpty( s ) ? null : s;
217     }
218 
219     public List<String> getDigits()
220     {
221         return digits;
222     }
223 
224     public String getAnnotation()
225     {
226         return annotation;
227     }
228 
229     public String getAnnotationRevSeparator()
230     {
231         return annotationRevSeparator;
232     }
233 
234     public String getAnnotationRevision()
235     {
236         return annotationRevision;
237     }
238 
239     public String getBuildSeparator()
240     {
241         return buildSeparator;
242     }
243 
244     public String getBuildSpecifier()
245     {
246         return buildSpecifier;
247     }
248 
249     /**
250      * @throws VersionComparisonConflictException if {@link org.eclipse.aether.version.Version} and
251      *             {@link org.apache.maven.artifact.versioning.ArtifactVersion ArtifactVersion} give different results
252      */
253     public int compareTo( Version other )
254         throws VersionComparisonConflictException
255     {
256         int aetherComparisonResult = this.aetherVersion.compareTo( other.aetherVersion );
257         int mavenComparisonResult = this.mavenArtifactVersion.compareTo( other.mavenArtifactVersion );
258 
259         if ( aetherComparisonResult < 0 && mavenComparisonResult < 0 )
260         {
261             return -1;
262         }
263         else if ( aetherComparisonResult == 0 && mavenComparisonResult == 0 )
264         {
265             return 0;
266         }
267         else if ( aetherComparisonResult > 0 && mavenComparisonResult > 0 )
268         {
269             return 1;
270         }
271         else
272         {
273             throw new VersionComparisonConflictException( this.strVersion, other.strVersion, aetherComparisonResult,
274                                                           mavenComparisonResult );
275         }
276     }
277 
278 }