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