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.Column; 019import org.apache.ibatis.reflection.Reflector; 020 021import java.lang.reflect.*; 022import java.util.Collection; 023import java.util.Map; 024import java.util.concurrent.ConcurrentHashMap; 025 026public class FieldWrapper { 027 028 public static Map<Class<?>, Map<String, FieldWrapper>> cache = new ConcurrentHashMap<>(); 029 030 private Field field; 031 private boolean isIgnore = false; 032 private Class<?> fieldType; 033 private Class<?> mappingType; 034 private Class<?> keyType; 035 private Method getterMethod; 036 private Method setterMethod; 037 038 public static FieldWrapper of(Class<?> clazz, String fieldName) { 039 Map<String, FieldWrapper> wrapperMap = cache.get(clazz); 040 if (wrapperMap == null) { 041 synchronized (clazz) { 042 wrapperMap = cache.get(clazz); 043 if (wrapperMap == null) { 044 wrapperMap = new ConcurrentHashMap<>(); 045 cache.put(clazz, wrapperMap); 046 } 047 } 048 } 049 050 FieldWrapper fieldWrapper = wrapperMap.get(fieldName); 051 if (fieldWrapper == null) { 052 synchronized (clazz) { 053 fieldWrapper = wrapperMap.get(fieldName); 054 if (fieldWrapper == null) { 055 Field findField = ClassUtil.getFirstField(clazz, field -> field.getName().equalsIgnoreCase(fieldName)); 056 if (findField == null) { 057 throw new IllegalStateException("Can not find field \"" + fieldName + "\" in class: " + clazz.getName()); 058 } 059 060 String setterName = "set" + StringUtil.firstCharToUpperCase(findField.getName()); 061 Method setter = ClassUtil.getFirstMethod(clazz, method -> 062 method.getParameterCount() == 1 063 && Modifier.isPublic(method.getModifiers()) 064 && method.getName().equals(setterName)); 065 066 fieldWrapper = new FieldWrapper(); 067 fieldWrapper.field = findField; 068 fieldWrapper.fieldType = findField.getType(); 069 initMappingTypeAndKeyType(clazz, findField, fieldWrapper); 070 071 Column column = findField.getAnnotation(Column.class); 072 if (column != null && column.ignore()) { 073 fieldWrapper.isIgnore = true; 074 } 075 076 fieldWrapper.setterMethod = setter; 077 078 String[] getterNames = new String[]{"get" + StringUtil.firstCharToUpperCase(findField.getName()), "is" + StringUtil.firstCharToUpperCase(findField.getName())}; 079 fieldWrapper.getterMethod = ClassUtil.getFirstMethod(clazz, method -> method.getParameterCount() == 0 080 && Modifier.isPublic(method.getModifiers()) 081 && ArrayUtil.contains(getterNames, method.getName())); 082 083 wrapperMap.put(fieldName, fieldWrapper); 084 } 085 } 086 } 087 088 return fieldWrapper; 089 } 090 091 private static void initMappingTypeAndKeyType(Class<?> clazz, Field field, FieldWrapper fieldWrapper) { 092 Reflector reflector = Reflectors.of(clazz); 093 Class<?> fieldType = reflector.getGetterType(field.getName()); 094 095 if (Collection.class.isAssignableFrom(fieldType)) { 096 Type genericType = field.getGenericType(); 097 if (genericType instanceof ParameterizedType) { 098 Type actualTypeArgument = ((ParameterizedType) genericType).getActualTypeArguments()[0]; 099 fieldWrapper.mappingType = (Class<?>) actualTypeArgument; 100 } 101 } else if (Map.class.isAssignableFrom(fieldType)) { 102 Type genericType = field.getGenericType(); 103 if (genericType instanceof ParameterizedType) { 104 fieldWrapper.keyType = (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0]; 105 Type actualTypeArgument = ((ParameterizedType) genericType).getActualTypeArguments()[1]; 106 if (actualTypeArgument instanceof ParameterizedType) { 107 fieldWrapper.mappingType = (Class<?>) ((ParameterizedType) actualTypeArgument).getRawType(); 108 } else { 109 fieldWrapper.mappingType = (Class<?>) actualTypeArgument; 110 } 111 } 112 } else { 113 fieldWrapper.mappingType = fieldType; 114 } 115 } 116 117 118 public void set(Object value, Object to) { 119 try { 120 if (setterMethod == null) { 121 throw new IllegalStateException("Can not find method \"set" + StringUtil.firstCharToUpperCase(field.getName()) + "\" in class: " + to.getClass().getName()); 122 } 123 setterMethod.invoke(to, value); 124 } catch (Exception e) { 125 throw new RuntimeException(e); 126 } 127 } 128 129 public Object get(Object target) { 130 try { 131 if (getterMethod == null) { 132 throw new IllegalStateException("Can not find method \"get" + StringUtil.firstCharToUpperCase(field.getName()) + ", is" 133 + StringUtil.firstCharToUpperCase(field.getName()) + "\" in class: " + target.getClass().getName()); 134 } 135 return getterMethod.invoke(target); 136 } catch (Exception e) { 137 throw new RuntimeException(e); 138 } 139 } 140 141 public Class<?> getFieldType() { 142 return fieldType; 143 } 144 145 public Class<?> getMappingType() { 146 return mappingType; 147 } 148 149 public Class<?> getKeyType() { 150 return keyType; 151 } 152 153 public Field getField() { 154 return field; 155 } 156 157 public boolean isIgnore() { 158 return isIgnore; 159 } 160}