001/* 002 * Copyright (c) 2023. JEFF Media GbR / mfnalex et al. 003 * 004 * This program is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program. If not, see <https://www.gnu.org/licenses/>. 016 */ 017 018package com.jeff_media.jefflib; 019 020import java.util.Arrays; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.EnumMap; 024import java.util.EnumSet; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Locale; 028import java.util.Map; 029import java.util.Objects; 030import java.util.Optional; 031import java.util.Set; 032import java.util.regex.Pattern; 033import java.util.stream.Collector; 034import java.util.stream.Collectors; 035import lombok.experimental.UtilityClass; 036 037/** 038 * Enum related methods 039 */ 040@UtilityClass 041public class EnumUtils { 042 043 private static final Map<Class<? extends Enum<?>>, Set<String>> ENUM_CACHE = new HashMap<>(); 044 private static final Map<Class<? extends Enum<?>>, List<? extends Enum<?>>> ENUM_ARRAY_CACHE = new HashMap<>(); 045 private static final Map<Class<? extends Enum<?>>, EnumMap<?, ?>> NEXT_ENUMS = new HashMap<>(); 046 047 /** 048 * Gets an EnumSet of the given Enum constants by their names. Enum constants that aren't found will print a warning. 049 * Case is ignored for Bukkit enums. 050 */ 051 public static <E extends Enum<E>> EnumSet<E> getEnumsFromListAsEnumSet(final Class<E> enumClazz, final List<String> list) { 052 return getEnumsFromList(enumClazz, list, Collectors.toCollection(() -> EnumSet.noneOf(enumClazz))); 053 } 054 055 /** 056 * Gets a Set of the given Enum constants by their names. Enum constants that aren't found will print a warning. 057 * Case is ignored for Bukkit enums. 058 */ 059 public static <E extends Enum<E>> Set<E> getEnumsFromListAsSet(final Class<E> enumClazz, final List<String> list) { 060 return getEnumsFromList(enumClazz, list, Collectors.toSet()); 061 } 062 063 /** 064 * Gets a Collection of the given Enum constants by their names. Enums constants that aren't found will print a warning. 065 * Case is ignored for Bukkit enums. 066 */ 067 public static <E extends Enum<E>, C extends Collection<E>> C getEnumsFromList(final Class<E> enumClazz, final List<String> list, final Collector<? super E, ?, C> collector) { 068 return list.stream().map(entry -> { 069 final Optional<E> result = getIfPresent(enumClazz, enumClazz.getName().startsWith("org.bukkit") ? entry.toUpperCase(Locale.ROOT) : entry); 070 if (!result.isPresent()) { 071 JeffLib.getPlugin().getLogger().severe("Could not find " + enumClazz.getSimpleName() + ": '" + entry + "'"); 072 return null; 073 } 074 return result.get(); 075 }).filter(Objects::nonNull).collect(collector); 076 } 077 078 /** 079 * Gets an {@link Optional} of a given Enum by its name 080 */ 081 public static <E extends Enum<E>> Optional<E> getIfPresent(final Class<E> enumClazz, final String value) { 082 final Set<String> enumSet = ENUM_CACHE.computeIfAbsent(enumClazz, EnumUtils::toStringSet); 083 return Optional.ofNullable(enumSet.contains(value) ? Enum.valueOf(enumClazz, value) : null); 084 } 085 086 private static Set<String> toStringSet(final Class<? extends Enum<?>> enumClazz) { 087 return Arrays.stream(enumClazz.getEnumConstants()).map(Enum::toString).collect(Collectors.toSet()); 088 } 089 090 /** 091 * Gets an EnumSet of the given Enum constants by a list of regex patterns. Example: 092 * <pre> 093 * materials: 094 * - "^((.+)_)*CHEST$" # matches CHEST, TRAPPED_CHEST, etc 095 * - "^((.+)_)*SHULKER_BOX$" # matches SHULKER_BOX, RED_SHULKER_BOX, etc 096 * - "^BARREL$" # matches only BARREL 097 * </pre> 098 */ 099 public static <E extends Enum<E>> EnumSet<E> getEnumsFromRegexList(final Class<E> enumClazz, final List<String> list) { 100 final EnumSet<E> result = EnumSet.noneOf(enumClazz); 101 for (final String regex : list) { 102 final Pattern pattern = Pattern.compile(regex); 103 for (final E e : enumClazz.getEnumConstants()) { 104 if (result.contains(e)) continue; 105 final String name = e.name(); 106 if (pattern.matcher(name).matches()) { 107 result.add(e); 108 } 109 } 110 } 111 return result; 112 } 113 114 /** 115 * Gets a random value of the given Enum class. Values are cached, so it doesn't have to call values() all the time. 116 */ 117 public static <E extends Enum<E>> E getRandomElement(final Class<E> enumClazz) { 118 final List<E> values = getValues(enumClazz); 119 return values.get(JeffLib.getThreadLocalRandom().nextInt(values.size())); 120 } 121 122 /** 123 * Returns all elements of the given enum class. Unlike calling values() on an element instance, 124 * or calling getEnumConstants() on an enum class, this will cache the delivered array and 125 * doesn't have to create a new one everytime. 126 * The returned list is unmodifiable. 127 */ 128 @SuppressWarnings("unchecked") 129 public static <E extends Enum<E>> List<E> getValues(final Class<E> enumClazz) { 130 List<E> values = (List<E>) ENUM_ARRAY_CACHE.get(enumClazz); 131 if (values == null) { 132 values = Collections.unmodifiableList(Arrays.asList(enumClazz.getEnumConstants())); 133 ENUM_ARRAY_CACHE.put(enumClazz, values); 134 } 135 return values; 136 } 137 138 /** 139 * Gets the next element of the given enum class by its ordinal. 140 * For example, if your enum class has three declared values A, B and C, then calling this method with A will return B, 141 * calling it with B will return C, and calling it with C will return A. 142 * The next element of each element is cached and does not require to call values() all the time. 143 */ 144 @SuppressWarnings("unchecked") 145 public static <E extends Enum<E>> E getNextElement(final E e) { 146 final Class<E> enumClazz = (Class<E>) e.getClass(); 147 final EnumMap<E, E> nextEnums = (EnumMap<E, E>) NEXT_ENUMS.computeIfAbsent(enumClazz, __ -> new EnumMap<E, E>(enumClazz)); 148 E next = nextEnums.get(e); 149 if (next == null) { 150 final int ordinal = e.ordinal(); 151 final List<E> values = getValues(enumClazz); 152 next = values.get((ordinal + 1) % values.size()); 153 nextEnums.put(e, next); 154 } 155 return next; 156 } 157 158 159} 160