org.diorite.cfg.system.Template.java Source code

Java tutorial

Introduction

Here is the source code for org.diorite.cfg.system.Template.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016. Diorite (by Bartomiej Mazur (aka GotoFinal))
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package org.diorite.cfg.system;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import org.diorite.cfg.system.elements.TemplateElement.ElementPlace;
import org.diorite.utils.reflections.ReflectElement;

/**
 * Object representation of class yaml template, used to generate
 * string with yaml, from given object.
 *
 * @param <T> type of object.
 */
public interface Template<T> {
    /**
     * @return name of template.
     */
    String getName();

    /**
     * @return type of template.
     */
    Class<T> getType();

    /**
     * @return header comment, may be null.
     */
    String getHeader();

    /**
     * @return footer comment, may be null.
     */
    String getFooter();

    /**
     * @return copy of map contains all fields data.
     */
    Map<ConfigField, ReflectElement<?>> getFields();

    /**
     * @return copy of map with mapping key{@literal ->}field.
     */
    Map<String, ConfigField> getFieldsNameMap();

    /**
     * @return class loader used by template.
     */
    ClassLoader getDefaultClassLoader();

    /**
     * Load object from string using this template.
     *
     * @param str         string with object data in yaml.
     * @param classLoader other class loader to use when creating objects.
     *
     * @return object created from yaml data and template.
     */
    T load(String str, ClassLoader classLoader);

    /**
     * Load object from {@link Reader} using this template.
     *
     * @param reader      {@link Reader} with object data in yaml.
     * @param classLoader other class loader to use when creating objects.
     *
     * @return object created from yaml data and template.
     */
    T load(Reader reader, ClassLoader classLoader);

    /**
     * Load object from {@link InputStream} using this template.
     *
     * @param is          {@link InputStream} with object data in yaml.
     * @param classLoader other class loader to use when creating objects.
     *
     * @return object created from yaml data and template.
     */
    T load(InputStream is, ClassLoader classLoader);

    /**
     * Load object from string using this template.
     *
     * @param str string with object data in yaml.
     *
     * @return object created from yaml data and template.
     */
    default T load(final String str) {
        return this.load(str, this.getDefaultClassLoader());
    }

    /**
     * Load object from {@link Reader} using this template.
     *
     * @param reader {@link Reader} with object data in yaml.
     *
     * @return object created from yaml data and template.
     */
    default T load(final Reader reader) {
        return this.load(reader, this.getDefaultClassLoader());
    }

    /**
     * Load object from {@link InputStream} using this template.
     *
     * @param is {@link InputStream} with object data in yaml.
     *
     * @return object created from yaml data and template.
     */
    default T load(final InputStream is) {
        return this.load(is, this.getDefaultClassLoader());
    }

    /**
     * Load object from {@link File} using this template.
     *
     * @param file        {@link File} with object data in yaml.
     * @param charset     encoding of file.
     * @param classLoader other class loader to use when creating objects.
     *
     * @return object created from yaml data and template.
     *
     * @throws IOException if file can't be found or it can't be opened.
     */
    default T load(final File file, final Charset charset, final ClassLoader classLoader) throws IOException {
        try (final InputStreamReader in = new InputStreamReader(new FileInputStream(file), charset)) {
            return this.load(in, classLoader);
        }
    }

    /**
     * Load object from {@link File} using this template.
     *
     * @param file    {@link File} with object data in yaml.
     * @param charset encoding of file.
     *
     * @return object created from yaml data and template.
     *
     * @throws IOException if file can't be found or it can't be opened.
     */
    default T load(final File file, final Charset charset) throws IOException {
        return this.load(file, charset, this.getDefaultClassLoader());
    }

    /**
     * Load object from {@link File} using this template. (UTF-8)
     *
     * @param file {@link File} with object data in yaml.
     *
     * @return object created from yaml data and template.
     *
     * @throws IOException if file can't be found or it can't be opened.
     * @see #load(File, Charset)
     */
    default T load(final File file) throws IOException {
        return this.load(file, StandardCharsets.UTF_8);
    }

    /**
     * dump object to selected {@link Appendable}.
     *
     * @param writer             {@link Appendable} to use, all data will be added here.
     * @param object             object to dump. (will be represented as YAML string in writer)
     * @param level              current indent level.
     * @param writeComments      if comments should be added to node.
     * @param elementPlace       element place, used in many templates to check current style and choose valid format.
     * @param forceDefaultValues if true, all values will be set to default ones.
     * @param <E>                exact type of appendable, used to return this same type as given.
     *
     * @return this same appendalbe as given, after adding string yaml representation of given object.
     *
     * @throws IOException from {@link Appendable}
     */
    <E extends Appendable> E dump(E writer, T object, int level, boolean writeComments, ElementPlace elementPlace,
            boolean forceDefaultValues) throws IOException;

    /**
     * dump object to selected {@link Appendable}.
     *
     * @param writer        {@link Appendable} to use, all data will be added here.
     * @param object        object to dump. (will be represented as YAML string in writer)
     * @param level         current indent level.
     * @param writeComments if comments should be added to node.
     * @param elementPlace  element place, used in many templates to check current style and choose valid format.
     * @param <E>           exact type of appendable, used to return this same type as given.
     *
     * @return this same appendalbe as given, after adding string yaml representation of given object.
     *
     * @throws IOException from {@link Appendable}
     */
    default <E extends Appendable> E dump(final E writer, final T object, final int level,
            final boolean writeComments, final ElementPlace elementPlace) throws IOException {
        return this.dump(writer, object, level, writeComments, elementPlace, false);
    }

    /**
     * dump object to selected {@link Appendable}.
     *
     * @param writer        {@link Appendable} to use, all data will be added here.
     * @param object        object to dump. (will be represented as YAML string in writer)
     * @param level         current indent level.
     * @param writeComments if comments should be added to node.
     * @param <E>           exact type of appendable, used to return this same type as given.
     *
     * @return this same appendalbe as given, after adding string yaml representation of given object.
     *
     * @throws IOException from {@link Appendable}\
     */
    default <E extends Appendable> E dump(final E writer, final T object, final int level,
            final boolean writeComments) throws IOException {
        return this.dump(writer, object, level, writeComments, ElementPlace.NORMAL);
    }

    /**
     * dump object to selected {@link Appendable}.
     *
     * @param writer {@link Appendable} to use, all data will be added here.
     * @param object object to dump. (will be represented as YAML string in writer)
     * @param level  current indent level.
     * @param <E>    exact type of appendable, used to return this same type as given.
     *
     * @return this same appendalbe as given, after adding string yaml representation of given object.
     *
     * @throws IOException from {@link Appendable}\
     */
    default <E extends Appendable> E dump(final E writer, final T object, final int level) throws IOException {
        return this.dump(writer, object, level, true, ElementPlace.NORMAL);
    }

    /**
     * dump object to selected {@link Appendable}.
     *
     * @param writer             {@link Appendable} to use, all data will be added here.
     * @param object             object to dump. (will be represented as YAML string in writer)
     * @param forceDefaultValues if true, all values will be set to default ones.
     * @param <E>                exact type of appendable, used to return this same type as given.
     *
     * @return this same appendalbe as given, after adding string yaml representation of given object.
     *
     * @throws IOException from {@link Appendable}\
     */
    default <E extends Appendable> E dump(final E writer, final T object, final boolean forceDefaultValues)
            throws IOException {
        return this.dump(writer, object, 0, true, ElementPlace.NORMAL, forceDefaultValues);
    }

    /**
     * dump object to selected {@link Appendable}.
     *
     * @param writer {@link Appendable} to use, all data will be added here.
     * @param object object to dump. (will be represented as YAML string in writer)
     * @param <E>    exact type of appendable, used to return this same type as given.
     *
     * @return this same appendalbe as given, after adding string yaml representation of given object.
     *
     * @throws IOException from {@link Appendable}\
     */
    default <E extends Appendable> E dump(final E writer, final T object) throws IOException {
        return this.dump(writer, object, 0, true, ElementPlace.NORMAL, false);
    }

    /**
     * dump object to selected {@link File} in YAML text format.
     *
     * @param file               file to use, it will be created if needed.
     * @param object             object to dump. (will be represented as YAML string in file)
     * @param charset            encoding of file. * @param forceDefaultValues if true, all values will be set to default ones.
     * @param forceDefaultValues if true, all values will be set to default ones.
     *
     * @throws IOException if file can't be created/edited.
     */
    void dump(File file, T object, Charset charset, boolean forceDefaultValues) throws IOException;

    /**
     * dump object to selected {@link File} in YAML text format.
     *
     * @param file    file to use, it will be created if needed.
     * @param object  object to dump. (will be represented as YAML string in file)
     * @param charset encoding of file. * @param forceDefaultValues if true, all values will be set to default ones.
     *
     * @throws IOException if file can't be created/edited.
     */
    default void dump(final File file, final T object, final Charset charset) throws IOException {
        this.dump(file, object, charset, false);
    }

    /**
     * dump object to selected {@link File} in YAML text format.
     *
     * @param file               file to use, it will be created if needed.
     * @param object             object to dump. (will be represented as YAML string in file)
     * @param forceDefaultValues if true, all values will be set to default ones.
     *
     * @throws IOException if file can't be created/edited.
     */
    default void dump(final File file, final T object, final boolean forceDefaultValues) throws IOException {
        this.dump(file, object, StandardCharsets.UTF_8, forceDefaultValues);
    }

    /**
     * dump object to selected {@link File} in YAML text format.
     *
     * @param file   file to use, it will be created if needed.
     * @param object object to dump. (will be represented as YAML string in file)
     *
     * @throws IOException if file can't be created/edited.
     */
    default void dump(final File file, final T object) throws IOException {
        this.dump(file, object, StandardCharsets.UTF_8, false);
    }

    /**
     * dump object to YAML string.
     *
     * @param object             object to dump. (will be represented as YAML string in writer)
     * @param forceDefaultValues if true, all values will be set to default ones.
     *
     * @return string yaml representation of given object.
     */
    String dumpAsString(T object, boolean forceDefaultValues);

    /**
     * dump object to YAML string.
     *
     * @param object object to dump. (will be represented as YAML string in writer)
     *
     * @return string yaml representation of given object.
     */
    default String dumpAsString(final T object) {
        return this.dumpAsString(object, false);
    }

    /**
     * Fill object with default values
     *
     * @param obj object to fill.
     *
     * @return this same - given - object.
     */
    T fillDefaults(T obj);

    /**
     * Append indent (2 spaces per level)
     *
     * @param writer {@link Appendable} to use, all data will be added here.
     * @param level  current indent level.
     *
     * @throws IOException from {@link Appendable}
     */
    static void spaces(final Appendable writer, final int level) throws IOException {
        if (level <= 0) {
            return;
        }
        for (int i = 0; i < level; i++) {
            writer.append("  ");
        }
    }

    /**
     * Append comment to given {@link Appendable}
     *
     * @param writer   {@link Appendable} to use, all data will be added here.
     * @param string   comment string to add, may be multi-line.
     * @param level    current indent level.
     * @param addSpace if true, additional space will be added before first # and no indent for first line.
     *
     * @throws IOException from {@link Appendable}
     */
    static void appendComment(final Appendable writer, final String string, final int level, final boolean addSpace)
            throws IOException {
        if (string != null) {
            final String[] split = StringUtils.split(string, '\n');
            boolean first = true;
            for (int i = 0, splitLength = split.length; i < splitLength; i++) {
                final String str = split[i];
                if (!first || !addSpace) {
                    spaces(writer, level);
                    writer.append("# ");
                } else {
                    first = false;
                    writer.append(" # ");
                }
                writer.append(str);
                if ((i + 1) < splitLength) {
                    writer.append('\n');
                }
            }
        }
    }
}