SerializableFunctionParser.java

package it.fulminazzo.yagl.parsers;

import it.fulminazzo.fulmicollection.interfaces.functions.BiFunctionException;
import it.fulminazzo.fulmicollection.interfaces.functions.TriConsumer;
import it.fulminazzo.fulmicollection.structures.tuples.Singlet;
import it.fulminazzo.fulmicollection.utils.ReflectionUtils;
import it.fulminazzo.fulmicollection.utils.SerializeUtils;
import it.fulminazzo.yagl.SerializableFunction;
import it.fulminazzo.yagl.utils.ParserUtils;
import it.fulminazzo.yamlparser.configuration.ConfigurationSection;
import it.fulminazzo.yamlparser.configuration.IConfiguration;
import it.fulminazzo.yamlparser.parsers.YAMLParser;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Constructor;

/**
 * A parser to serialize a general {@link SerializableFunction}.
 *
 * @param <F> the type of the {@link SerializableFunction}
 */
@SuppressWarnings("unchecked")
public class SerializableFunctionParser<F extends SerializableFunction> extends YAMLParser<F> {

    /**
     * Instantiates a new Serializable function parser.
     *
     * @param clazz the {@link SerializableFunction} class
     */
    public SerializableFunctionParser(final @NotNull Class<F> clazz) {
        super(clazz);
    }

    @Override
    protected BiFunctionException<IConfiguration, String, F> getLoader() {
        return (c, s) -> {
            ConfigurationSection section = c.getConfigurationSection(s);
            if (section == null) return null;
            // Get the type
            String type = section.getString("type");
            if (type == null) throw new IllegalArgumentException("'type' cannot be null");
            Class<?> clazz = typeToClass(getOClass(), type);
            // Get content
            String content = section.getString("content");
            try {
                Singlet<Object> object;
                if (content == null) {
                    Constructor<?> constructor = clazz.getConstructor();
                    object = ReflectionUtils.setAccessible(constructor).map(Constructor::newInstance);
                } else {
                    Constructor<?> constructor = clazz.getConstructor(String.class);
                    object = ReflectionUtils.setAccessible(constructor).map(o -> o.newInstance(content));
                }
                return (F) object.getValue();
            } catch (NoSuchMethodException e) {
                return SerializeUtils.deserializeFromBase64(content);
            }
        };
    }

    @Override
    protected TriConsumer<IConfiguration, String, F> getDumper() {
        return (c, s, f) -> {
            c.set(s, null);
            if (f == null) return;
            c.set(s + ".type", ParserUtils.classToType(getOClass(), (Class<? extends F>) f.getClass()));
            c.set(s + ".content", f.serialize());
        };
    }

    /**
     * Converts the given type to the corresponding class.
     *
     * @param <C>       the type of the class
     * @param mainClass the main class
     * @param type      the type
     * @return the class
     */
    protected static <C> Class<? extends C> typeToClass(final Class<C> mainClass, final @NotNull String type) {
        try {
            return ParserUtils.typeToClass(mainClass, type);
        } catch (RuntimeException e) {
            return mainClass;
        }
    }

}