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