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