com.azure.core.util.ExpandableStringEnum Maven / Gradle / Ivy
Show all versions of azure-core Show documentation
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.core.util;
import com.azure.core.implementation.ReflectionUtils;
import com.azure.core.util.logging.ClientLogger;
import com.fasterxml.jackson.annotation.JsonValue;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import static java.lang.invoke.MethodType.methodType;
/**
* Base implementation for expandable, single string enums.
*
* @param a specific expandable enum type
*/
public abstract class ExpandableStringEnum> {
private static final Map, MethodHandle> CONSTRUCTORS = new ConcurrentHashMap<>();
private static final Map, ConcurrentHashMap>> VALUES
= new ConcurrentHashMap<>();
private static final ClientLogger LOGGER = new ClientLogger(ExpandableStringEnum.class);
private String name;
private Class clazz;
/**
* Creates a new instance of {@link ExpandableStringEnum} without a {@link #toString()} value.
*
* This constructor shouldn't be called as it will produce a {@link ExpandableStringEnum} which doesn't
* have a String enum value.
*
* @deprecated Use the {@link #fromString(String, Class)} factory method.
*/
@Deprecated
public ExpandableStringEnum() {
}
/**
* Creates an instance of the specific expandable string enum from a String.
*
* @param name The value to create the instance from.
* @param clazz The class of the expandable string enum.
* @param the class of the expandable string enum.
* @return The expandable string enum instance.
*
* @throws RuntimeException wrapping implementation class constructor exception (if any is thrown).
*/
@SuppressWarnings({"unchecked", "deprecation"})
protected static > T fromString(String name, Class clazz) {
if (name == null) {
return null;
}
ConcurrentHashMap clazzValues = VALUES.computeIfAbsent(clazz, key -> new ConcurrentHashMap<>());
T value = (T) clazzValues.get(name);
if (value != null) {
return value;
} else {
MethodHandle ctor = CONSTRUCTORS.computeIfAbsent(clazz, ExpandableStringEnum::getDefaultConstructor);
if (ctor == null) {
// logged in ExpandableStringEnum::getDefaultConstructor
return null;
}
try {
value = (T) ctor.invoke();
} catch (Throwable e) {
LOGGER.warning("Failed to create {}, default constructor threw exception", clazz.getName(), e);
return null;
}
return value.nameAndAddValue(name, value, clazz);
}
}
private static MethodHandle getDefaultConstructor(Class clazz) {
try {
MethodHandles.Lookup lookup = ReflectionUtils.getLookupToUse(clazz);
return lookup.findConstructor(clazz, methodType(void.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
LOGGER.verbose("Can't find or access default constructor for {}, make sure corresponding package is open to azure-core", clazz.getName(), e);
} catch (Exception e) {
LOGGER.verbose("Failed to get lookup for {}", clazz.getName(), e);
}
return null;
}
@SuppressWarnings("unchecked")
T nameAndAddValue(String name, T value, Class clazz) {
this.name = name;
this.clazz = clazz;
((ConcurrentHashMap) VALUES.get(clazz)).put(name, value);
return (T) this;
}
/**
* Gets a collection of all known values to an expandable string enum type.
*
* @param clazz the class of the expandable string enum.
* @param the class of the expandable string enum.
* @return A collection of all known values for the given {@code clazz}.
*/
@SuppressWarnings("unchecked")
protected static > Collection values(Class clazz) {
return new ArrayList((Collection) VALUES.getOrDefault(clazz, new ConcurrentHashMap<>()).values());
}
@Override
@JsonValue
public String toString() {
return this.name;
}
@Override
public int hashCode() {
return Objects.hash(this.clazz, this.name);
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
} else if (clazz == null || !clazz.isAssignableFrom(obj.getClass())) {
return false;
} else if (obj == this) {
return true;
} else if (this.name == null) {
return ((ExpandableStringEnum) obj).name == null;
} else {
return this.name.equals(((ExpandableStringEnum) obj).name);
}
}
}