com.extrahardmode.config.EHMConfig.java Source code

Java tutorial

Introduction

Here is the source code for com.extrahardmode.config.EHMConfig.java

Source

/*
 * This file is part of
 * ExtraHardMode Server Plugin for Minecraft
 *
 * Copyright (C) 2012 Ryan Hamshire
 * Copyright (C) 2013 Diemex
 *
 * ExtraHardMode is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ExtraHardMode is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with ExtraHardMode.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.extrahardmode.config;

import com.extrahardmode.service.IoHelper;
import com.extrahardmode.service.config.*;
import com.extrahardmode.service.config.customtypes.BlockRelationsList;
import com.extrahardmode.service.config.customtypes.BlockType;
import com.extrahardmode.service.config.customtypes.BlockTypeList;
import com.extrahardmode.service.config.customtypes.PotionEffectHolder;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.*;

/**
 * A Wrapper that contains <ul> <li>a FileConfiguration</li> <li>A reference to the config file</li> <li>Information
 * about the Mode this Config is loaded in</li> <li>Information about which status this Config is in</li> <li></li></ul>
 */
public class EHMConfig {
    /**
     * Nodes to load from the config
     */
    private Set<ConfigNode> mConfigNodes = new LinkedHashSet<ConfigNode>();

    /**
     * Loaded config values
     */
    private Map<ConfigNode, Object> mLoadedNodes = new HashMap<ConfigNode, Object>();

    /**
     * Location we loaded the File from
     */
    private File mConfigFile;

    /**
     * Loaded FileConfiguration
     */
    private FileConfiguration mConfig;

    /**
     * Header is at the top of the file
     */
    private Header mHeader;

    /**
     * Mode with which this config will get loaded
     */
    private Mode mMode = Mode.NOT_SET;

    /**
     * Worlds in which this config is active in
     */
    private Set<String> mWorlds = new LinkedHashSet<String>(); //Linked: keeps inserted order

    /**
     * If this config is enabled for all worlds
     */
    private boolean mEnabledForAll = false;

    /**
     * If the header on top the config should be printed
     */
    private boolean mPrintHeader = true;

    /**
     * If line comments should be printed
     */
    private boolean mPrintComments = true;

    /**
     * Some status information about this Config
     */
    private Status mStatus = Status.OK;

    /**
     * Node that holds the mode of the config (string)
     */
    private ConfigNode mModeNode = RootNode.MODE;

    /**
     * Node that holds the worlds of this config (list<string>)
     */
    private ConfigNode mWorldsNode = RootNode.WORLDS;

    /**
     * Node that determines if the header should be printed
     */
    private ConfigNode mPrintHeaderNode = RootNode.PRINT_HEADER;

    /**
     * Should node comments be printed
     */
    private ConfigNode mPrintCommentsNode = RootNode.PRINT_COMMENTS;

    /**
     * Constructor
     *
     * @param file configuration file
     */
    public EHMConfig(File file) {
        this.mConfigFile = file;
        this.mConfig = YamlConfiguration.loadConfiguration(mConfigFile);
    }

    /**
     * Constructor
     *
     * @param config       that's loaded
     * @param fullFilePath fileName including the directory!
     */
    public EHMConfig(FileConfiguration config, String fullFilePath) {
        this.mConfig = config;
        mConfigFile = new File(fullFilePath);
        if (!mConfigFile.exists()) {
            try {
                mConfigFile.getParentFile().mkdirs();
                mConfigFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Validate.isTrue(mConfigFile.exists() && mConfigFile.canWrite(),
                    "FilePath " + fullFilePath + " doesn't exist or is not writable");
        }
    }

    /**
     * Constructor
     *
     * @param config that's loaded
     * @param file   File-Object to save to
     */
    public EHMConfig(FileConfiguration config, File file) {
        this.mConfig = config;
        mConfigFile = file;
    }

    /**
     * Initializes and fully loads this configuration
     * <pre>
     * - load mode
     * - load worlds
     * - load nodes
     * - verify nodes
     * </pre>
     */
    public void load() {
        if (mConfigNodes.isEmpty())
            throw new IllegalStateException("You forgot to add nodes to " + mConfigFile.getName());
        loadMode();
        loadWorlds();
        loadCommentOptions();
        loadNodes();
        validateNodes();
    }

    /**
     * Saves the config to file
     */
    public void save() {
        if (mLoadedNodes.isEmpty())
            throw new IllegalStateException("No nodes are loaded, nothing to save to " + mConfigFile.getName());
        saveNodes();
        if (mPrintHeader)
            writeHeader();
    }

    public Map<ConfigNode, Object> getLoadedNodes() {
        return mLoadedNodes;
    }

    /**
     * Get loaded FileConfiguration
     */
    public FileConfiguration getFileConfiguration() {
        return mConfig;
    }

    /**
     * Set FileConfiguration
     */
    public void setFileConfiguration(FileConfiguration config) {
        this.mConfig = config;
    }

    /**
     * Returns the File to save the FileConfiguration to
     */
    public File getConfigFile() {
        return mConfigFile;
    }

    /**
     * Get the fileName of this Config
     */
    public String getFileName() {
        return mConfigFile.getName();
    }

    /**
     * Set where to save this Config
     */
    public void setConfigFile(File configFile) {
        this.mConfigFile = configFile;
    }

    /**
     * Get the Mode this config should be loaded
     *
     * @return mode or Mode.NOT_SET if not set yet
     */
    public Mode getMode() {
        return mMode;
    }

    /**
     * Set the Mode with which this Config should be loaded
     *
     * @param mode to set
     */
    public void setMode(Mode mode) {
        this.mMode = mode;
    }

    /**
     * Get the Status of this config
     *
     * @return Status, initialized with Status.OK
     */
    public Status getStatus() {
        return mStatus;
    }

    /**
     * Set the Status of this Config
     *
     * @param status of the Config
     */
    public void setStatus(Status status) {
        this.mStatus = status;
    }

    /**
     * Changes the node from which to load the mode
     *
     * @param node node to set it to
     */
    public void setModeNode(ConfigNode node) {
        this.mModeNode = node;
    }

    /**
     * Changes the node from which to load the worlds
     *
     * @param node node to set it to
     */
    public void setWorldsNode(ConfigNode node) {
        this.mWorldsNode = node;
    }

    /**
     * Changes the node from which to load if we should print the header
     *
     * @param node node to set it to
     */
    public void setPrintHeaderNode(ConfigNode node) {
        this.mPrintHeaderNode = node;
    }

    /**
     * Changes the node from which to load if we should print line comments
     *
     * @param node node to set it to
     */

    public void setPrintCommentsNode(ConfigNode node) {
        this.mPrintCommentsNode = node;
    }

    /**
     * Register new nodes to load from the config
     *
     * @param nodes nodes to register
     */
    public void registerNodes(ConfigNode[] nodes) {
        Collections.addAll(mConfigNodes, nodes);
    }

    /**
     * Register new nodes to load from the config
     *
     * @param nodes nodes to register
     */
    public void registerNodes(Collection<ConfigNode> nodes) {
        mConfigNodes.addAll(nodes);
    }

    /**
     * Get the worlds for this config
     *
     * @return worlds in which the config is active
     */
    public Collection<String> getWorlds() {
        return mWorlds;
    }

    /**
     * Load the mode from the config
     */
    public void loadMode() {
        //DETERMINE MODE FIRST
        String modeString = mConfig.getString(mModeNode.getPath());
        try {
            if (modeString != null)
                setMode(Mode.valueOf(modeString.toUpperCase()));
        } catch (IllegalArgumentException ignored) {
        } finally {
            if (getMode() == null || getMode() == Mode.NOT_SET) {
                if (isMainConfig())
                    setMode(Mode.MAIN);
                else
                    setMode(Mode.INHERIT);
            }
        }
    }

    /**
     * Load the worlds where this config is active
     */
    public void loadWorlds() {
        mWorlds.addAll(mConfig.getStringList(mWorldsNode.getPath()));

        //Check for all worlds placeholder = Enables plugin for all worlds
        for (String world : mWorlds)
            if (world.equals(MultiWorldConfig.ALL_WORLDS))
                mEnabledForAll = true;
    }

    public void loadCommentOptions() {
        mPrintHeader = mConfig.getBoolean(mPrintHeaderNode.getPath(), true);
        mPrintComments = mConfig.getBoolean(mPrintCommentsNode.getPath(), true);
    }

    /**
     * Load all values from the config and save in our map
     */
    public void loadNodes() {
        loop: for (ConfigNode node : mConfigNodes) {
            Object obj = null;

            switch (node.getVarType()) {
            case LIST: {
                if (mConfig.get(node.getPath()) instanceof List)
                    obj = mConfig.getStringList(node.getPath());
                break;
            }
            case DOUBLE: {
                if (mConfig.get(node.getPath()) instanceof Double)
                    obj = mConfig.getDouble(node.getPath());
                break;
            }
            case STRING: {
                if (mConfig.get(node.getPath()) instanceof String)
                    obj = mConfig.getString(node.getPath());
                break;
            }
            case INTEGER: {
                if (mConfig.get(node.getPath()) instanceof Integer)
                    obj = mConfig.getInt(node.getPath());
                break;
            }
            case BOOLEAN: {
                if (mConfig.get(node.getPath()) instanceof Boolean)
                    obj = mConfig.getBoolean(node.getPath());
                break;
            }
            case POTION_EFFECT: {
                ConfigurationSection section = mConfig.getConfigurationSection(node.getPath());
                obj = PotionEffectHolder.loadFromConfig(section);
                break;
            }
            case BLOCKTYPE: {
                if (mConfig.getString(node.getPath()) != null)
                    obj = BlockType.loadFromConfig(mConfig.getString(node.getPath()));
                break;
            }
            case BLOCKTYPE_LIST: {
                if (mConfig.get(node.getPath()) instanceof List) {
                    List<String> list = mConfig.getStringList(node.getPath());
                    BlockTypeList blocks = new BlockTypeList();
                    for (String str : list) {
                        BlockType block = BlockType.loadFromConfig(str);
                        if (block != null)
                            blocks.add(block);
                    }
                    obj = blocks;
                } else if (mConfig.isSet(node.getPath()))
                    obj = BlockTypeList.EMPTY_LIST;
                break;
            }
            case BLOCK_RELATION_LIST: {
                if (mConfig.get(node.getPath()) instanceof List) {
                    List<String> list = mConfig.getStringList(node.getPath());
                    BlockRelationsList blocks = new BlockRelationsList();
                    for (String str : list)
                        blocks.addFromConfig(str);
                    obj = blocks;
                } else if (mConfig.isSet(node.getPath()))
                    obj = BlockRelationsList.EMPTY_LIST;
                break;
            }
            //ignore comments
            case COMMENT:
                continue loop;
            default: {
                obj = mConfig.get(node.getPath());
                throw new UnsupportedOperationException(
                        node.getPath() + "No specific getter available for Type: " + " " + node.getVarType());
            }
            }
            mLoadedNodes.put(node, obj);
        }
    }

    /**
     * Make sure that all our loaded values are valid and usable by the plugin
     */
    public void validateNodes() {
        for (ConfigNode node : mConfigNodes) {
            switch (node.getVarType()) {
            case INTEGER: {
                Integer validated = Validation.validateInt(node, mLoadedNodes.get(node));
                mLoadedNodes.put(node, validated);
                break;
            }
            case COMMENT:
                break;
            //TODO ADD BLOCKTYPE_LIST
            default: {
                Object validateMe = mLoadedNodes.get(node);
                if (validateMe == null)
                    mLoadedNodes.put(node, node.getDefaultValue());
            }
            }
            //Make sure the string of the node matches
            if (node == mModeNode)
                mLoadedNodes.put(node, mMode.name());
        }
    }

    /**
     * Writes all our validated objects back to the config in the order they were added
     */
    private void saveNodes() {
        FileConfiguration outConfig = new YamlConfiguration();
        for (ConfigNode node : mConfigNodes) {
            Object value = mLoadedNodes.get(node);
            //Custom writing code for our custom objects
            switch (node.getVarType()) {
            case BLOCKTYPE: {
                if (value instanceof BlockType) {
                    outConfig.set(node.getPath(), ((BlockType) value).saveToString());
                    break;
                }
            }
            case BLOCKTYPE_LIST: {
                if (value instanceof BlockTypeList) {
                    List<String> blockStrings = new ArrayList<String>();
                    for (BlockType blockType : ((BlockTypeList) value).toArray())
                        blockStrings.add(blockType.saveToString());
                    outConfig.set(node.getPath(), blockStrings);
                    break;
                }
            }
            case BLOCK_RELATION_LIST: {
                if (value instanceof BlockRelationsList) {
                    String[] blockStrings = ((BlockRelationsList) value).toConfigStrings();
                    outConfig.set(node.getPath(), blockStrings);
                    break;
                }
            }
            case POTION_EFFECT: {
                if (value instanceof PotionEffectHolder) {
                    ((PotionEffectHolder) value).saveToConfig(outConfig, node.getPath());
                    break;
                }
            }
            case COMMENT:
                break;
            default: {
                outConfig.set(node.getPath(), value);
            }
            }
        }
        try {
            outConfig.save(mConfigFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void writeHeader() {
        if (mHeader == null)
            return;
        ByteArrayOutputStream memHeaderStream = null;
        OutputStreamWriter memWriter = null;
        try {
            //Write Header to a temporary file
            memHeaderStream = new ByteArrayOutputStream();
            memWriter = new OutputStreamWriter(memHeaderStream, Charset.forName("UTF-8").newEncoder());
            memWriter.write(String.format(mHeader.toString()));
            memWriter.close();
            //Copy Header to the beginning of the config file
            IoHelper.writeHeader(mConfigFile, memHeaderStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (memHeaderStream != null)
                    memHeaderStream.close();
                if (memWriter != null)
                    memWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean isEnabledForAll() {
        return mEnabledForAll;
    }

    public boolean isMainConfig() {
        return mConfigFile.getName().equals("config.yml");
    }

    /**
     * Can this config be loaded, e.g. it has all the required nodes
     *
     * @return if the config can be loaded
     */
    public boolean isValid() {
        if (mConfig == null)
            throw new IllegalStateException("FileConfiguration hasn't been loaded yet");
        return (mConfig.getValues(true).containsKey(RootNode.baseNode())
                && mConfig.getStringList(RootNode.WORLDS.getPath()) != null) || isMainConfig();
    }

    public void setHeader(Header header) {
        this.mHeader = header;
    }

    public boolean printHeader() {
        return mPrintHeader;
    }

    public boolean printComments() {
        return mPrintComments;
    }
}