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