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.handler;
017
018import com.mybatisflex.annotation.EnumValue;
019import com.mybatisflex.core.exception.FlexExceptions;
020import com.mybatisflex.core.util.ClassUtil;
021import com.mybatisflex.core.util.StringUtil;
022import org.apache.ibatis.type.BaseTypeHandler;
023import org.apache.ibatis.type.JdbcType;
024
025import java.lang.reflect.Field;
026import java.lang.reflect.Method;
027import java.lang.reflect.Modifier;
028import java.sql.CallableStatement;
029import java.sql.PreparedStatement;
030import java.sql.ResultSet;
031import java.sql.SQLException;
032import java.util.List;
033
034public class FlexEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
035
036    private Class<?> enumPropertyType;
037    private E[] enums;
038    private Field property;
039    private Method getter;
040
041    public FlexEnumTypeHandler(Class<E> enumClass) {
042        List<Field> allFields = ClassUtil.getAllFields(enumClass, field -> field.getAnnotation(EnumValue.class) != null);
043        Field field = allFields.get(0);
044
045        String fieldGetterName = "get" + StringUtil.firstCharToUpperCase(field.getName());
046        List<Method> allMethods = ClassUtil.getAllMethods(enumClass, method -> {
047            String methodName = method.getName();
048            return methodName.equals(fieldGetterName);
049        });
050
051        enumPropertyType = ClassUtil.wrap(field.getType());
052        enums = enumClass.getEnumConstants();
053
054        if (allMethods.isEmpty()) {
055            if (Modifier.isPublic(field.getModifiers())) {
056                property = field;
057            } else {
058                throw new IllegalStateException("Can not find \"" + fieldGetterName + "()\" method in enum: " + enumClass.getName());
059            }
060        } else {
061            getter = allMethods.get(0);
062        }
063
064    }
065
066
067    @Override
068    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
069        Object value = getValue(parameter);
070        if (jdbcType == null) {
071            ps.setObject(i, value);
072        } else {
073            ps.setObject(i, value, jdbcType.TYPE_CODE);
074        }
075    }
076
077
078    private Object getValue(E object) {
079        try {
080            return getter != null
081                    ? getter.invoke(object)
082                    : property.get(object);
083        } catch (Exception e) {
084            throw FlexExceptions.wrap(e);
085        }
086    }
087
088
089    @Override
090    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
091        Object value = rs.getObject(columnName, this.enumPropertyType);
092        if (null == value && rs.wasNull()) {
093            return null;
094        }
095        return convertToEnum(value);
096    }
097
098
099    @Override
100    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
101        Object value = rs.getObject(columnIndex, this.enumPropertyType);
102        if (null == value && rs.wasNull()) {
103            return null;
104        }
105        return convertToEnum(value);
106    }
107
108
109    @Override
110    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
111        Object value = cs.getObject(columnIndex, this.enumPropertyType);
112        if (null == value && cs.wasNull()) {
113            return null;
114        }
115        return convertToEnum(value);
116    }
117
118
119    private E convertToEnum(Object value) {
120        for (E e : enums) {
121            if (value.equals(getValue(e))) {
122                return e;
123            }
124        }
125        return null;
126    }
127
128}