content: package com.gm.wj.util;
  
  import com.gm.wj.exception.BeanUtilsException;
  import org.springframework.beans.BeanWrapperImpl;
  import org.springframework.beans.BeansException;
  import org.springframework.lang.NonNull;
  import org.springframework.lang.Nullable;
  import org.springframework.util.Assert;
  import org.springframework.util.CollectionUtils;
  
  import java.beans.PropertyDescriptor;
  import java.util.*;
  import java.util.stream.Collectors;
  
  /**
   * Bean utilities (copied from halo-master).
   *
   * @author johnniang
   * @date 2020/4/2 19:52
   */
  public class BeanUtils {
  
      private BeanUtils() {
      }
  
      /**
       * Transforms from the source object. (copy same properties only)
       *
       * @param source      source data
       * @param targetClass target class must not be null
       * @param <T>         target class type
       * @return instance with specified type copying from source data; or null if source data is null
       * @throws BeanUtilsException if newing target instance failed or copying failed
       */
      @Nullable
      public static <T> T transformFrom(@Nullable Object source, @NonNull Class<T> targetClass) {
          Assert.notNull(targetClass, "Target class must not be null");
  
          if (source == null) {
              return null;
          }
  
          // Init the instance
          try {
              // New instance for the target class
              T targetInstance = targetClass.newInstance();
              // Copy properties
              org.springframework.beans.BeanUtils.copyProperties(source, targetInstance, getNullPropertyNames(source));
              // Return the target instance
              return targetInstance;
          } catch (Exception e) {
              throw new BeanUtilsException("Failed to new " + targetClass.getName() + " instance or copy properties", e);
          }
      }
  
      /**
       * Transforms from source data collection in batch.
       *
       * @param sources     source data collection
       * @param targetClass target class must not be null
       * @param <T>         target class type
       * @return target collection transforming from source data collection.
       * @throws BeanUtilsException if newing target instance failed or copying failed
       */
      @NonNull
      public static <T> List<T> transformFromInBatch(Collection<?> sources, @NonNull Class<T> targetClass) {
          if (CollectionUtils.isEmpty(sources)) {
              return Collections.emptyList();
          }
  
          // Transform in batch
          return sources.stream()
                  .map(source -> transformFrom(source, targetClass))
                  .collect(Collectors.toList());
      }
  
      /**
       * Update properties (non null).
       *
       * @param source source data must not be null
       * @param target target data must not be null
       * @throws BeanUtilsException if copying failed
       */
      public static void updateProperties(@NonNull Object source, @NonNull Object target) {
          Assert.notNull(source, "source object must not be null");
          Assert.notNull(target, "target object must not be null");
  
          // Set non null properties from source properties to target properties
          try {
              org.springframework.beans.BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
          } catch (BeansException e) {
              throw new BeanUtilsException("Failed to copy properties", e);
          }
      }
  
      /**
       * Gets null names array of property.
       *
       * @param source object data must not be null
       * @return null name array of property
       */
      @NonNull
      private static String[] getNullPropertyNames(@NonNull Object source) {
          return getNullPropertyNameSet(source).toArray(new String[0]);
      }
  
      /**
       * Gets null names set of property.
       *
       * @param source object data must not be null
       * @return null name set of property
       */
      @NonNull
      private static Set<String> getNullPropertyNameSet(@NonNull Object source) {
  
          Assert.notNull(source, "source object must not be null");
          BeanWrapperImpl beanWrapper = new BeanWrapperImpl(source);
          PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
  
          Set<String> emptyNames = new HashSet<>();
  
          for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
              String propertyName = propertyDescriptor.getName();
              Object propertyValue = beanWrapper.getPropertyValue(propertyName);
  
              // if property value is equal to null, add it to empty name set
              if (propertyValue == null) {
                  emptyNames.add(propertyName);
              }
          }
  
          return emptyNames;
      }
  }
  