001/* 002 * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.util; 017 018import com.mybatisflex.annotation.EnumValue; 019import com.mybatisflex.core.exception.FlexExceptions; 020import org.apache.ibatis.util.MapUtil; 021 022import java.lang.reflect.Field; 023import java.lang.reflect.Method; 024import java.lang.reflect.Modifier; 025import java.util.Map; 026import java.util.concurrent.ConcurrentHashMap; 027 028public class EnumWrapper<E extends Enum<E>> { 029 030 private static final Map<Class, EnumWrapper> cache = new ConcurrentHashMap<>(); 031 032 private boolean hasEnumValueAnnotation = false; 033 034 private Class<?> enumClass; 035 private E[] enums; 036 private Field property; 037 private Class<?> propertyType; 038 private Method getterMethod; 039 040 public static <R extends Enum<R>> EnumWrapper<R> of(Class<?> enumClass) { 041 return MapUtil.computeIfAbsent(cache, enumClass, EnumWrapper::new); 042 } 043 044 public EnumWrapper(Class<E> enumClass) { 045 this.enumClass = enumClass; 046 this.enums = enumClass.getEnumConstants(); 047 048 Field enumValueField = ClassUtil.getFirstField(enumClass, field -> field.getAnnotation(EnumValue.class) != null); 049 if (enumValueField != null) { 050 hasEnumValueAnnotation = true; 051 } 052 053 if (hasEnumValueAnnotation) { 054 String getterMethodName = "get" + StringUtil.firstCharToUpperCase(enumValueField.getName()); 055 056 Method getter = ClassUtil.getFirstMethod(enumClass, method -> { 057 String methodName = method.getName(); 058 return methodName.equals(getterMethodName) && Modifier.isPublic(method.getModifiers()); 059 }); 060 061 propertyType = ClassUtil.getWrapType(enumValueField.getType()); 062 063 if (getter == null) { 064 if (Modifier.isPublic(enumValueField.getModifiers())) { 065 property = enumValueField; 066 } else { 067 throw new IllegalStateException("Can not find method \"" + getterMethodName + "()\" in enum: " + enumClass.getName()); 068 } 069 } else { 070 this.getterMethod = getter; 071 } 072 } 073 074 if (!hasEnumValueAnnotation) { 075 Method enumValueMethod = ClassUtil.getFirstMethod(enumClass, method -> method.getAnnotation(EnumValue.class) != null); 076 if (enumValueMethod != null) { 077 String methodName = enumValueMethod.getName(); 078 if (!(methodName.startsWith("get") && methodName.length() > 3)) { 079 throw new IllegalStateException("Can not find get method \"" + methodName + "()\" in enum: " + enumClass.getName()); 080 } 081 this.getterMethod = enumValueMethod; 082 this.hasEnumValueAnnotation = true; 083 Class<?> returnType = enumValueMethod.getReturnType(); 084 if (returnType.isPrimitive()) { 085 returnType = ConvertUtil.primitiveToBoxed(returnType); 086 } 087 this.propertyType = returnType; 088 } 089 } 090 } 091 092 093 public Object getEnumValue(E object) { 094 try { 095 return getterMethod != null ? getterMethod.invoke(object) : property.get(object); 096 } catch (Exception e) { 097 throw FlexExceptions.wrap(e); 098 } 099 } 100 101 102 public E getEnum(Object value) { 103 if (value != null) { 104 for (E e : enums) { 105 if (value.equals(getEnumValue(e))) { 106 return e; 107 } 108 } 109 } 110 return null; 111 } 112 113 114 public boolean hasEnumValueAnnotation() { 115 return hasEnumValueAnnotation; 116 } 117 118 public Class<?> getEnumClass() { 119 return enumClass; 120 } 121 122 public E[] getEnums() { 123 return enums; 124 } 125 126 public Field getProperty() { 127 return property; 128 } 129 130 public Class<?> getPropertyType() { 131 return propertyType; 132 } 133 134 public Method getGetterMethod() { 135 return getterMethod; 136 } 137 138}