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

Java tutorial

Introduction

Here is the source code for org.diorite.cfg.system.TemplateYamlConstructor.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.beans.IntrospectionException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.yaml.snakeyaml.constructor.Construct;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;

import org.diorite.cfg.system.deserializers.TemplateDeserializer;
import org.diorite.cfg.system.deserializers.TemplateDeserializers;
import org.diorite.cfg.yaml.DioriteYaml;
import org.diorite.cfg.yaml.DioriteYamlConstructor;
import org.diorite.utils.reflections.ReflectElement;

public class TemplateYamlConstructor extends DioriteYamlConstructor {
    private final ClassLoader customClassLoader;

    /**
     * Construct new TemplateYamlConstructor using custom class loader.
     *
     * @param customClassLoader other class loader than normal.
     */
    public TemplateYamlConstructor(final ClassLoader customClassLoader) {
        this.customClassLoader = customClassLoader;
    }

    /**
     * Construct new default TemplateYamlConstructor.
     */
    public TemplateYamlConstructor() {
        this.customClassLoader = null;
    }

    private static class FieldCounter {
        private final Template<?> template;
        private final Map<String, ConfigField> fields;

        private FieldCounter(final Template<?> template) {
            this.template = template;
            this.fields = template.getFieldsNameMap();
        }

        @Override
        public String toString() {
            return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).appendSuper(super.toString())
                    .append("template", this.template).append("fields", this.fields.keySet()).toString();
        }
    }

    @Override
    protected Class<?> getClassForName(final String name) throws ClassNotFoundException {
        if (this.customClassLoader != null) {
            return Class.forName(name, true, this.customClassLoader);
        }
        return Class.forName(name);
    }

    {
        this.yamlClassConstructors.put(NodeId.mapping, new TemplateConstructMapping());
    }

    /**
     * Template construct mapping, used to implement template defaults and deserializers.
     */
    public class TemplateConstructMapping extends DioriteConstructMapping {
        /**
         * Returns TemplateYamlConstructor related to this object.
         *
         * @return TemplateYamlConstructor related to this object.
         */
        public TemplateYamlConstructor getTemplateYamlConstructor() {
            return TemplateYamlConstructor.this;
        }

        @Override
        public Property getProperty(final Class<?> type, final String name) throws IntrospectionException {
            return super.getProperty(type, name);
        }

        @Override
        public Object construct(final Node node) {
            return super.construct(node);
        }

        @Override
        public void construct2ndStep(final Node node, final Object object) {
            super.construct2ndStep(node, object);
        }

        @Override
        public Object createEmptyJavaBean(final MappingNode node) {
            final TemplateDeserializer<?> deserializer = TemplateDeserializers.getElement(node.getType());
            if (deserializer != null) {
                return deserializer.construct(this, node);
            }
            return super.createEmptyJavaBean(node);
        }

        @Override
        public Object constructJavaBean2ndStep(final MappingNode node, Object object) {
            final Class<?> beanType = node.getType();
            final Template<?> template = TemplateCreator.getTemplate(beanType, false);
            final FieldCounter counter;
            if (template != null) {
                counter = new FieldCounter(template);
                node.getValue().stream().filter(nodeTuple -> nodeTuple.getKeyNode() instanceof ScalarNode).forEach(
                        nodeTuple -> counter.fields.remove(((ScalarNode) nodeTuple.getKeyNode()).getValue()));
            } else {
                counter = null;
            }
            object = super.constructJavaBean2ndStep(node, object);
            if (counter != null) {
                final Map<ConfigField, ReflectElement<?>> fields = template.getFields();
                for (final ConfigField field : counter.fields.values()) {
                    if (!field.hasDefaultValue()) {
                        continue;
                    }
                    final ReflectElement<?> element = fields.get(field);
                    if (element != null) {
                        element.set(object, field.getDefaultValue());
                    }
                }
            }
            return object;
        }
    }

    @Override
    public Class<?> getClassForNode(final Node node) {
        return super.getClassForNode(node);
    }

    @Override
    public void flattenMapping(final MappingNode node) {
        super.flattenMapping(node);
    }

    @Override
    public void constructMapping2ndStep(final MappingNode node, final Map<Object, Object> mapping) {
        super.constructMapping2ndStep(node, mapping);
    }

    @Override
    public void constructSet2ndStep(final MappingNode node, final Set<Object> set) {
        super.constructSet2ndStep(node, set);
    }

    /**
     * Unsafe version of {@link #constructObject(Node)}
     *
     * @param node Node to be constructed
     *
     * @return Java instance
     */
    public Object unsafeConstructObject(final Node node) {
        final Construct constructor = this.getConstructor(node);
        final Object data = constructor.construct(node);
        if (node.isTwoStepsConstruction()) {
            constructor.construct2ndStep(node, data);
        }
        return data;
    }

    @Override
    public Object constructObject(final Node node) {
        return super.constructObject(node);
    }

    @Override
    public Construct getConstructor(final Node node) {
        return super.getConstructor(node);
    }

    @Override
    public Object constructScalar(final ScalarNode node) {
        return super.constructScalar(node);
    }

    @Override
    public List<Object> createDefaultList(final int initSize) {
        return super.createDefaultList(initSize);
    }

    @Override
    public Set<Object> createDefaultSet(final int initSize) {
        return super.createDefaultSet(initSize);
    }

    @Override
    public Object createArray(final Class<?> type, final int size) {
        return super.createArray(type, size);
    }

    @Override
    public List<?> constructSequence(final SequenceNode node) {
        return super.constructSequence(node);
    }

    @Override
    public Set<?> constructSet(final SequenceNode node) {
        return super.constructSet(node);
    }

    @Override
    public Object constructArray(final SequenceNode node) {
        return super.constructArray(node);
    }

    @Override
    public void constructSequenceStep2(final SequenceNode node, final Collection<Object> collection) {
        super.constructSequenceStep2(node, collection);
    }

    @Override
    public Object constructArrayStep2(final SequenceNode node, final Object array) {
        return super.constructArrayStep2(node, array);
    }

    @Override
    public Map<Object, Object> createDefaultMap() {
        return super.createDefaultMap();
    }

    @Override
    public Set<Object> constructSet(final MappingNode node) {
        return super.constructSet(node);
    }

    @Override
    public Set<Object> createDefaultSet() {
        return super.createDefaultSet();
    }

    @Override
    public Map<Object, Object> constructMapping(final MappingNode node) {
        return super.constructMapping(node);
    }

    /**
     * @return snake yaml instance using DioriteYaml constructor extension, used by all templates.
     */
    public static DioriteYaml getInstance() {
        return new DioriteYaml(new TemplateYamlConstructor());
    }

    /**
     * @param customClassLoader other class loader than normal.
     *
     * @return snake yaml instance using DioriteYaml constructor extension, used by all templates.
     */
    public static DioriteYaml getInstance(final ClassLoader customClassLoader) {
        return new DioriteYaml(new TemplateYamlConstructor(customClassLoader));
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
                .append("customClassLoader", this.customClassLoader).toString();
    }
}