mirror of https://github.com/halo-dev/halo
Add BeanUtils, abstract input converter and output converter
parent
1c17fe5a33
commit
7a1c3234d9
|
@ -0,0 +1,24 @@
|
|||
package cc.ryanc.halo.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* BeanUtils exception.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
public class BeanUtilsException extends HaloException {
|
||||
|
||||
public BeanUtilsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BeanUtilsException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getStatus() {
|
||||
return HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package cc.ryanc.halo.model.dto.base;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import static cc.ryanc.halo.utils.BeanUtils.transformFrom;
|
||||
import static cc.ryanc.halo.utils.BeanUtils.updateProperties;
|
||||
|
||||
/**
|
||||
* Convenience for input dto.Abstract input dto converter.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
public abstract class AbstractInputConverter<DOMAIN> implements InputConverter<DOMAIN> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final Class<DOMAIN> domainType = (Class<DOMAIN>) fetchType(0);
|
||||
|
||||
@Override
|
||||
public DOMAIN convertTo() {
|
||||
return transformFrom(this, domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DOMAIN domain) {
|
||||
updateProperties(this, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get actual generic type.
|
||||
*
|
||||
* @param index generic type index
|
||||
* @return real type will be returned
|
||||
*/
|
||||
private Type fetchType(int index) {
|
||||
return ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[index];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package cc.ryanc.halo.model.dto.base;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import static cc.ryanc.halo.utils.BeanUtils.updateProperties;
|
||||
|
||||
/**
|
||||
* Abstract output dto converter. (it must be extended by DTO)
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
public abstract class AbstractOutputConverter<DTO, DOMAIN> implements OutputConverter<DTO, DOMAIN> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final Class<DTO> dtoType = (Class<DTO>) fetchType(0);
|
||||
|
||||
public AbstractOutputConverter() {
|
||||
Assert.isTrue(dtoType.equals(getClass()), "this converter must be extended by DTO type");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public DTO convertFrom(DOMAIN domain) {
|
||||
updateProperties(domain, this);
|
||||
return (DTO) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get actual generic type.
|
||||
*
|
||||
* @param index generic type index
|
||||
* @return real type will be returned
|
||||
*/
|
||||
private Type fetchType(int index) {
|
||||
return ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[index];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package cc.ryanc.halo.model.dto.base;
|
||||
|
||||
/**
|
||||
* Converter interface for input DTO.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
public interface InputConverter<DOMAIN> {
|
||||
|
||||
/**
|
||||
* Convert to domain.(shallow)
|
||||
*
|
||||
* @return new domain with same value(not null)
|
||||
*/
|
||||
DOMAIN convertTo();
|
||||
|
||||
/**
|
||||
* Update a domain by dto.(shallow)
|
||||
*
|
||||
* @param domain updated domain
|
||||
*/
|
||||
void update(DOMAIN domain);
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package cc.ryanc.halo.model.dto.base;
|
||||
|
||||
/**
|
||||
* Converter interface for output DTO.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
public interface OutputConverter<DTO, DOMAIN> {
|
||||
|
||||
/**
|
||||
* Convert from domain.(shallow)
|
||||
*
|
||||
* @param domain domain data
|
||||
* @return converted dto data
|
||||
*/
|
||||
DTO convertFrom(DOMAIN domain);
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package cc.ryanc.halo.utils;
|
||||
|
||||
import cc.ryanc.halo.exception.BeanUtilsException;
|
||||
import cc.ryanc.halo.logging.Logger;
|
||||
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.
|
||||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
public class BeanUtils {
|
||||
|
||||
private final static Logger LOG = Logger.getLogger(BeanUtils.class);
|
||||
|
||||
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.getDeclaredConstructor().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 propertye value is equal to null, add it to empty name set
|
||||
if (propertyValue == null) {
|
||||
emptyNames.add(propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
return emptyNames;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue