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 }