ObjectSchema.java

package space.sunqian.common.object.data;

import space.sunqian.annotations.Immutable;
import space.sunqian.annotations.Nonnull;
import space.sunqian.annotations.Nullable;

import java.beans.BeanInfo;
import java.lang.reflect.Type;
import java.util.Map;

/**
 * This interface represents the schema of non-map object, parsed by a {@link ObjectSchemaParser}, and provides
 * information about the object's properties.
 * <p>
 * It is very similar to the {@link BeanInfo} used to describe
 * <a href="https://www.oracle.com/java/technologies/javase/javabeans-spec.html">JavaBeans</a>, but it only includes
 * simple properties, without indexed properties, events, methods, or other more complex components. And the rules for
 * parsing properties are defined by the implementation of {@link ObjectSchemaParser}, rather than a public rules.
 * <p>
 * Two {@link ObjectSchema}s are considered equal if, and only if both types of objects and both parsers from
 * {@link #parser()} are equal.
 *
 * @author sunqian
 */
@Immutable
public interface ObjectSchema extends DataSchema {

    /**
     * Parse the given type to {@link ObjectSchema} using {@link ObjectSchemaParser#defaultParser()}.
     * <p>
     * Note that this method does not cache the results and will generate new instances every invocation.
     *
     * @param type the given type
     * @return the {@link ObjectSchema} parsed from the given type using {@link ObjectSchemaParser#defaultParser()}
     */
    static @Nonnull ObjectSchema parse(@Nonnull Type type) {
        return ObjectSchemaParser.defaultParser().parse(type);
    }

    /**
     * Returns the {@link ObjectSchemaParser} of this {@link ObjectSchema}.
     *
     * @return the {@link ObjectSchemaParser} of this {@link ObjectSchema}
     */
    @Nonnull
    ObjectSchemaParser parser();

    /**
     * Returns a map contains all properties of this {@link ObjectSchema}.
     *
     * @return a map contains all properties of this {@link ObjectSchema}
     */
    @Immutable
    @Nonnull
    Map<@Nonnull String, @Nonnull ObjectProperty> properties();

    /**
     * Returns the specified property with the specified name in this {@link ObjectSchema}.
     *
     * @param name the specified name
     * @return the specified property with the specified name in this {@link ObjectSchema}
     */
    default @Nullable ObjectProperty getProperty(String name) {
        return properties().get(name);
    }

    @Override
    default boolean isMapSchema() {
        return false;
    }

    @Override
    default boolean isObjectSchema() {
        return true;
    }

    /**
     * Returns whether this {@link ObjectSchema} is equal to the other {@link ObjectSchema}. They are considered equal
     * if, and only if both types of objects and both parsers from {@link #parser()} are equal.
     *
     * @param other the other {@link ObjectSchema}
     * @return whether this {@link ObjectSchema} is equal to the other {@link ObjectSchema}
     */
    boolean equals(Object other);

    /**
     * Returns the hash code of this {@link ObjectSchema}. The hash code is generated via {@link #type()} and
     * {@link #parser()} like following codes:
     * <pre>{@code
     * int result = 1;
     * result = 31 * result + type().hashCode();
     * result = 31 * result + parser().hashCode();
     * return result;
     * }</pre>
     *
     * @return the hash code of this {@link ObjectSchema}
     */
    int hashCode();

    /**
     * Returns a string representation of this {@link ObjectSchema}. The string is generated like following codes:
     * <pre>{@code
     * return type().getTypeName() + "[" +
     *     properties().values().stream()
     *         .map(DataProperty::toString)
     *         .collect(Collectors.joining(", "))
     *     + "]";
     * }</pre>
     *
     * @return a string representation of this {@link ObjectSchema}
     */
    @Nonnull
    String toString();
}