ObjectSchemaParser.java
package space.sunqian.common.object.data;
import space.sunqian.annotations.Nonnull;
import space.sunqian.annotations.RetainedParam;
import space.sunqian.annotations.ThreadSafe;
import space.sunqian.common.collect.ListKit;
import space.sunqian.common.object.data.handlers.AbstractObjectSchemaHandler;
import space.sunqian.common.object.data.handlers.SimpleBeanSchemaHandler;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
/**
* This interface is used to parse {@link Type} to {@link ObjectSchema}. It uses a list of {@link Handler}s to execute
* the specific parsing operations, where each {@link Handler} possesses its own specific parsing logic.
* <p>
* There is a skeletal handler implementation: {@link AbstractObjectSchemaHandler}. And the default parser is based on
* {@link SimpleBeanSchemaHandler}.
*
* @author sunqian
*/
@ThreadSafe
public interface ObjectSchemaParser {
/**
* Returns the default {@link ObjectSchemaParser} with {@link SimpleBeanSchemaHandler} as the only handler.
*
* @return the default {@link ObjectSchemaParser}
*/
static @Nonnull ObjectSchemaParser defaultParser() {
return ObjectSchemaParserImpl.DEFAULT;
}
/**
* Creates and returns a new {@link ObjectSchemaParser} with the given handlers.
*
* @param handlers the given handlers
* @return a new {@link ObjectSchemaParser} with the given handlers
*/
static @Nonnull ObjectSchemaParser newParser(@Nonnull @RetainedParam Handler @Nonnull ... handlers) {
return newParser(ListKit.list(handlers));
}
/**
* Creates and returns a new {@link ObjectSchemaParser} with given handlers.
*
* @param handlers given handlers
* @return a new {@link ObjectSchemaParser} with given handlers
*/
static @Nonnull ObjectSchemaParser newParser(@Nonnull @RetainedParam List<@Nonnull Handler> handlers) {
return new ObjectSchemaParserImpl(handlers);
}
/**
* Parses the given type to an instance of {@link ObjectSchema}, and returns the parsed {@link ObjectSchema}.
* <p>
* The parsing logic of the implementations must be: invokes the {@link Handler#parse(ObjectSchemaParser.Context)}
* in the order of {@link #handlers()} until one of the handlers returns {@code false}. The codes are similar to:
* <pre>{@code
* for (Handler handler : handlers()) {
* if (!handler.parse(context)) {
* break;
* }
* }
* }</pre>
* <p>
* Note that this method does not cache the results and will generate new instances every invocation.
*
* @param type the given type
* @return the parsed {@link ObjectSchema}
* @throws DataObjectException if any problem occurs
*/
default @Nonnull ObjectSchema parse(@Nonnull Type type) throws DataObjectException {
try {
ObjectSchemaBuilder builder = new ObjectSchemaBuilder(type);
for (Handler handler : handlers()) {
if (!handler.parse(builder)) {
break;
}
}
return builder.build(this);
} catch (Exception e) {
throw new DataObjectException(type, e);
}
}
/**
* Returns all handlers of this parser.
*
* @return all handlers of this parser
*/
@Nonnull
List<@Nonnull Handler> handlers();
/**
* Returns a new {@link ObjectSchemaParser} of which handler list consists of the given handler as the first
* element, followed by {@link #handlers()} of the current parser.
*
* @param handler the given handler
* @return a new {@link ObjectSchemaParser} of which handler list consists of the given handler as the first
* element, followed by {@link #handlers()} of the current parser
*/
default @Nonnull ObjectSchemaParser 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 newParser(newHandlers);
}
/**
* Returns a new {@link ObjectSchemaParser} of which handler list consists of {@link #handlers()} of the current
* parser, followed by the given handler as the last element.
*
* @param handler the given handler
* @return a {@link ObjectSchemaParser} of which handler list consists of {@link #handlers()} of the current parser,
* followed by the given handler as the last element
*/
default @Nonnull ObjectSchemaParser 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 newParser(newHandlers);
}
/**
* Returns this parser as a {@link Handler}.
*
* @return this parser as a {@link Handler}
*/
@Nonnull
Handler asHandler();
/**
* Handler for {@link ObjectSchemaParser}, provides the specific parsing logic.
*
* @author sunqian
*/
@ThreadSafe
interface Handler {
/**
* Parses {@link Type} to {@link ObjectSchema} with its owner parsing logic. The {@link Type} is specified by
* {@link Context#dataType()} of the given context, and the parsed properties should be stored in
* {@link Context#propertyBaseMap()}. Returns {@code false} to prevent subsequent handlers to continue to parse,
* otherwise returns {@code true}.
*
* @param context the given context
* @return whether to continue to parse
* @throws Exception for errors during parsing
*/
boolean parse(@Nonnull Context context) throws Exception;
}
/**
* Context for parsing the specified {@link Type} to {@link ObjectSchema}.
*
* @author sunqian
*/
interface Context {
/**
* Returns the specified type.
*
* @return the specified type
*/
@Nonnull
Type dataType();
/**
* Returns a mutable map for storing property base infos.
* <p>
* The map through whole parsing process, stores and shares the property base infos for all handlers, and each
* handler can add or remove or modify the property base info.
*
* @return a mutable map for storing property base infos
*/
@Nonnull
Map<@Nonnull String, @Nonnull ObjectPropertyBase> propertyBaseMap();
}
}