001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.core.configuration.file;
020
021import com.plotsquared.core.configuration.Configuration;
022import com.plotsquared.core.configuration.InvalidConfigurationException;
023import com.plotsquared.core.configuration.MemoryConfiguration;
024
025import java.io.BufferedReader;
026import java.io.File;
027import java.io.FileInputStream;
028import java.io.FileNotFoundException;
029import java.io.FileOutputStream;
030import java.io.IOException;
031import java.io.InputStreamReader;
032import java.io.OutputStreamWriter;
033import java.io.Reader;
034import java.io.Writer;
035import java.nio.charset.StandardCharsets;
036import java.util.stream.Collectors;
037
038/**
039 * This is a base class for all File based implementations of {@link
040 * Configuration}.
041 */
042public abstract class FileConfiguration extends MemoryConfiguration {
043
044    /**
045     * Creates an empty FileConfiguration with no default values.
046     */
047    FileConfiguration() {
048    }
049
050    /**
051     * Creates an empty FileConfiguration using the specified {@link
052     * Configuration} as a source for all default values.
053     *
054     * @param defaults Default value provider
055     */
056    public FileConfiguration(Configuration defaults) {
057        super(defaults);
058    }
059
060    /**
061     * Saves this FileConfiguration to the specified location.
062     *
063     * <p>If the file does not exist, it will be created. If already exists, it
064     * will be overwritten. If it cannot be overwritten or created, an
065     * exception will be thrown.
066     *
067     * <p>This method will save using the system default encoding, or possibly
068     * using UTF8.
069     *
070     * @param file File to save to.
071     * @throws IOException Thrown when the given file cannot be written to for
072     *                     any reason.
073     */
074    public void save(File file) throws IOException {
075        File parent = file.getParentFile();
076        if (parent != null) {
077            parent.mkdirs();
078        }
079
080        String data = saveToString();
081
082        try (Writer writer = new OutputStreamWriter(
083                new FileOutputStream(file),
084                StandardCharsets.UTF_8
085        )) {
086            writer.write(data);
087        }
088    }
089
090    /**
091     * Saves this FileConfiguration to a string, and returns it.
092     *
093     * @return String containing this configuration.
094     */
095    public abstract String saveToString();
096
097    /**
098     * Loads this FileConfiguration from the specified location.
099     *
100     * <p>All the values contained within this configuration will be removed,
101     * leaving only settings and defaults, and the new values will be loaded
102     * from the given file.
103     *
104     * <p>If the file cannot be loaded for any reason, an exception will be
105     * thrown.
106     *
107     * @param file File to load from.
108     * @throws FileNotFoundException         Thrown when the given file cannot be
109     *                                       opened.
110     * @throws IOException                   Thrown when the given file cannot be read.
111     * @throws InvalidConfigurationException Thrown when the given file is not
112     *                                       a valid Configuration.
113     * @throws IllegalArgumentException      Thrown when file is null.
114     */
115    public void load(File file) throws IOException, InvalidConfigurationException {
116
117        try (FileInputStream stream = new FileInputStream(file)) {
118            load(new InputStreamReader(stream, StandardCharsets.UTF_8));
119        }
120    }
121
122    /**
123     * Loads this FileConfiguration from the specified reader.
124     *
125     * <p>All the values contained within this configuration will be removed,
126     * leaving only settings and defaults, and the new values will be loaded
127     * from the given stream.
128     *
129     * @param reader the reader to load from
130     * @throws IOException                   thrown when underlying reader throws an IOException
131     * @throws InvalidConfigurationException thrown when the reader does not
132     *                                       represent a valid Configuration
133     */
134    public void load(Reader reader) throws IOException, InvalidConfigurationException {
135
136        String builder;
137
138        try (BufferedReader input = reader instanceof BufferedReader ?
139                (BufferedReader) reader :
140                new BufferedReader(reader)) {
141
142            builder = input.lines().map(line -> line + '\n').collect(Collectors.joining());
143        }
144
145        loadFromString(builder);
146    }
147
148    /**
149     * Loads this FileConfiguration from the specified string, as
150     * opposed to from file.
151     *
152     * <p>All the values contained within this configuration will be removed,
153     * leaving only settings and defaults, and the new values will be loaded
154     * from the given string.
155     *
156     * <p>If the string is invalid in any way, an exception will be thrown.
157     *
158     * @param contents Contents of a Configuration to load.
159     * @throws InvalidConfigurationException Thrown if the specified string is
160     *                                       invalid.
161     */
162    public abstract void loadFromString(String contents) throws InvalidConfigurationException;
163
164    /**
165     * Compiles the header for this FileConfiguration and returns the
166     * result.
167     *
168     * <p>This will use the header from {@link #options()} -&gt; {@link
169     * FileConfigurationOptions#header()}, respecting the rules of {@link
170     * FileConfigurationOptions#copyHeader()} if set.
171     *
172     * @return Compiled header
173     */
174    protected abstract String buildHeader();
175
176    @Override
177    public FileConfigurationOptions options() {
178        if (this.options == null) {
179            this.options = new FileConfigurationOptions(this);
180        }
181
182        return (FileConfigurationOptions) this.options;
183    }
184
185}