313 lines
9.3 KiB
Java
Raw Normal View History

2015-07-05 20:51:34 +10:00
package com.intellectualcrafters.configuration.file;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
2015-07-06 01:44:10 +10:00
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
2015-07-05 20:51:34 +10:00
import java.util.Map;
2015-07-31 00:25:16 +10:00
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.representer.Representer;
import com.intellectualcrafters.configuration.Configuration;
import com.intellectualcrafters.configuration.ConfigurationSection;
import com.intellectualcrafters.configuration.InvalidConfigurationException;
import com.intellectualcrafters.plot.PS;
2015-07-05 20:51:34 +10:00
/**
* An implementation of {@link Configuration} which saves all files in Yaml.
* Note that this implementation is not synchronized.
*/
2015-09-11 20:09:22 +10:00
public class YamlConfiguration extends FileConfiguration
{
2015-07-05 20:51:34 +10:00
protected static final String COMMENT_PREFIX = "# ";
protected static final String BLANK_CONFIG = "{}\n";
private final DumperOptions yamlOptions = new DumperOptions();
private final Representer yamlRepresenter = new YamlRepresenter();
private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions);
@Override
2015-09-11 20:09:22 +10:00
public String saveToString()
{
2015-07-05 20:51:34 +10:00
yamlOptions.setIndent(options().indent());
yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
yamlOptions.setAllowUnicode(SYSTEM_UTF);
yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
2015-09-11 20:09:22 +10:00
final String header = buildHeader();
2015-07-05 20:51:34 +10:00
String dump = yaml.dump(getValues(false));
2015-09-11 20:09:22 +10:00
if (dump.equals(BLANK_CONFIG))
{
2015-07-05 20:51:34 +10:00
dump = "";
}
return header + dump;
}
@Override
2015-09-11 20:09:22 +10:00
public void loadFromString(final String contents) throws InvalidConfigurationException
{
if (contents == null) { throw new NullPointerException("Contents cannot be null"); }
2015-07-05 20:51:34 +10:00
Map<?, ?> input;
2015-09-11 20:09:22 +10:00
try
{
2015-07-05 20:51:34 +10:00
input = (Map<?, ?>) yaml.load(contents);
2015-09-11 20:09:22 +10:00
}
catch (final YAMLException e)
{
2015-07-05 20:51:34 +10:00
throw new InvalidConfigurationException(e);
2015-09-11 20:09:22 +10:00
}
catch (final ClassCastException e)
{
2015-07-05 20:51:34 +10:00
throw new InvalidConfigurationException("Top level is not a Map.");
}
2015-09-11 20:09:22 +10:00
final String header = parseHeader(contents);
if (header.length() > 0)
{
2015-07-05 20:51:34 +10:00
options().header(header);
}
2015-09-11 20:09:22 +10:00
if (input != null)
{
2015-07-05 20:51:34 +10:00
convertMapsToSections(input, this);
}
}
2015-09-11 20:09:22 +10:00
protected void convertMapsToSections(final Map<?, ?> input, final ConfigurationSection section)
{
for (final Map.Entry<?, ?> entry : input.entrySet())
{
final String key = entry.getKey().toString();
final Object value = entry.getValue();
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
if (value instanceof Map)
{
2015-07-05 20:51:34 +10:00
convertMapsToSections((Map<?, ?>) value, section.createSection(key));
2015-09-11 20:09:22 +10:00
}
else
{
2015-07-05 20:51:34 +10:00
section.set(key, value);
}
}
}
2015-09-11 20:09:22 +10:00
protected String parseHeader(final String input)
{
final String[] lines = input.split("\r?\n", -1);
final StringBuilder result = new StringBuilder();
2015-07-05 20:51:34 +10:00
boolean readingHeader = true;
boolean foundHeader = false;
2015-09-11 20:09:22 +10:00
for (int i = 0; (i < lines.length) && (readingHeader); i++)
{
final String line = lines[i];
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
if (line.startsWith(COMMENT_PREFIX))
{
if (i > 0)
{
2015-07-05 20:51:34 +10:00
result.append("\n");
}
2015-09-11 20:09:22 +10:00
if (line.length() > COMMENT_PREFIX.length())
{
2015-07-05 20:51:34 +10:00
result.append(line.substring(COMMENT_PREFIX.length()));
}
foundHeader = true;
2015-09-11 20:09:22 +10:00
}
else if ((foundHeader) && (line.length() == 0))
{
2015-07-05 20:51:34 +10:00
result.append("\n");
2015-09-11 20:09:22 +10:00
}
else if (foundHeader)
{
2015-07-05 20:51:34 +10:00
readingHeader = false;
}
}
return result.toString();
}
@Override
2015-09-11 20:09:22 +10:00
protected String buildHeader()
{
final String header = options().header();
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
if (options().copyHeader())
{
final Configuration def = getDefaults();
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
if ((def != null) && (def instanceof FileConfiguration))
{
final FileConfiguration filedefaults = (FileConfiguration) def;
final String defaultsHeader = filedefaults.buildHeader();
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
if ((defaultsHeader != null) && (defaultsHeader.length() > 0)) { return defaultsHeader; }
2015-07-05 20:51:34 +10:00
}
}
2015-09-11 20:09:22 +10:00
if (header == null) { return ""; }
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
final StringBuilder builder = new StringBuilder();
final String[] lines = header.split("\r?\n", -1);
2015-07-05 20:51:34 +10:00
boolean startedHeader = false;
2015-09-11 20:09:22 +10:00
for (int i = lines.length - 1; i >= 0; i--)
{
2015-07-05 20:51:34 +10:00
builder.insert(0, "\n");
2015-09-11 20:09:22 +10:00
if ((startedHeader) || (lines[i].length() != 0))
{
2015-07-05 20:51:34 +10:00
builder.insert(0, lines[i]);
builder.insert(0, COMMENT_PREFIX);
startedHeader = true;
}
}
return builder.toString();
}
@Override
2015-09-11 20:09:22 +10:00
public YamlConfigurationOptions options()
{
if (options == null)
{
2015-07-05 20:51:34 +10:00
options = new YamlConfigurationOptions(this);
}
return (YamlConfigurationOptions) options;
}
/**
* Creates a new {@link YamlConfiguration}, loading from the given file.
* <p>
* Any errors loading the Configuration will be logged and then ignored.
* If the specified input is not a valid config, a blank config will be
* returned.
* <p>
* The encoding used may follow the system dependent default.
*
* @param file Input file
* @return Resulting configuration
* @throws IllegalArgumentException Thrown if file is null
*/
2015-09-11 20:09:22 +10:00
public static YamlConfiguration loadConfiguration(final File file)
{
if (file == null) { throw new NullPointerException("File cannot be null"); }
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
final YamlConfiguration config = new YamlConfiguration();
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
try
{
2015-07-05 20:51:34 +10:00
config.load(file);
2015-09-11 20:09:22 +10:00
}
catch (final Exception ex)
{
try
{
file.getAbsolutePath();
2015-07-06 01:44:10 +10:00
File dest = new File(file.getAbsolutePath() + "_broken");
int i = 0;
2015-09-11 20:09:22 +10:00
while (dest.exists())
{
2015-07-06 01:44:10 +10:00
dest = new File(file.getAbsolutePath() + "_broken_" + i++);
}
2015-09-11 20:09:22 +10:00
Files.copy(file.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
2015-07-31 00:25:16 +10:00
PS.debug("&dCould not read: &7" + file);
PS.debug("&drenamed to: &7" + dest.getName());
PS.debug("&c============ Full stacktrace ============");
2015-07-06 01:44:10 +10:00
ex.printStackTrace();
2015-07-31 00:25:16 +10:00
PS.debug("&c=========================================");
2015-09-11 20:09:22 +10:00
}
catch (final IOException e)
{
2015-07-06 01:44:10 +10:00
e.printStackTrace();
}
2015-07-05 20:51:34 +10:00
}
return config;
}
/**
* Creates a new {@link YamlConfiguration}, loading from the given stream.
* <p>
* Any errors loading the Configuration will be logged and then ignored.
* If the specified input is not a valid config, a blank config will be
* returned.
*
* @param stream Input stream
* @return Resulting configuration
* @throws IllegalArgumentException Thrown if stream is null
* @deprecated does not properly consider encoding
* @see #load(InputStream)
* @see #loadConfiguration(Reader)
*/
@Deprecated
2015-09-11 20:09:22 +10:00
public static YamlConfiguration loadConfiguration(final InputStream stream)
{
if (stream == null) { throw new NullPointerException("Stream cannot be null"); }
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
final YamlConfiguration config = new YamlConfiguration();
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
try
{
2015-07-05 20:51:34 +10:00
config.load(stream);
2015-09-11 20:09:22 +10:00
}
catch (final IOException ex)
{
2015-07-31 00:25:16 +10:00
PS.debug("Cannot load configuration from stream");
2015-07-05 20:51:34 +10:00
ex.printStackTrace();
2015-09-11 20:09:22 +10:00
}
catch (final InvalidConfigurationException ex)
{
2015-07-05 20:51:34 +10:00
ex.printStackTrace();
2015-07-31 00:25:16 +10:00
PS.debug("Cannot load configuration from stream");
2015-07-05 20:51:34 +10:00
}
return config;
}
/**
* Creates a new {@link YamlConfiguration}, loading from the given reader.
* <p>
* Any errors loading the Configuration will be logged and then ignored.
* If the specified input is not a valid config, a blank config will be
* returned.
*
* @param reader input
* @return resulting configuration
* @throws IllegalArgumentException Thrown if stream is null
*/
2015-09-11 20:09:22 +10:00
public static YamlConfiguration loadConfiguration(final Reader reader)
{
if (reader == null) { throw new NullPointerException("Reader cannot be null"); }
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
final YamlConfiguration config = new YamlConfiguration();
2015-07-05 20:51:34 +10:00
2015-09-11 20:09:22 +10:00
try
{
2015-07-05 20:51:34 +10:00
config.load(reader);
2015-09-11 20:09:22 +10:00
}
catch (final IOException ex)
{
2015-07-31 00:25:16 +10:00
PS.debug("Cannot load configuration from stream");
2015-07-05 20:51:34 +10:00
ex.printStackTrace();
2015-09-11 20:09:22 +10:00
}
catch (final InvalidConfigurationException ex)
{
2015-07-31 00:25:16 +10:00
PS.debug("Cannot load configuration from stream");
2015-07-05 20:51:34 +10:00
ex.printStackTrace();
}
return config;
}
}