ObjectConverter.java
package space.sunqian.common.object.convert;
import space.sunqian.annotations.Nonnull;
import space.sunqian.annotations.Nullable;
import space.sunqian.annotations.RetainedParam;
import space.sunqian.common.Fs;
import space.sunqian.common.base.option.Option;
import space.sunqian.common.collect.ListKit;
import space.sunqian.common.object.convert.handlers.AssignableConvertHandler;
import space.sunqian.common.object.convert.handlers.CommonConvertHandler;
import space.sunqian.common.object.data.ObjectBuilderProvider;
import space.sunqian.common.reflect.TypeRef;
import java.lang.reflect.Type;
import java.util.List;
/**
* This interface is used to convert an object from the specified type to the target type.
* <p>
* It contains and uses a list of {@link Handler}s to sequentially attempt conversion. A handler can return
* {@link Status#HANDLER_CONTINUE}, {@link Status#HANDLER_BREAK} or a normal value as the final result. The conversion
* logic is as follows:
* <pre>{@code
* for (Handler handler : handlers()) {
* Object ret;
* try {
* ret = handler.convert(src, srcType, target, this, options);
* } catch (Exception e) {
* throw new ObjectConvertException(e);
* }
* if (ret == Status.HANDLER_CONTINUE) {
* continue;
* }
* if (ret == Status.HANDLER_BREAK) {
* throw new UnsupportedObjectConvertException(src, srcType, target, this, options);
* }
* return ret;
* }
* throw new UnsupportedObjectConvertException(src, srcType, target, this, options);
* }</pre>
* <p>
* The thread safety of the methods in this interface is determined by its dependent {@link DataMapper},
* {@link ObjectBuilderProvider}, and other objects. By default, they are all thread-safe.
*
* @author sunqian
*/
public interface ObjectConverter {
/**
* Returns the default {@link ObjectConverter}, of which handlers are:
* <ul>
* <li>{@link AssignableConvertHandler};</li>
* <li>{@link CommonConvertHandler};</li>
* </ul>
*
* @return the default converter
*/
static @Nonnull ObjectConverter defaultConverter() {
return ObjectConverterImpl.DEFAULT;
}
/**
* Creates and returns a new {@link ObjectConverter} with the given handlers.
*
* @param handlers the given handlers
* @return a new {@link ObjectConverter} with the given handlers
*/
static @Nonnull ObjectConverter newConverter(@Nonnull @RetainedParam Handler @Nonnull ... handlers) {
return newConverter(ListKit.list(handlers));
}
/**
* Creates and returns a new {@link ObjectConverter} with given handlers.
*
* @param handlers given handlers
* @return a new {@link ObjectConverter} with given handlers
*/
static @Nonnull ObjectConverter newConverter(@Nonnull @RetainedParam List<@Nonnull Handler> handlers) {
return new ObjectConverterImpl(handlers);
}
/**
* Converts the given source object from the specified type to the target type.
* <p>
* The options parameter can be empty, in which case the default behavior will be used, or built-in options in
* {@link ConvertOption} or other custom options for custom implementations.
*
* @param src the given source object
* @param target the specified type of the target object
* @param options the other conversion options
* @param <T> the target type
* @return the converted object, {@code null} is permitted
* @throws UnsupportedObjectConvertException if the conversion from the specified type to the target type is not
* supported
* @throws ObjectConvertException if the conversion failed
*/
default <T> T convert(
@Nullable Object src,
@Nonnull Class<? extends T> target,
@Nonnull Option<?, ?> @Nonnull ... options
) throws UnsupportedObjectConvertException, ObjectConvertException {
return Fs.as(convert(src, (Type) target, options));
}
/**
* Converts the given source object from the specified type to the target type.
* <p>
* The options parameter can be empty, in which case the default behavior will be used, or built-in options in
* {@link ConvertOption} or other custom options for custom implementations.
*
* @param src the given source object
* @param target the specified type ref of the target object
* @param options the other conversion options
* @param <T> the target type
* @return the converted object, {@code null} is permitted
* @throws UnsupportedObjectConvertException if the conversion from the specified type to the target type is not
* supported
* @throws ObjectConvertException if the conversion failed
*/
default <T> T convert(
@Nullable Object src,
@Nonnull TypeRef<? extends T> target,
@Nonnull Option<?, ?> @Nonnull ... options
) throws UnsupportedObjectConvertException, ObjectConvertException {
return Fs.as(convert(src, target.type(), options));
}
/**
* Converts the given source object from the specified type to the target type.
* <p>
* The options parameter can be empty, in which case the default behavior will be used, or built-in options in
* {@link ConvertOption} or other custom options for custom implementations.
*
* @param src the given source object
* @param target the specified type of the target object
* @param options the other conversion options
* @return the converted object, {@code null} is permitted
* @throws UnsupportedObjectConvertException if the conversion from the specified type to the target type is not
* supported
* @throws ObjectConvertException if the conversion failed
*/
default Object convert(
@Nullable Object src,
@Nonnull Type target,
@Nonnull Option<?, ?> @Nonnull ... options
) throws UnsupportedObjectConvertException, ObjectConvertException {
return convert(src, src == null ? Object.class : src.getClass(), target, options);
}
/**
* Converts the given source object from the specified type to the target type.
* <p>
* The options parameter can be empty, in which case the default behavior will be used, or built-in options in
* {@link ConvertOption} or other custom options for custom implementations.
*
* @param src the given source object
* @param srcType the specified type of the given source object
* @param target the specified type of the target object
* @param options the other conversion options
* @param <T> the target type
* @return the converted object, {@code null} is permitted
* @throws UnsupportedObjectConvertException if the conversion from the specified type to the target type is not
* supported
* @throws ObjectConvertException if the conversion failed
*/
default <T> T convert(
@Nullable Object src,
@Nonnull Type srcType,
@Nonnull Class<? extends T> target,
@Nonnull Option<?, ?> @Nonnull ... options
) throws UnsupportedObjectConvertException, ObjectConvertException {
return Fs.as(convert(src, srcType, (Type) target, options));
}
/**
* Converts the given source object from the specified type to the target type.
* <p>
* The options parameter can be empty, in which case the default behavior will be used, or built-in options in
* {@link ConvertOption} or other custom options for custom implementations.
*
* @param src the given source object
* @param srcType the specified type of the given source object
* @param target the specified type ref of the target object
* @param options the other conversion options
* @param <T> the target type
* @return the converted object, {@code null} is permitted
* @throws UnsupportedObjectConvertException if the conversion from the specified type to the target type is not
* supported
* @throws ObjectConvertException if the conversion failed
*/
default <T> T convert(
@Nullable Object src,
@Nonnull Type srcType,
@Nonnull TypeRef<? extends T> target,
@Nonnull Option<?, ?> @Nonnull ... options
) throws UnsupportedObjectConvertException, ObjectConvertException {
return Fs.as(convert(src, srcType, target.type(), options));
}
/**
* Converts the given source object from the specified type to the target type.
* <p>
* The options parameter can be empty, in which case the default behavior will be used, or built-in options in
* {@link ConvertOption} or other custom options for custom implementations.
*
* @param src the given source object
* @param srcType the specified type of the given source object
* @param target the specified type of the target object
* @param options the other conversion options
* @return the converted object, {@code null} is permitted
* @throws UnsupportedObjectConvertException if the conversion from the specified type to the target type is not
* supported
* @throws ObjectConvertException if the conversion failed
*/
default Object convert(
@Nullable Object src,
@Nonnull Type srcType,
@Nonnull Type target,
@Nonnull Option<?, ?> @Nonnull ... options
) throws UnsupportedObjectConvertException, ObjectConvertException {
for (Handler handler : handlers()) {
Object ret;
try {
ret = handler.convert(src, srcType, target, this, options);
} catch (Exception e) {
throw new ObjectConvertException(e);
}
if (ret == Status.HANDLER_CONTINUE) {
continue;
}
if (ret == Status.HANDLER_BREAK) {
throw new UnsupportedObjectConvertException(src, srcType, target, this, options);
}
return ret;
}
throw new UnsupportedObjectConvertException(src, srcType, target, this, options);
}
/**
* Returns all handlers of this converter.
*
* @return all handlers of this converter
*/
@Nonnull
List<@Nonnull Handler> handlers();
/**
* Returns a new {@link ObjectConverter} of which handler list consists of the given handler as the first element,
* followed by {@link #handlers()} of the current converter.
*
* @param handler the given handler
* @return a new {@link ObjectConverter} of which handler list consists of the given handler as the first element,
* followed by {@link #handlers()} of the current converter
*/
default @Nonnull ObjectConverter withFirstHandler(@Nonnull Handler handler) {
Handler[] newHandlers = new Handler[handlers().size() + 1];
int i = 0;
newHandlers[i++] = handler;
for (Handler h : handlers()) {
newHandlers[i++] = h;
}
return newConverter(newHandlers);
}
/**
* Returns a new {@link ObjectConverter} of which handler list consists of {@link #handlers()} of the current
* converter, followed by the given handler as the last element.
*
* @param handler the given handler
* @return a {@link ObjectConverter} of which handler list consists of {@link #handlers()} of the current converter,
* followed by the given handler as the last element
*/
default @Nonnull ObjectConverter withLastHandler(@Nonnull Handler handler) {
Handler[] newHandlers = new Handler[handlers().size() + 1];
int i = 0;
for (Handler h : handlers()) {
newHandlers[i++] = h;
}
newHandlers[i] = handler;
return newConverter(newHandlers);
}
/**
* Returns this converter as a {@link Handler}.
*
* @return this converter as a {@link Handler}
*/
@Nonnull
Handler asHandler();
/**
* Handler for {@link ObjectConverter}, provides the specific conversion logic.
* <p>
* The thread safety of the methods in this interface is determined by its dependent {@link DataMapper},
* {@link ObjectBuilderProvider}, and other objects. By default, they are all thread-safe.
*
* @author sunqian
*/
interface Handler {
/**
* Converts the given source object to the given target type.
*
* @param src the given source object
* @param srcType the specified type of the given source object
* @param target the specified type of the target object
* @param converter the converter where this handler in
* @param options the other conversion options
* @return the converted object, {@code null} is permitted, or {@link Status#HANDLER_CONTINUE} /
* {@link Status#HANDLER_BREAK} if conversion failed
* @throws Exception any exception can be thrown here
*/
Object convert(
@Nullable Object src,
@Nonnull Type srcType,
@Nonnull Type target,
@Nonnull ObjectConverter converter,
@Nonnull Option<?, ?> @Nonnull ... options
) throws Exception;
}
/**
* Represents the status of conversion.
*
* @author sunqian
*/
enum Status {
/**
* This status is returned by a {@link Handler} in a conversion process, to indicate that the current handler
* cannot convert but can continue to convert by the next handler.
*/
HANDLER_CONTINUE,
/**
* This status is returned by a {@link Handler} in a conversion process, to indicate that the current handler
* cannot convert and the conversion process needs to end in failure.
*/
HANDLER_BREAK
}
}