
infra.beans.support.BeanProperties Maven / Gradle / Ivy
/*
* Copyright 2017 - 2024 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see [https://www.gnu.org/licenses/]
*/
package infra.beans.support;
import java.util.Map;
import java.util.Set;
import infra.beans.BeanMetadata;
import infra.beans.BeanProperty;
import infra.beans.BeanWrapperImpl;
import infra.beans.InvalidPropertyException;
import infra.beans.NoSuchPropertyException;
import infra.beans.SimpleTypeConverter;
import infra.beans.TypeConverter;
import infra.lang.Assert;
import infra.lang.Nullable;
import infra.util.ObjectUtils;
/**
* Bean properties utils
*
* @author Harry Yang
* @since 3.0.2 2021/5/2 22:14
*/
public class BeanProperties {
/**
* Copy the property values of the given source bean into the given target bean.
* Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
*
* @param source source object
* @param destination destination object
*/
public static void copy(Object source, Object destination) {
copy(source, destination, (TypeConverter) null);
}
/**
* Copy the property values of the given source bean into the given target bean.
*
Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
*
* @param source source object
* @param destination destination object
* @param converter type-converter to convert bean-properties
*/
public static void copy(Object source, Object destination, @Nullable TypeConverter converter) {
Assert.notNull(source, "source object is required");
Assert.notNull(destination, "destination object is required");
BeanMetadata destinationMetadata = BeanMetadata.forInstance(destination);
copy(source, destinationMetadata, destination, converter, null);
}
/**
* Copy the property values of the given source bean into the given target bean.
*
Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
*
* @param source the source bean
* @param ignoreProperties array of property names to ignore
*/
public static void copy(Object source, Object destination, @Nullable String... ignoreProperties) {
copy(source, destination, null, ignoreProperties);
}
/**
* Copy the property values of the given source bean into the given target bean.
*
Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
*
* @param source the source bean
* @param converter type-converter to convert bean-properties
* @param ignoreProperties array of property names to ignore
*/
public static void copy(Object source, Object destination,
@Nullable TypeConverter converter, @Nullable String... ignoreProperties) {
Assert.notNull(source, "source object is required");
Assert.notNull(destination, "destination object is required");
BeanMetadata destinationMetadata = BeanMetadata.forInstance(destination);
copy(source, destinationMetadata, destination, converter, ignoreProperties);
}
/**
* Copy the property values of the given source bean into the given target bean.
*
Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
*
* @param source source object
* @param destination destination class
* @return returns a destination type object
*/
public static T copy(Object source, Class destination) {
return copy(source, destination, (TypeConverter) null);
}
/**
* Copy the property values of the given source bean into the given target bean.
* Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
*
* @param source source object
* @param destination destination class
* @param converter type-converter to convert bean-properties
* @return returns a destination type object
*/
@SuppressWarnings("unchecked")
public static T copy(Object source, Class destination, @Nullable TypeConverter converter) {
Assert.notNull(source, "source object is required");
Assert.notNull(destination, "destination class is required");
BeanMetadata destinationMetadata = BeanMetadata.forClass(destination);
Object destinationInstance = destinationMetadata.newInstance(); // destination
copy(source, destinationMetadata, destinationInstance, converter, null);
return (T) destinationInstance;
}
/**
* Copy the property values of the given source bean into the given target bean.
* Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
*/
public static T copy(Object source, Class destination, @Nullable String... ignoreProperties) {
return copy(source, destination, null, ignoreProperties);
}
/**
* Copy the property values of the given source bean into the given target bean.
* Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
*
* @param converter type-converter to convert bean-properties
*/
@SuppressWarnings("unchecked")
public static T copy(Object source, Class destination,
@Nullable TypeConverter converter, @Nullable String... ignoreProperties) {
Assert.notNull(source, "source object is required");
Assert.notNull(destination, "destination class is required");
BeanMetadata destinationMetadata = BeanMetadata.forClass(destination);
Object destinationInstance = destinationMetadata.newInstance(); // destination
copy(source, destinationMetadata, destinationInstance, converter, ignoreProperties);
return (T) destinationInstance;
}
/**
* Ignore read-only properties
*/
@SuppressWarnings("unchecked")
private static void copy(Object source, BeanMetadata destination,
Object destinationInstance, @Nullable TypeConverter converter, @Nullable String[] ignoreProperties) {
if (converter == null) {
converter = new SimpleTypeConverter();
}
if (ObjectUtils.isNotEmpty(ignoreProperties)) {
Set ignorePropertiesSet = Set.of(ignoreProperties);
if (source instanceof Map) {
for (Map.Entry entry : ((Map) source).entrySet()) {
String propertyName = entry.getKey();
if (!ignorePropertiesSet.contains(propertyName)) {
BeanProperty beanProperty = destination.getBeanProperty(propertyName);
if (beanProperty != null && beanProperty.isWriteable()) {
beanProperty.setValue(destinationInstance, entry.getValue(), converter);
}
}
}
}
else {
BeanMetadata sourceMetadata = BeanMetadata.forInstance(source);
for (BeanProperty property : sourceMetadata) {
if (property.isReadable()) {
String propertyName = property.getName();
if (!ignorePropertiesSet.contains(propertyName)) {
BeanProperty beanProperty = destination.getBeanProperty(propertyName);
if (beanProperty != null && beanProperty.isWriteable()) {
beanProperty.setValue(destinationInstance, property.getValue(source), converter);
}
}
}
}
}
}
else {
if (source instanceof Map) {
for (Map.Entry entry : ((Map) source).entrySet()) {
String propertyName = entry.getKey();
BeanProperty beanProperty = destination.getBeanProperty(propertyName);
if (beanProperty != null && beanProperty.isWriteable()) {
beanProperty.setValue(destinationInstance, entry.getValue(), converter);
}
}
}
else {
BeanMetadata sourceMetadata = BeanMetadata.forInstance(source);
for (BeanProperty property : sourceMetadata) {
if (property.isReadable()) {
String propertyName = property.getName();
BeanProperty beanProperty = destination.getBeanProperty(propertyName);
if (beanProperty != null && beanProperty.isWriteable()) {
beanProperty.setValue(destinationInstance, property.getValue(source), converter);
}
}
}
}
}
}
//
/**
* Populate the JavaBeans properties of the specified bean, based on
* the specified name/value pairs. This method uses Java reflection APIs
* to identify corresponding "property setter" method names, and deals
* with setter arguments of type String
, boolean
,
* int
, long
, float
, and
* double
. In addition, array setters for these types (or the
* corresponding primitive types) can also be identified.
*
* The particular setter method to be called for each property is
* determined using the usual JavaBeans introspection mechanisms. Thus,
* you may identify custom setter methods using a BeanInfo class that is
* associated with the class of the bean itself. If no such BeanInfo
* class is available, the standard method name conversion ("set" plus
* the capitalized name of the property in question) is used.
*
*
* default is ignoreUnknownProperty
*
*
* @param bean JavaBean whose properties are being populated
* @param properties Map keyed by property name, with the
* corresponding (String or String[]) value(s) to be set
* @throws NoSuchPropertyException If no such property
* @throws InvalidPropertyException Invalid property value
*/
public static void populate(Object bean, Map properties) {
populate(bean, properties, true);
}
/**
* Populate the JavaBeans properties of the specified bean, based on
* the specified name/value pairs. This method uses Java reflection APIs
* to identify corresponding "property setter" method names, and deals
* with setter arguments of type String
, boolean
,
* int
, long
, float
, and
* double
. In addition, array setters for these types (or the
* corresponding primitive types) can also be identified.
*
* The particular setter method to be called for each property is
* determined using the usual JavaBeans introspection mechanisms. Thus,
* you may identify custom setter methods using a BeanInfo class that is
* associated with the class of the bean itself. If no such BeanInfo
* class is available, the standard method name conversion ("set" plus
* the capitalized name of the property in question) is used.
*
* @param bean JavaBean whose properties are being populated
* @param properties Map keyed by property name, with the
* corresponding (String or String[]) value(s) to be set
* @throws NoSuchPropertyException If no such property
* @throws InvalidPropertyException Invalid property value
* @see BeanWrapperImpl
*/
public static void populate(Object bean, Map properties, boolean ignoreUnknown) {
Assert.notNull(bean, "target bean is required");
Assert.notNull(properties, "properties is required");
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(bean);
beanWrapper.setAutoGrowNestedPaths(true);
beanWrapper.setPropertyValues(properties, ignoreUnknown, true);
}
}