com.virtlink.commons.configuration2.jackson.JacksonConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for com.virtlink.commons.configuration2.jackson.JacksonConfiguration.java

Source

/*
 * Copyright 2015-2015 Daniel Pelsmaeker
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.virtlink.commons.configuration2.jackson;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.HierarchicalConfiguration;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.io.FileLocator;
import org.apache.commons.configuration2.io.FileLocatorAware;
import org.apache.commons.configuration2.io.InputStreamSupport;
import org.apache.commons.configuration2.tree.ImmutableNode;
import org.apache.commons.configuration2.tree.ImmutableNode.Builder;

import javax.annotation.Nullable;
import java.io.*;
import java.util.*;

/**
 * A configuration using a Jackson language (e.g. YAML, JSON).
 */
public abstract class JacksonConfiguration extends BaseHierarchicalConfiguration
        implements FileBasedConfiguration, InputStreamSupport, FileLocatorAware {

    private static final TypeReference<HashMap<String, Object>> HASH_MAP_TYPE_REFERENCE = new TypeReference<HashMap<String, Object>>() {
    };
    private final SimpleModule module;
    private final ObjectMapper mapper;

    /**
     * The {@link FileLocator} that may specify where the source/destination file is.
     * <p>
     * This is for use by derived classses, if necessary.
     */
    @Nullable
    protected FileLocator locator = null;

    /**
     * Initializes a new instance of the {@link JacksonConfiguration} class.
     *
     * @param factory The Jackson factory to use.
     */
    protected JacksonConfiguration(final JsonFactory factory) {
        this(factory, null);
    }

    /**
     * Initializes a new instance of the {@link JacksonConfiguration} class.
     *
     * @param factory The Jackson factory to use.
     * @param config  The configuration whose nodes to copy into this configuration.
     */
    protected JacksonConfiguration(final JsonFactory factory,
            final HierarchicalConfiguration<ImmutableNode> config) {
        super(config);

        Preconditions.checkNotNull(factory);

        this.module = new SimpleModule();
        this.mapper = new ObjectMapper(factory);
        this.mapper.registerModule(this.module);
    }

    /**
     * Reads the configuration.
     *
     * @param inputStream The input stream to read from.
     * @throws ConfigurationException
     * @throws IOException
     */
    @Override
    public void read(final InputStream inputStream) throws ConfigurationException, IOException {
        try (Reader reader = new InputStreamReader(inputStream)) {
            read(reader);
        }
    }

    /**
     * Reads the configuration.
     *
     * @param reader The reader to read from.
     * @throws ConfigurationException
     * @throws IOException
     */
    @Override
    public void read(final Reader reader) throws ConfigurationException, IOException {
        Preconditions.checkNotNull(reader);

        final HashMap<String, Object> settings = this.mapper.readValue(reader, HASH_MAP_TYPE_REFERENCE);
        final ImmutableNode rootNode = toNode(new Builder(), settings);
        this.getSubConfigurationParentModel().mergeRoot(rootNode, null, null, null, this);
    }

    /**
     * Writes the configuration.
     *
     * @param writer The writer to write to.
     * @throws ConfigurationException
     * @throws IOException
     */
    @Override
    public void write(final Writer writer) throws ConfigurationException, IOException {
        Preconditions.checkNotNull(writer);

        @SuppressWarnings("unchecked")
        final HashMap<String, Object> settings = (HashMap<String, Object>) fromNode(
                this.getModel().getInMemoryRepresentation());
        this.mapper.writerWithDefaultPrettyPrinter().writeValue(writer, settings);
    }

    /**
     * Sets the file locator to use for the next invocation of {@link #read(InputStream)} or {@link #write(Writer)}.
     *
     * @param locator The file locator to use; or <code>null</code>.
     */
    @Override
    public void initFileLocator(@Nullable final FileLocator locator) {
        this.locator = locator;
    }

    /**
     * Creates a node for the specified object.
     *
     * @param builder The node builder.
     * @param obj The object.
     * @return The created node.
     */
    private ImmutableNode toNode(final Builder builder, final Object obj) {
        assert !(obj instanceof List);

        if (obj instanceof Map) {
            return mapToNode(builder, (Map<String, Object>) obj);
        } else {
            return valueToNode(builder, obj);
        }
    }

    /**
     * Creates a node for the specified map.
     *
     * @param builder The node builder.
     * @param map The map.
     * @return The created node.
     */
    private ImmutableNode mapToNode(final Builder builder, final Map<String, Object> map) {
        for (final Map.Entry<String, Object> entry : map.entrySet()) {
            final String key = entry.getKey();
            final Object value = entry.getValue();
            if (value instanceof List) {
                // For a list, add each list item as a child of this node.
                for (final Object item : (List) value) {
                    addChildNode(builder, key, item);
                }
            } else {
                // Otherwise, add the value as a child of this node.
                addChildNode(builder, key, value);
            }
        }
        return builder.create();
    }

    /**
     * Adds a child node to the specified builder.
     *
     * @param builder The builder to add the node to.
     * @param name The name of the node.
     * @param value The value of the node.
     */
    private void addChildNode(final Builder builder, final String name, final Object value) {
        assert !(value instanceof List);

        final Builder childBuilder = new Builder();
        // Set the name of the child node.
        childBuilder.name(name);
        // Set the value of the child node.
        final ImmutableNode childNode = toNode(childBuilder, value);
        // Add the node to the children of the node being built.
        builder.addChild(childNode);
    }

    /**
     * Creates a node for the specified value.
     *
     * @param builder The node builder.
     * @param value The value.
     * @return The created node.
     */
    private ImmutableNode valueToNode(final Builder builder, final Object value) {
        // Set the value of the node being built.
        return builder.value(value).create();
    }

    /**
     * Gets a serialization object from a node.
     *
     * @param node The node.
     * @return The object representing the node.
     */
    private Object fromNode(final ImmutableNode node) {
        if (!node.getChildren().isEmpty()) {
            final Map<String, List<ImmutableNode>> children = getChildrenWithName(node);

            final HashMap<String, Object> map = new HashMap<>();
            for (final Map.Entry<String, List<ImmutableNode>> entry : children.entrySet()) {
                assert !entry.getValue().isEmpty();
                if (entry.getValue().size() == 1) {
                    // Just one node.
                    final ImmutableNode child = entry.getValue().get(0);
                    final Object childValue = fromNode(child);
                    map.put(entry.getKey(), childValue);
                } else {
                    // Multiple nodes.
                    final ArrayList<Object> list = new ArrayList<>();
                    for (final ImmutableNode child : entry.getValue()) {
                        final Object childValue = fromNode(child);
                        list.add(childValue);
                    }
                    map.put(entry.getKey(), list);
                }
            }
            return map;
        } else {
            return node.getValue();
        }
    }

    private Map<String, List<ImmutableNode>> getChildrenWithName(final ImmutableNode node) {
        final Map<String, List<ImmutableNode>> children = new HashMap<>();
        for (final ImmutableNode child : node.getChildren()) {
            final String name = child.getNodeName();
            if (!children.containsKey(name)) {
                children.put(name, new ArrayList<ImmutableNode>());
            }
            children.get(name).add(child);
        }
        return children;
    }
}