ObjectCopier.java
package space.sunqian.fs.object.convert;
import space.sunqian.annotation.Immutable;
import space.sunqian.annotation.Nonnull;
import space.sunqian.annotation.Nullable;
import space.sunqian.annotation.RetainedParam;
import space.sunqian.annotation.ThreadSafe;
import space.sunqian.fs.base.option.Option;
import space.sunqian.fs.collect.ListKit;
import space.sunqian.fs.object.builder.BuilderOperatorProvider;
import space.sunqian.fs.object.convert.handlers.CommonCopierHandler;
import space.sunqian.fs.object.schema.MapSchema;
import space.sunqian.fs.object.schema.ObjectProperty;
import space.sunqian.fs.object.schema.ObjectSchema;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* This interface is used to copy data properties from an object to another object. The object should be a {@link Map}
* or a non-map object which can be parsed to {@link MapSchema} and {@link ObjectSchema}.
* <p>
* A copier can have default options. The options parameter of a copy method (such as
* {@link #copyProperties(Object, Type, Object, Type, ObjectConverter, Option[])}) will be merged with the default
* options when the method is called.
*
* @author sunqian
* @implNote The default implementations of {@link ObjectCopier} support annotations defined in
* {@link space.sunqian.fs.object.annotation}.
*/
@ThreadSafe
public interface ObjectCopier {
/**
* Returns the default {@link ObjectCopier}. Here are handlers in the default converter:
* <ul>
* <li>{@link CommonCopierHandler#getInstance()};</li>
* </ul>
*
* @return the default {@link ObjectCopier}
* @see CommonCopierHandler
*/
static @Nonnull ObjectCopier defaultCopier() {
return ObjectCopierImpl.DEFAULT;
}
/**
* Creates and returns a default implementation of {@link ObjectCopier} with the given handlers, the default
* implementation and the default handler ({@link CommonCopierHandler}) follow the options from
* {@link ConvertOption}.
*
* @param handlers the given handlers
* @return a default implementation of {@link ObjectCopier} with the given handlers
*/
static @Nonnull ObjectCopier newCopier(@Nonnull @RetainedParam Handler @Nonnull ... handlers) {
return newCopier(ListKit.list(handlers));
}
/**
* Creates and returns a default implementation of {@link ObjectCopier} with the given handlers, the default
* implementation and the default handler ({@link CommonCopierHandler}) follow the options from
* {@link ConvertOption}.
*
* @param handlers given handlers
* @return a default implementation of {@link ObjectCopier} with the given handlers
*/
static @Nonnull ObjectCopier newCopier(@Nonnull @RetainedParam List<@Nonnull Handler> handlers) {
return newCopier(handlers, Collections.emptyList());
}
/**
* Creates and returns a default implementation of {@link ObjectCopier} with the given handlers and default options,
* the default implementation and the default handler ({@link CommonCopierHandler}) follow the options from
* {@link ConvertOption}.
*
* @param handlers given handlers
* @param defaultOptions given default options
* @return a default implementation of {@link ObjectCopier} with the given handlers and default options
*/
static @Nonnull ObjectCopier newCopier(
@Nonnull @RetainedParam List<@Nonnull Handler> handlers,
@Nonnull @RetainedParam List<@Nonnull Option<?, ?>> defaultOptions
) {
return new ObjectCopierImpl(handlers, defaultOptions);
}
/**
* Copy properties from the given source object to the given destination object. The object can be a {@link Map} or
* a non-map object which can be parsed to {@link ObjectSchema}.
* <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 dst the given destination object
* @param options the options for copying properties
* @throws ObjectCopyException if an error occurs during copying properties
*/
default void copyProperties(
@Nonnull Object src, @Nonnull Object dst, @Nonnull Option<?, ?> @Nonnull ... options
) throws ObjectCopyException {
copyProperties(src, src.getClass(), dst, dst.getClass(), options);
}
/**
* Copy properties from the given source object to the given destination object. The object can be a {@link Map} or
* a non-map object which can be parsed to {@link ObjectSchema}.
* <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 dst the given destination object
* @param converter the converter for converting values of the properties if needed
* @param options the options for copying properties
* @throws ObjectCopyException if an error occurs during copying properties
*/
default void copyProperties(
@Nonnull Object src,
@Nonnull Object dst,
@Nonnull ObjectConverter converter,
@Nonnull Option<?, ?> @Nonnull ... options
) throws ObjectCopyException {
copyProperties(src, src.getClass(), dst, dst.getClass(), converter, options);
}
/**
* Copy properties from the given source object to the given destination object. The object can be a {@link Map} or
* a non-map object which can be parsed to {@link ObjectSchema}.
* <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 specifies the type of the given source object
* @param dst the given destination object
* @param dstType specifies the type of the given destination object
* @param options the options for copying properties
* @throws ObjectCopyException if an error occurs during copying properties
*/
default void copyProperties(
@Nonnull Object src,
@Nonnull Type srcType,
@Nonnull Object dst,
@Nonnull Type dstType,
@Nonnull Option<?, ?> @Nonnull ... options
) throws ObjectCopyException {
copyProperties(
src,
srcType,
dst,
dstType,
ObjectConverter.defaultConverter(),
options
);
}
/**
* Copy properties from the given source object to the given destination object. The object can be a {@link Map} or
* a non-map object which can be parsed to {@link ObjectSchema}.
* <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 specifies the type of the given source object
* @param dst the given destination object
* @param dstType specifies the type of the given destination object
* @param converter the converter for converting values of the properties if needed
* @param options the options for copying properties
* @throws ObjectCopyException if an error occurs during copying properties
*/
void copyProperties(
@Nonnull Object src,
@Nonnull Type srcType,
@Nonnull Object dst,
@Nonnull Type dstType,
@Nonnull ObjectConverter converter,
@Nonnull Option<?, ?> @Nonnull ... options
) throws ObjectCopyException;
/**
* Returns all handlers of this {@link ObjectCopier}.
*
* @return all handlers of this {@link ObjectCopier}
*/
@Nonnull
@Immutable
List<@Nonnull Handler> handlers();
/**
* Returns the default options of this {@link ObjectCopier}.
*
* @return the default options of this {@link ObjectCopier}
*/
@Nonnull
@Immutable
List<@Nonnull Option<?, ?>> defaultOptions();
/**
* Returns a new {@link ObjectCopier} of which first handler is the given handler and the next handler is this
* {@link ObjectCopier} as a {@link Handler}. This method is equivalent:
* <pre>{@code
* newConverter(firstHandler, this.asHandler())
* }</pre>
*
* @param firstHandler the first handler
* @return a new {@link ObjectCopier} of which first handler is the given handler and the next handler is this
* {@link ObjectCopier} as a {@link Handler}
*/
default @Nonnull ObjectCopier withFirstHandler(@Nonnull Handler firstHandler) {
return newCopier(firstHandler, this.asHandler());
}
/**
* Returns a new {@link ObjectCopier} of which default options are the given options.
*
* @param defaultOptions the default options
* @return a new {@link ObjectCopier} of which default options are the given options
*/
default @Nonnull ObjectCopier withDefaultOptions(@Nonnull Option<?, ?> @Nonnull ... defaultOptions) {
return newCopier(handlers(), ListKit.list(defaultOptions));
}
/**
* Returns this {@link ObjectCopier} as a {@link Handler}.
*
* @return this {@link ObjectCopier} as a {@link Handler}
*/
@Nonnull
Handler asHandler();
/**
* Handler for {@link ObjectCopier}, provides the specific copy logic for copying each object property to the
* destination object. It has 4 methods:
* <ul>
* <li>
* {@link #copyProperty(Object, Object, Map, MapSchema, Map, MapSchema, ObjectConverter, Option[])}:
* from source map entry to destination map entry;
* </li>
* <li>
* {@link #copyProperty(Object, Object, Map, MapSchema, Object, ObjectSchema, ObjectConverter, Option[])}:
* from source map entry to destination object property;
* </li>
* <li>
* {@link #copyProperty(String, ObjectProperty, Object, ObjectSchema, Map, MapSchema, ObjectConverter, Option[])}:
* from source object property to destination map entry;
* </li>
* <li>
* {@link #copyProperty(String, ObjectProperty, Object, ObjectSchema, Map, MapSchema, ObjectConverter, Option[])}:
* from source object property to destination object property;
* </li>
* </ul>
* All those methods have default implementations, which directly copy properties from the source object to the
* target object, following the rules of the specified options defined in {@link ConvertOption}.
* <p>
* The thread safety of the methods in this interface is determined by its dependent {@link ObjectConverter},
* {@link BuilderOperatorProvider}, and other objects. By default, they are all thread-safe.
*
* @author sunqian
* @implNote The default implementations of {@link Handler} support annotations defined in
* {@link space.sunqian.fs.object.annotation}.
*/
interface Handler {
/**
* This method will be invoked when copy an entry from the source map to a destination map. Returns
* {@code false} to prevent subsequent handlers to continue to copy, otherwise returns {@code true} to continue
* to copy.
*
* @param srcKey the key of the entry to be copied
* @param srcValue the value of the entry to be copied
* @param srcSchema the schema of the source map
* @param dst the destination map
* @param dstSchema the schema of the destination map
* @param converter the converter used in the mapping process
* @param options the options used in the mapping process
* @return whether to continue to copy
* @throws Exception any exception can be thrown here
*/
boolean copyProperty(
@Nonnull Object srcKey,
@Nullable Object srcValue,
@Nonnull Map<Object, Object> src,
@Nonnull MapSchema srcSchema,
@Nonnull Map<Object, Object> dst,
@Nonnull MapSchema dstSchema,
@Nonnull ObjectConverter converter,
@Nonnull Option<?, ?> @Nonnull ... options
) throws Exception;
/**
* This method will be invoked when copy an entry from the source map to a destination object. Returns
* {@code false} to prevent subsequent handlers to continue to copy, otherwise returns {@code true} to continue
* to copy.
*
* @param srcKey the key of the entry to be copied
* @param srcValue the value of the entry to be copied
* @param srcSchema the schema of the source map
* @param dst the destination object
* @param dstSchema the schema of the destination object
* @param converter the converter used in the mapping process
* @param options the options used in the mapping process
* @return whether to continue to copy
* @throws Exception any exception can be thrown here
*/
boolean copyProperty(
@Nonnull Object srcKey,
@Nullable Object srcValue,
@Nonnull Map<Object, Object> src,
@Nonnull MapSchema srcSchema,
@Nonnull Object dst,
@Nonnull ObjectSchema dstSchema,
@Nonnull ObjectConverter converter,
@Nonnull Option<?, ?> @Nonnull ... options
) throws Exception;
/**
* This method will be invoked when copy a property from the source object to a destination map. Returns
* {@code false} to prevent subsequent handlers to continue to copy, otherwise returns {@code true} to continue
* to copy.
*
* @param srcPropertyName the name of the property to be copied
* @param srcProperty the property to be copied
* @param src the source object
* @param srcSchema the schema of the source object
* @param dst the destination map
* @param dstSchema the schema of the destination map
* @param converter the converter used in the mapping process
* @param options the options used in the mapping process
* @return whether to continue to copy
* @throws Exception any exception can be thrown here
*/
boolean copyProperty(
@Nonnull String srcPropertyName,
@Nonnull ObjectProperty srcProperty,
@Nonnull Object src,
@Nonnull ObjectSchema srcSchema,
@Nonnull Map<Object, Object> dst,
@Nonnull MapSchema dstSchema,
@Nonnull ObjectConverter converter,
@Nonnull Option<?, ?> @Nonnull ... options
) throws Exception;
/**
* This method will be invoked when copy a property from the source object to a destination object. Returns
* {@code false} to prevent subsequent handlers to continue to copy, otherwise returns {@code true} to continue
* to copy.
*
* @param srcPropertyName the name of the property to be copied
* @param srcProperty the property to be copied
* @param src the source object
* @param srcSchema the schema of the source object
* @param dst the destination object
* @param dstSchema the schema of the destination object
* @param converter the converter used in the mapping process
* @param options the options used in the mapping process
* @return whether to continue to copy
* @throws Exception any exception can be thrown here
*/
boolean copyProperty(
@Nonnull String srcPropertyName,
@Nonnull ObjectProperty srcProperty,
@Nonnull Object src,
@Nonnull ObjectSchema srcSchema,
@Nonnull Object dst,
@Nonnull ObjectSchema dstSchema,
@Nonnull ObjectConverter converter,
@Nonnull Option<?, ?> @Nonnull ... options
) throws Exception;
}
}