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.apache.maven.artifact.versioning;
20  
21  /**
22   * Default implementation of artifact versioning.
23   *
24   */
25  public class DefaultArtifactVersion implements ArtifactVersion {
26      private Integer majorVersion;
27  
28      private Integer minorVersion;
29  
30      private Integer incrementalVersion;
31  
32      private Integer buildNumber;
33  
34      private String qualifier;
35  
36      private ComparableVersion comparable;
37  
38      public DefaultArtifactVersion(String version) {
39          parseVersion(version);
40      }
41  
42      @Override
43      public int hashCode() {
44          // equals is defined as compareTo == 0, so the hash must follow ordering equality:
45          // "1-ga" and "1" are equal here and must hash alike, which the structural
46          // ComparableVersion.hashCode() does not guarantee
47          return 11 + comparable.orderingHashCode();
48      }
49  
50      @Override
51      public boolean equals(Object other) {
52          if (this == other) {
53              return true;
54          }
55  
56          if (other instanceof ArtifactVersion artifactVersion) {
57              return compareTo(artifactVersion) == 0;
58          }
59  
60          return false;
61      }
62  
63      @Override
64      public int compareTo(ArtifactVersion otherVersion) {
65          if (otherVersion instanceof DefaultArtifactVersion defaultArtifactVersion) {
66              return this.comparable.compareTo(defaultArtifactVersion.comparable);
67          } else {
68              return compareTo(new DefaultArtifactVersion(otherVersion.toString()));
69          }
70      }
71  
72      @Override
73      public int getMajorVersion() {
74          return majorVersion != null ? majorVersion : 0;
75      }
76  
77      @Override
78      public int getMinorVersion() {
79          return minorVersion != null ? minorVersion : 0;
80      }
81  
82      @Override
83      public int getIncrementalVersion() {
84          return incrementalVersion != null ? incrementalVersion : 0;
85      }
86  
87      @Override
88      public int getBuildNumber() {
89          return buildNumber != null ? buildNumber : 0;
90      }
91  
92      @Override
93      public String getQualifier() {
94          return qualifier;
95      }
96  
97      @Override
98      public final void parseVersion(String version) {
99          comparable = new ComparableVersion(version);
100 
101         int index = version.indexOf('-');
102 
103         String part1;
104         String part2 = null;
105 
106         if (index < 0) {
107             part1 = version;
108         } else {
109             part1 = version.substring(0, index);
110             part2 = version.substring(index + 1);
111         }
112 
113         if (part2 != null) {
114             if (part2.length() == 1 || !part2.startsWith("0")) {
115                 buildNumber = tryParseInt(part2);
116                 if (buildNumber == null) {
117                     qualifier = part2;
118                 }
119             } else {
120                 qualifier = part2;
121             }
122         }
123 
124         if ((!part1.contains(".")) && !part1.startsWith("0")) {
125             majorVersion = tryParseInt(part1);
126             if (majorVersion == null) {
127                 // qualifier is the whole version, including "-"
128                 qualifier = version;
129                 buildNumber = null;
130             }
131         } else {
132             boolean fallback = false;
133 
134             String[] tok = part1.split("\\.");
135             int idx = 0;
136             if (idx < tok.length) {
137                 majorVersion = getNextIntegerToken(tok[idx++]);
138                 if (majorVersion == null) {
139                     fallback = true;
140                 }
141             } else {
142                 fallback = true;
143             }
144             if (idx < tok.length) {
145                 minorVersion = getNextIntegerToken(tok[idx++]);
146                 if (minorVersion == null) {
147                     fallback = true;
148                 }
149             }
150             if (idx < tok.length) {
151                 incrementalVersion = getNextIntegerToken(tok[idx++]);
152                 if (incrementalVersion == null) {
153                     fallback = true;
154                 }
155             }
156             if (idx < tok.length) {
157                 qualifier = tok[idx];
158                 fallback = isDigits(qualifier);
159             }
160 
161             // string tokenizer won't detect these and ignores them
162             if (part1.contains("..") || part1.startsWith(".") || part1.endsWith(".")) {
163                 fallback = true;
164             }
165 
166             if (fallback) {
167                 // qualifier is the whole version, including "-"
168                 qualifier = version;
169                 majorVersion = null;
170                 minorVersion = null;
171                 incrementalVersion = null;
172                 buildNumber = null;
173             }
174         }
175     }
176 
177     private static boolean isDigits(String cs) {
178         if (cs == null || cs.isEmpty()) {
179             return false;
180         }
181         final int sz = cs.length();
182         for (int i = 0; i < sz; i++) {
183             if (!Character.isDigit(cs.charAt(i))) {
184                 return false;
185             }
186         }
187         return true;
188     }
189 
190     private static Integer getNextIntegerToken(String s) {
191         if ((s.length() > 1) && s.startsWith("0")) {
192             return null;
193         }
194         return tryParseInt(s);
195     }
196 
197     private static Integer tryParseInt(String s) {
198         // for performance, check digits instead of relying later on catching NumberFormatException
199         if (!isDigits(s)) {
200             return null;
201         }
202 
203         try {
204             long longValue = Long.parseLong(s);
205             if (longValue > Integer.MAX_VALUE) {
206                 return null;
207             }
208             return (int) longValue;
209         } catch (NumberFormatException e) {
210             // should never happen since checked isDigits(s) before
211             return null;
212         }
213     }
214 
215     @Override
216     public String toString() {
217         return comparable.toString();
218     }
219 }