001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.util.version; 020 021import java.util.Collections; 022import java.util.LinkedHashMap; 023import java.util.Locale; 024import java.util.Map; 025import java.util.Optional; 026 027/** 028 * The recognized generic version qualifiers. Qualifiers may apply a "shift" to versions (ie when sorting), if present. 029 * 030 * @since 2.0.17 031 */ 032public final class GenericQualifiers { 033 private GenericQualifiers() {} 034 035 public static final String LABEL_ALPHA = "alpha"; 036 037 public static final String LABEL_BETA = "beta"; 038 039 public static final String LABEL_MILESTONE = "milestone"; 040 041 public static final Integer QUALIFIER_ALPHA = -5; 042 043 public static final Integer QUALIFIER_BETA = -4; 044 045 public static final Integer QUALIFIER_MILESTONE = -3; 046 047 public static final Integer QUALIFIER_RC = -2; 048 049 public static final Integer QUALIFIER_SNAPSHOT = -1; 050 051 public static final Integer QUALIFIER_ZERO = 0; 052 053 public static final Integer QUALIFIER_SP = 1; 054 055 private static final Map<String, Integer> QUALIFIERS; 056 057 static { 058 Map<String, Integer> qualifiers = new LinkedHashMap<>(); 059 qualifiers.put(LABEL_ALPHA, QUALIFIER_ALPHA); 060 qualifiers.put(LABEL_BETA, QUALIFIER_BETA); 061 qualifiers.put(LABEL_MILESTONE, QUALIFIER_MILESTONE); 062 qualifiers.put("rc", QUALIFIER_RC); 063 qualifiers.put("cr", QUALIFIER_RC); 064 qualifiers.put("snapshot", QUALIFIER_SNAPSHOT); 065 qualifiers.put("ga", QUALIFIER_ZERO); 066 qualifiers.put("final", QUALIFIER_ZERO); 067 qualifiers.put("release", QUALIFIER_ZERO); 068 qualifiers.put("sp", QUALIFIER_SP); 069 QUALIFIERS = Collections.unmodifiableMap(qualifiers); 070 } 071 072 /** 073 * Returns qualifier (an {@link Integer} for given token, if detected. This method is used in {@link GenericVersion} 074 * that tokenizes version, and uses string token in call. The input must have {@link String#toLowerCase(Locale)} 075 * applied. 076 */ 077 static Optional<Integer> tokenQualifier(String token) { 078 return Optional.ofNullable(QUALIFIERS.get(token)); 079 } 080 081 /** 082 * Returns qualifier (an {@link Integer} for given string, if detected. Qualifier, if present, defines "shift", 083 * if negative, it is a "preview version" (e.g. alpha, beta, milestone, release candidate or snapshot), if zero, 084 * it is a "final version" (e.g. ga, final, release), if positive, it is a "service pack" version (e.g. sp). 085 * If no qualifier is detected, an empty optional is returned. 086 * 087 * @param token the string to analyze for qualifier, must not be {@code null} 088 */ 089 public static Optional<Integer> qualifier(String token) { 090 // most trivial preview version is "a1" 091 if (token.length() > 1) { 092 String v = token.toLowerCase(Locale.ENGLISH); 093 // simple case: full qualifier label is present (assuming once) 094 for (Map.Entry<String, Integer> entry : QUALIFIERS.entrySet()) { 095 String label = entry.getKey(); 096 int pos = v.indexOf(label); 097 if (pos > -1 098 && (pos == 0 || !Character.isLetter(v.charAt(pos - 1))) 099 && (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}