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.eclipse.aether.util.version;
20  
21  import java.util.Collections;
22  import java.util.LinkedHashMap;
23  import java.util.Locale;
24  import java.util.Map;
25  import java.util.Optional;
26  
27  /**
28   * The recognized generic version qualifiers. Qualifiers may apply a "shift" to versions (ie when sorting), if present.
29   *
30   * @since 2.0.17
31   */
32  public final class GenericQualifiers {
33      private GenericQualifiers() {}
34  
35      public static final String LABEL_ALPHA = "alpha";
36  
37      public static final String LABEL_BETA = "beta";
38  
39      public static final String LABEL_MILESTONE = "milestone";
40  
41      public static final Integer QUALIFIER_ALPHA = -5;
42  
43      public static final Integer QUALIFIER_BETA = -4;
44  
45      public static final Integer QUALIFIER_MILESTONE = -3;
46  
47      public static final Integer QUALIFIER_RC = -2;
48  
49      public static final Integer QUALIFIER_SNAPSHOT = -1;
50  
51      public static final Integer QUALIFIER_ZERO = 0;
52  
53      public static final Integer QUALIFIER_SP = 1;
54  
55      private static final Map<String, Integer> QUALIFIERS;
56  
57      static {
58          Map<String, Integer> qualifiers = new LinkedHashMap<>();
59          qualifiers.put(LABEL_ALPHA, QUALIFIER_ALPHA);
60          qualifiers.put(LABEL_BETA, QUALIFIER_BETA);
61          qualifiers.put(LABEL_MILESTONE, QUALIFIER_MILESTONE);
62          qualifiers.put("rc", QUALIFIER_RC);
63          qualifiers.put("cr", QUALIFIER_RC);
64          qualifiers.put("snapshot", QUALIFIER_SNAPSHOT);
65          qualifiers.put("ga", QUALIFIER_ZERO);
66          qualifiers.put("final", QUALIFIER_ZERO);
67          qualifiers.put("release", QUALIFIER_ZERO);
68          qualifiers.put("sp", QUALIFIER_SP);
69          QUALIFIERS = Collections.unmodifiableMap(qualifiers);
70      }
71  
72      /**
73       * Returns qualifier (an {@link Integer} for given token, if detected. This method is used in {@link GenericVersion}
74       * that tokenizes version, and uses string token in call. The input must have {@link String#toLowerCase(Locale)}
75       * applied.
76       */
77      static Optional<Integer> tokenQualifier(String token) {
78          return Optional.ofNullable(QUALIFIERS.get(token));
79      }
80  
81      /**
82       * Returns qualifier (an {@link Integer} for given string, if detected. Qualifier, if present, defines "shift",
83       * if negative, it is a "preview version" (e.g. alpha, beta, milestone, release candidate or snapshot), if zero,
84       * it is a "final version" (e.g. ga, final, release), if positive, it is a "service pack" version (e.g. sp).
85       * If no qualifier is detected, an empty optional is returned.
86       *
87       * @param token the string to analyze for qualifier, must not be {@code null}
88       */
89      public static Optional<Integer> qualifier(String token) {
90          // most trivial preview version is "a1"
91          if (token.length() > 1) {
92              String v = token.toLowerCase(Locale.ENGLISH);
93              // simple case: full qualifier label is present (assuming once)
94              for (Map.Entry<String, Integer> entry : QUALIFIERS.entrySet()) {
95                  String label = entry.getKey();
96                  int pos = v.indexOf(label);
97                  if (pos > -1
98                          && (pos == 0 || !Character.isLetter(v.charAt(pos - 1)))
99                          && (pos >= v.length() - label.length()
100                                 || !Character.isLetter(v.charAt(pos + label.length())))) {
101                     // it must be surrounded by "transition" (non-char; to avoid "rc" detection in "1.0-arc")
102                     return Optional.of(entry.getValue());
103                 }
104             }
105             // complex case: contains 'a', 'b' or 'm' followed immediately by number
106             for (char ch : new char[] {'a', 'b', 'm'}) {
107                 int idx = v.lastIndexOf(ch);
108                 if (idx > -1 && v.length() > idx + 1) {
109                     if (Character.isDigit(v.charAt(idx + 1))) {
110                         if (ch == 'a') {
111                             return Optional.of(QUALIFIER_ALPHA);
112                         } else if (ch == 'b') {
113                             return Optional.of(QUALIFIER_BETA);
114                         } else {
115                             return Optional.of(QUALIFIER_MILESTONE);
116                         }
117                     }
118                 }
119             }
120         }
121         return Optional.empty();
122     }
123 }