All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wl4g.infra.common.bean.BeanUtils2 Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 ~ 2025 the original author or authors. James Wong 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.wl4g.infra.common.bean;

import static com.wl4g.infra.common.lang.ClassUtils2.anyTypeOf;
import static com.wl4g.infra.common.reflect.ReflectionUtils2.findField;
import static com.wl4g.infra.common.reflect.ReflectionUtils2.isCompatibleType;
import static com.wl4g.infra.common.reflect.ReflectionUtils2.isGenericModifier;
import static com.wl4g.infra.common.reflect.ReflectionUtils2.makeAccessible;
import static com.wl4g.infra.common.reflect.TypeUtils2.isSimpleCollectionType;
import static com.wl4g.infra.common.reflect.TypeUtils2.isSimpleType;
import static java.lang.String.format;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static org.apache.commons.lang3.StringUtils.startsWithAny;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import javax.annotation.Nullable;
import javax.validation.constraints.NotNull;

import com.wl4g.infra.common.reflect.ReflectionUtils2.FieldFilter;

/**
 * Enhanced static convenience methods for JavaBeans: for instantiating beans,
 * checking bean property types, copying bean properties, etc. 
* Enhanced for: {@link org.springframework.beans.BeanUtils} * * @author James Wong James Wong * @version v1.0 2019年5月10日 * @since */ public abstract class BeanUtils2 { /** * Calls the given callback on all fields of the target class, recursively * running the class hierarchy up to copy all declared fields.
* It will contain all the fields defined by all parent or super classes. At * the same time, the target and the source object must be compatible. * * @param target * The target object to copy to * @param src * Source object * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void deepCopyFieldState(@NotNull Object target, @NotNull Object src) throws IllegalArgumentException, IllegalAccessException { deepCopyFieldState(target, src, DEFAULT_FIELD_FILTER, DEFAULT_FIELD_COPYER); } /** * Calls the given callback on all fields of the target class, recursively * running the class hierarchy up to copy all declared fields.
* It will contain all the fields defined by all parent or super classes. At * the same time, the target and the source object must be compatible. * * @param target * The target object to copy to * @param src * Source object * @param ff * Field filter * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void deepCopyFieldState(@NotNull Object target, @NotNull Object src, @NotNull FieldFilter ff) throws IllegalArgumentException, IllegalAccessException { deepCopyFieldState(target, src, ff, DEFAULT_FIELD_COPYER); } /** * Calls the given callback on all fields of the target class, recursively * running the class hierarchy up to copy all declared fields.
* It will contain all the fields defined by all parent or super classes. At * the same time, the target and the source object must be compatible. * * @param target * The target object to copy to * @param src * Source object * @param fc * Field copyer * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void deepCopyFieldState(@NotNull Object target, @NotNull Object src, @NotNull FieldProcessor fc) throws IllegalArgumentException, IllegalAccessException { deepCopyFieldState(target, src, DEFAULT_FIELD_FILTER, fc); } /** * Calls the given callback on all fields of the target class, recursively * running the class hierarchy up to copy all declared fields.
* It will contain all the fields defined by all parent or super classes. At * the same time, the target and the source object must be compatible. * * @param target * The target object to copy to * @param src * Source object * @param ff * Field filter * @param fp * Customizable copyer * @throws IllegalArgumentException * @throws IllegalAccessException */ public static void deepCopyFieldState( @NotNull Object target, @NotNull Object src, @NotNull FieldFilter ff, @NotNull FieldProcessor fp) throws IllegalArgumentException, IllegalAccessException { if (!(target != null && src != null && ff != null && fp != null)) { throw new IllegalArgumentException("Target and source FieldFilter and FieldProcessor must not be null"); } // Check if the target is compatible with the source object Class targetClass = target.getClass(), sourceClass = src.getClass(); if (!isCompatibleType(target.getClass(), src.getClass())) { throw new IllegalArgumentException( format("Incompatible the objects, target class: %s, source class: %s", targetClass, sourceClass)); } Class targetCls = target.getClass(); // [MARK0] do { doDeepCopyFields(targetCls, target, src, ff, fp); } while ((targetCls = targetCls.getSuperclass()) != Object.class); } /** * Calls the given callback on all fields of the target class, recursively * running the class hierarchy up to copy all declared fields.
* Note: that it does not contain fields defined by the parent or super * class. At the same time, the target and the source object must be * compatible.
* Note: Attribute fields of parent and superclass are not included * * @param currentTargetClass * The level of the class currently copied to (upward recursion) * @param target * The target object to copy to * @param src * Source object * @param ff * Field filter * @param fp * Customizable copyer * @throws IllegalArgumentException * @throws IllegalAccessException */ private static void doDeepCopyFields( Class currentTargetClass, @NotNull Object target, @NotNull Object src, @NotNull FieldFilter ff, @NotNull FieldProcessor fp) throws IllegalArgumentException, IllegalAccessException { if (isNull(currentTargetClass) || isNull(ff) || isNull(fp)) { throw new IllegalArgumentException( "Hierarchy current target class or source FieldFilter and FieldProcessor can't null"); } // Skip the current level copy. if (isNull(src) || isNull(target)) { return; } // Recursive traversal matching and processing Class sourceClass = src.getClass(); for (Field tf : currentTargetClass.getDeclaredFields()) { // Must be filtered over. // [BUGFIX]: for example when recursively getting // java.nio.charset.Charset, there will be an infinite loop stack // overflow (jvm8 defaults to 1024) if (Modifier.isFinal(tf.getModifiers()) || startsWithAny(tf.getDeclaringClass().getName(), "java.nio", "java.util", "org.apache.commons.lang", "org.springframework.util", "org.springframework.web.util", "org.springframework.boot.util")) { continue; } makeAccessible(tf); Object targetPropertyValue = tf.get(target); // See:[MARK0] Object sourcePropertyValue = null; Field sf = findField(sourceClass, tf.getName()); if (nonNull(sf)) { makeAccessible(sf); sourcePropertyValue = sf.get(src); } // Base type or collection type or enum? if (isSimpleType(tf.getType()) || isSimpleCollectionType(tf.getType()) || tf.getType().isEnum()) { // [MARK2] Filter matching property if (nonNull(fp) && ff.matches(tf)) { fp.doProcess(target, tf, sf, sourcePropertyValue); } } else { doDeepCopyFields(tf.getType(), targetPropertyValue, sourcePropertyValue, ff, fp); } } } /** * Enhanced callback interface invoked on each field in the hierarchy. */ public static interface FieldProcessor { /** * Use the given field processing(for example: copying). * * @param target * @param tf * @param sf * @param sourcePropertyValue * The value of the attributes of the source bean * @throws IllegalAccessException * @throws IllegalArgumentException */ void doProcess(@NotNull Object target, @NotNull Field tf, @NotNull Field sf, @Nullable Object sourcePropertyValue) throws IllegalArgumentException, IllegalAccessException; } /** * Default field filter of {@link FieldFilter}. * * @see:{@link com.wl4g.infra.common.reflect.ReflectionUtils2#isGenericAccessibleModifier(int)} */ public static final FieldFilter DEFAULT_FIELD_FILTER = targetField -> isGenericModifier(targetField.getModifiers()); /** * Default COPYER of {@link FieldProcessor}. */ public static final FieldProcessor DEFAULT_FIELD_COPYER = (target, tf, sf, sourcePropertyValue) -> { if (nonNull(sourcePropertyValue)) { tf.setAccessible(true); tf.set(target, sourcePropertyValue); } }; /** * Simple merging COPYER of {@link FieldProcessor}. */ public static final FieldProcessor SIMPLE_MERGE_FIELD_COPYER = (target, tf, sf, sourcePropertyValue) -> { if (nonNull(sourcePropertyValue)) { tf.setAccessible(true); Object targetPropertyValue = tf.get(target); // Check if the conditions for replication are met. if (isNull(targetPropertyValue)) { tf.set(target, sourcePropertyValue); } else if (anyTypeOf(targetPropertyValue.getClass(), int.class, Integer.class) && ((Integer) targetPropertyValue) <= 0) { tf.set(target, sourcePropertyValue); } else if (anyTypeOf(targetPropertyValue.getClass(), float.class, Float.class) && ((Float) targetPropertyValue) <= 0) { tf.set(target, sourcePropertyValue); } else if (anyTypeOf(targetPropertyValue.getClass(), double.class, Double.class) && ((Double) targetPropertyValue) <= 0) { tf.set(target, sourcePropertyValue); } else if (anyTypeOf(targetPropertyValue.getClass(), short.class, Short.class) && ((Short) targetPropertyValue) <= 0) { tf.set(target, sourcePropertyValue); } else { // String/custom Type/... // Ignore } } }; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy