mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-15 21:01:24 +01:00
SPIGOT-3247: Comment support for YAML files
By: Wolf2323 <gabrielpatrikurban@gmail.com>
This commit is contained in:
parent
e61faa55b8
commit
ed8a152b3a
@ -996,4 +996,66 @@ public interface ConfigurationSection {
|
||||
* @throws IllegalArgumentException Thrown if path is null.
|
||||
*/
|
||||
public void addDefault(@NotNull String path, @Nullable Object value);
|
||||
|
||||
/**
|
||||
* Gets the requested comment list by path.
|
||||
* <p>
|
||||
* If no comments exist, an empty list will be returned. A null entry
|
||||
* represents an empty line and an empty String represents an empty comment
|
||||
* line.
|
||||
*
|
||||
* @param path Path of the comments to get.
|
||||
* @return A unmodifiable list of the requested comments, every entry
|
||||
* represents one line.
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getComments(@NotNull String path);
|
||||
|
||||
/**
|
||||
* Gets the requested inline comment list by path.
|
||||
* <p>
|
||||
* If no comments exist, an empty list will be returned. A null entry
|
||||
* represents an empty line and an empty String represents an empty comment
|
||||
* line.
|
||||
*
|
||||
* @param path Path of the comments to get.
|
||||
* @return A unmodifiable list of the requested comments, every entry
|
||||
* represents one line.
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getInlineComments(@NotNull String path);
|
||||
|
||||
/**
|
||||
* Sets the comment list at the specified path.
|
||||
* <p>
|
||||
* If value is null, the comments will be removed. A null entry is an empty
|
||||
* line and an empty String entry is an empty comment line. If the path does
|
||||
* not exist, no comments will be set. Any existing comments will be
|
||||
* replaced, regardless of what the new comments are.
|
||||
* <p>
|
||||
* Some implementations may have limitations on what persists. See their
|
||||
* individual javadocs for details.
|
||||
*
|
||||
* @param path Path of the comments to set.
|
||||
* @param comments New comments to set at the path, every entry represents
|
||||
* one line.
|
||||
*/
|
||||
public void setComments(@NotNull String path, @Nullable List<String> comments);
|
||||
|
||||
/**
|
||||
* Sets the inline comment list at the specified path.
|
||||
* <p>
|
||||
* If value is null, the comments will be removed. A null entry is an empty
|
||||
* line and an empty String entry is an empty comment line. If the path does
|
||||
* not exist, no comment will be set. Any existing comments will be
|
||||
* replaced, regardless of what the new comments are.
|
||||
* <p>
|
||||
* Some implementations may have limitations on what persists. See their
|
||||
* individual javadocs for details.
|
||||
*
|
||||
* @param path Path of the comments to set.
|
||||
* @param comments New comments to set at the path, every entry represents
|
||||
* one line.
|
||||
*/
|
||||
public void setInlineComments(@NotNull String path, @Nullable List<String> comments);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.bukkit.configuration;
|
||||
|
||||
import static org.bukkit.util.NumberConversions.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
@ -22,7 +23,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
* A type of {@link ConfigurationSection} that is stored in memory.
|
||||
*/
|
||||
public class MemorySection implements ConfigurationSection {
|
||||
protected final Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
protected final Map<String, SectionPathData> map = new LinkedHashMap<String, SectionPathData>();
|
||||
private final Configuration root;
|
||||
private final ConfigurationSection parent;
|
||||
private final String path;
|
||||
@ -217,7 +218,12 @@ public class MemorySection implements ConfigurationSection {
|
||||
if (value == null) {
|
||||
map.remove(key);
|
||||
} else {
|
||||
map.put(key, value);
|
||||
SectionPathData entry = map.get(key);
|
||||
if (entry == null) {
|
||||
map.put(key, new SectionPathData(value));
|
||||
} else {
|
||||
entry.setData(value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
section.set(key, value);
|
||||
@ -263,8 +269,8 @@ public class MemorySection implements ConfigurationSection {
|
||||
|
||||
String key = path.substring(i2);
|
||||
if (section == this) {
|
||||
Object result = map.get(key);
|
||||
return (result == null) ? def : result;
|
||||
SectionPathData result = map.get(key);
|
||||
return (result == null) ? def : result.getData();
|
||||
}
|
||||
return section.get(key, def);
|
||||
}
|
||||
@ -296,7 +302,7 @@ public class MemorySection implements ConfigurationSection {
|
||||
String key = path.substring(i2);
|
||||
if (section == this) {
|
||||
ConfigurationSection result = new MemorySection(this, key);
|
||||
map.put(key, result);
|
||||
map.put(key, new SectionPathData(result));
|
||||
return result;
|
||||
}
|
||||
return section.createSection(key);
|
||||
@ -860,11 +866,11 @@ public class MemorySection implements ConfigurationSection {
|
||||
if (section instanceof MemorySection) {
|
||||
MemorySection sec = (MemorySection) section;
|
||||
|
||||
for (Map.Entry<String, Object> entry : sec.map.entrySet()) {
|
||||
for (Map.Entry<String, SectionPathData> entry : sec.map.entrySet()) {
|
||||
output.add(createPath(section, entry.getKey(), this));
|
||||
|
||||
if ((deep) && (entry.getValue() instanceof ConfigurationSection)) {
|
||||
ConfigurationSection subsection = (ConfigurationSection) entry.getValue();
|
||||
if ((deep) && (entry.getValue().getData() instanceof ConfigurationSection)) {
|
||||
ConfigurationSection subsection = (ConfigurationSection) entry.getValue().getData();
|
||||
mapChildrenKeys(output, subsection, deep);
|
||||
}
|
||||
}
|
||||
@ -881,17 +887,17 @@ public class MemorySection implements ConfigurationSection {
|
||||
if (section instanceof MemorySection) {
|
||||
MemorySection sec = (MemorySection) section;
|
||||
|
||||
for (Map.Entry<String, Object> entry : sec.map.entrySet()) {
|
||||
for (Map.Entry<String, SectionPathData> entry : sec.map.entrySet()) {
|
||||
// Because of the copyDefaults call potentially copying out of order, we must remove and then add in our saved order
|
||||
// This means that default values we haven't set end up getting placed first
|
||||
// See SPIGOT-4558 for an example using spigot.yml - watch subsections move around to default order
|
||||
String childPath = createPath(section, entry.getKey(), this);
|
||||
output.remove(childPath);
|
||||
output.put(childPath, entry.getValue());
|
||||
output.put(childPath, entry.getValue().getData());
|
||||
|
||||
if (entry.getValue() instanceof ConfigurationSection) {
|
||||
if (entry.getValue().getData() instanceof ConfigurationSection) {
|
||||
if (deep) {
|
||||
mapChildrenValues(output, (ConfigurationSection) entry.getValue(), deep);
|
||||
mapChildrenValues(output, (ConfigurationSection) entry.getValue().getData(), deep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -942,14 +948,11 @@ public class MemorySection implements ConfigurationSection {
|
||||
char separator = root.options().pathSeparator();
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (section != null) {
|
||||
for (ConfigurationSection parent = section; (parent != null) && (parent != relativeTo); parent = parent.getParent()) {
|
||||
if (builder.length() > 0) {
|
||||
builder.insert(0, separator);
|
||||
}
|
||||
|
||||
builder.insert(0, parent.getName());
|
||||
for (ConfigurationSection parent = section; (parent != null) && (parent != relativeTo); parent = parent.getParent()) {
|
||||
if (builder.length() > 0) {
|
||||
builder.insert(0, separator);
|
||||
}
|
||||
builder.insert(0, parent.getName());
|
||||
}
|
||||
|
||||
if ((key != null) && (key.length() > 0)) {
|
||||
@ -963,6 +966,69 @@ public class MemorySection implements ConfigurationSection {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<String> getComments(@NotNull final String path) {
|
||||
final SectionPathData pathData = getSectionPathData(path);
|
||||
return pathData == null ? Collections.emptyList() : pathData.getComments();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public List<String> getInlineComments(@NotNull final String path) {
|
||||
final SectionPathData pathData = getSectionPathData(path);
|
||||
return pathData == null ? Collections.emptyList() : pathData.getInlineComments();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComments(@NotNull final String path, @Nullable final List<String> comments) {
|
||||
final SectionPathData pathData = getSectionPathData(path);
|
||||
if (pathData != null) {
|
||||
pathData.setComments(comments);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInlineComments(@NotNull final String path, @Nullable final List<String> comments) {
|
||||
final SectionPathData pathData = getSectionPathData(path);
|
||||
if (pathData != null) {
|
||||
pathData.setInlineComments(comments);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SectionPathData getSectionPathData(@NotNull String path) {
|
||||
Validate.notNull(path, "Path cannot be null");
|
||||
|
||||
Configuration root = getRoot();
|
||||
if (root == null) {
|
||||
throw new IllegalStateException("Cannot access section without a root");
|
||||
}
|
||||
|
||||
final char separator = root.options().pathSeparator();
|
||||
// i1 is the leading (higher) index
|
||||
// i2 is the trailing (lower) index
|
||||
int i1 = -1, i2;
|
||||
ConfigurationSection section = this;
|
||||
while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) {
|
||||
section = section.getConfigurationSection(path.substring(i2, i1));
|
||||
if (section == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String key = path.substring(i2);
|
||||
if (section == this) {
|
||||
SectionPathData entry = map.get(key);
|
||||
if (entry != null) {
|
||||
return entry;
|
||||
}
|
||||
} else if (section instanceof MemorySection) {
|
||||
return ((MemorySection) section).getSectionPathData(key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Configuration root = getRoot();
|
||||
|
@ -0,0 +1,81 @@
|
||||
package org.bukkit.configuration;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
final class SectionPathData {
|
||||
|
||||
private Object data;
|
||||
private List<String> comments;
|
||||
private List<String> inlineComments;
|
||||
|
||||
public SectionPathData(@Nullable Object data) {
|
||||
this.data = data;
|
||||
comments = Collections.emptyList();
|
||||
inlineComments = Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(@Nullable final Object data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* If no comments exist, an empty list will be returned. A null entry in the
|
||||
* list represents an empty line and an empty String represents an empty
|
||||
* comment line.
|
||||
*
|
||||
* @return A unmodifiable list of the requested comments, every entry
|
||||
* represents one line.
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the comments on a {@link ConfigurationSection} entry.
|
||||
*
|
||||
* A null entry in the List is an empty line and an empty String entry is an
|
||||
* empty comment line. Any existing comments will be replaced, regardless of
|
||||
* what the new comments are.
|
||||
*
|
||||
* @param comments New comments to set every entry represents one line.
|
||||
*/
|
||||
public void setComments(@Nullable final List<String> comments) {
|
||||
this.comments = (comments == null) ? Collections.emptyList() : Collections.unmodifiableList(comments);
|
||||
}
|
||||
|
||||
/**
|
||||
* If no comments exist, an empty list will be returned. A null entry in the
|
||||
* list represents an empty line and an empty String represents an empty
|
||||
* comment line.
|
||||
*
|
||||
* @return A unmodifiable list of the requested comments, every entry
|
||||
* represents one line.
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getInlineComments() {
|
||||
return inlineComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the comments on a {@link ConfigurationSection} entry.
|
||||
*
|
||||
* A null entry in the List is an empty line and an empty String entry is an
|
||||
* empty comment line. Any existing comments will be replaced, regardless of
|
||||
* what the new comments are.
|
||||
*
|
||||
* @param inlineComments New comments to set every entry represents one
|
||||
* line.
|
||||
*/
|
||||
public void setInlineComments(@Nullable final List<String> inlineComments) {
|
||||
this.inlineComments = (inlineComments == null) ? Collections.emptyList() : Collections.unmodifiableList(inlineComments);
|
||||
}
|
||||
}
|
@ -202,17 +202,17 @@ public abstract class FileConfiguration extends MemoryConfiguration {
|
||||
public abstract void loadFromString(@NotNull String contents) throws InvalidConfigurationException;
|
||||
|
||||
/**
|
||||
* Compiles the header for this {@link FileConfiguration} and returns the
|
||||
* result.
|
||||
* <p>
|
||||
* This will use the header from {@link #options()} -> {@link
|
||||
* FileConfigurationOptions#header()}, respecting the rules of {@link
|
||||
* FileConfigurationOptions#copyHeader()} if set.
|
||||
* @return empty string
|
||||
*
|
||||
* @return Compiled header
|
||||
* @deprecated This method only exists for backwards compatibility. It will
|
||||
* do nothing and should not be used! Please use
|
||||
* {@link FileConfigurationOptions#getHeader()} instead.
|
||||
*/
|
||||
@NotNull
|
||||
protected abstract String buildHeader();
|
||||
@Deprecated
|
||||
protected String buildHeader() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
|
@ -1,5 +1,8 @@
|
||||
package org.bukkit.configuration.file;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.bukkit.configuration.MemoryConfiguration;
|
||||
import org.bukkit.configuration.MemoryConfigurationOptions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -10,8 +13,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
* FileConfiguration}
|
||||
*/
|
||||
public class FileConfigurationOptions extends MemoryConfigurationOptions {
|
||||
private String header = null;
|
||||
private boolean copyHeader = true;
|
||||
private List<String> header = Collections.emptyList();
|
||||
private List<String> footer = Collections.emptyList();
|
||||
private boolean parseComments = true;
|
||||
|
||||
protected FileConfigurationOptions(@NotNull MemoryConfiguration configuration) {
|
||||
super(configuration);
|
||||
@ -46,16 +50,32 @@ public class FileConfigurationOptions extends MemoryConfigurationOptions {
|
||||
* automatically be applied, but you may include one if you wish for extra
|
||||
* spacing.
|
||||
* <p>
|
||||
* Null is a valid value which will indicate that no header is to be
|
||||
* applied. The default value is null.
|
||||
* If no comments exist, an empty list will be returned. A null entry
|
||||
* represents an empty line and an empty String represents an empty comment
|
||||
* line.
|
||||
*
|
||||
* @return Header
|
||||
* @return Unmodifiable header, every entry represents one line.
|
||||
*/
|
||||
@Nullable
|
||||
public String header() {
|
||||
@NotNull
|
||||
public List<String> getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The string header.
|
||||
*
|
||||
* @deprecated use getHeader() instead.
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated
|
||||
public String header() {
|
||||
StringBuilder stringHeader = new StringBuilder();
|
||||
for (String line : header) {
|
||||
stringHeader.append(line == null ? "\n" : line + "\n");
|
||||
}
|
||||
return stringHeader.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the header that will be applied to the top of the saved output.
|
||||
* <p>
|
||||
@ -65,63 +85,119 @@ public class FileConfigurationOptions extends MemoryConfigurationOptions {
|
||||
* automatically be applied, but you may include one if you wish for extra
|
||||
* spacing.
|
||||
* <p>
|
||||
* Null is a valid value which will indicate that no header is to be
|
||||
* applied.
|
||||
* If no comments exist, an empty list will be returned. A null entry
|
||||
* represents an empty line and an empty String represents an empty comment
|
||||
* line.
|
||||
*
|
||||
* @param value New header
|
||||
* @param value New header, every entry represents one line.
|
||||
* @return This object, for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public FileConfigurationOptions header(@Nullable String value) {
|
||||
this.header = value;
|
||||
public FileConfigurationOptions setHeader(@Nullable List<String> value) {
|
||||
this.header = (value == null) ? Collections.emptyList() : Collections.unmodifiableList(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not the header should be copied from a default source.
|
||||
* <p>
|
||||
* If this is true, if a default {@link FileConfiguration} is passed to
|
||||
* {@link
|
||||
* FileConfiguration#setDefaults(org.bukkit.configuration.Configuration)}
|
||||
* then upon saving it will use the header from that config, instead of
|
||||
* the one provided here.
|
||||
* <p>
|
||||
* If no default is set on the configuration, or the default is not of
|
||||
* type FileConfiguration, or that config has no header ({@link #header()}
|
||||
* returns null) then the header specified in this configuration will be
|
||||
* used.
|
||||
* <p>
|
||||
* Defaults to true.
|
||||
* @param value The string header.
|
||||
* @return This object, for chaining.
|
||||
*
|
||||
* @return Whether or not to copy the header
|
||||
* @deprecated use setHeader() instead
|
||||
*/
|
||||
public boolean copyHeader() {
|
||||
return copyHeader;
|
||||
@NotNull
|
||||
@Deprecated
|
||||
public FileConfigurationOptions header(@Nullable String value) {
|
||||
this.header = (value == null) ? Collections.emptyList() : Collections.unmodifiableList(Arrays.asList(value.split("\\n")));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not the header should be copied from a default source.
|
||||
* Gets the footer that will be applied to the bottom of the saved output.
|
||||
* <p>
|
||||
* If this is true, if a default {@link FileConfiguration} is passed to
|
||||
* {@link
|
||||
* FileConfiguration#setDefaults(org.bukkit.configuration.Configuration)}
|
||||
* then upon saving it will use the header from that config, instead of
|
||||
* the one provided here.
|
||||
* This footer will be commented out and applied directly at the bottom of
|
||||
* the generated output of the {@link FileConfiguration}. It is not required
|
||||
* to include a newline at the beginning of the footer as it will
|
||||
* automatically be applied, but you may include one if you wish for extra
|
||||
* spacing.
|
||||
* <p>
|
||||
* If no default is set on the configuration, or the default is not of
|
||||
* type FileConfiguration, or that config has no header ({@link #header()}
|
||||
* returns null) then the header specified in this configuration will be
|
||||
* used.
|
||||
* <p>
|
||||
* Defaults to true.
|
||||
* If no comments exist, an empty list will be returned. A null entry
|
||||
* represents an empty line and an empty String represents an empty comment
|
||||
* line.
|
||||
*
|
||||
* @param value Whether or not to copy the header
|
||||
* @return Unmodifiable footer, every entry represents one line.
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getFooter() {
|
||||
return footer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the footer that will be applied to the bottom of the saved output.
|
||||
* <p>
|
||||
* This footer will be commented out and applied directly at the bottom of
|
||||
* the generated output of the {@link FileConfiguration}. It is not required
|
||||
* to include a newline at the beginning of the footer as it will
|
||||
* automatically be applied, but you may include one if you wish for extra
|
||||
* spacing.
|
||||
* <p>
|
||||
* If no comments exist, an empty list will be returned. A null entry
|
||||
* represents an empty line and an empty String represents an empty comment
|
||||
* line.
|
||||
*
|
||||
* @param value New footer, every entry represents one line.
|
||||
* @return This object, for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public FileConfigurationOptions copyHeader(boolean value) {
|
||||
copyHeader = value;
|
||||
public FileConfigurationOptions setFooter(@Nullable List<String> value) {
|
||||
this.footer = (value == null) ? Collections.emptyList() : Collections.unmodifiableList(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not comments should be loaded and saved.
|
||||
* <p>
|
||||
* Defaults to true.
|
||||
*
|
||||
* @return Whether or not comments are parsed.
|
||||
*/
|
||||
public boolean parseComments() {
|
||||
return parseComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not comments should be loaded and saved.
|
||||
* <p>
|
||||
* Defaults to true.
|
||||
*
|
||||
* @param value Whether or not comments are parsed.
|
||||
* @return This object, for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public MemoryConfigurationOptions parseComments(boolean value) {
|
||||
parseComments = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether or not comments are parsed.
|
||||
*
|
||||
* @deprecated Call {@link #parseComments()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean copyHeader() {
|
||||
return parseComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value Should comments be parsed.
|
||||
* @return This object, for chaining
|
||||
*
|
||||
* @deprecated Call {@link #parseComments(boolean)} instead.
|
||||
*/
|
||||
@NotNull
|
||||
@Deprecated
|
||||
public FileConfigurationOptions copyHeader(boolean value) {
|
||||
parseComments = value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,14 @@
|
||||
package org.bukkit.configuration.file;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang.Validate;
|
||||
@ -11,148 +16,231 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.comments.CommentLine;
|
||||
import org.yaml.snakeyaml.comments.CommentType;
|
||||
import org.yaml.snakeyaml.error.YAMLException;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
import org.yaml.snakeyaml.nodes.AnchorNode;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
import org.yaml.snakeyaml.nodes.ScalarNode;
|
||||
import org.yaml.snakeyaml.nodes.SequenceNode;
|
||||
import org.yaml.snakeyaml.nodes.Tag;
|
||||
import org.yaml.snakeyaml.reader.UnicodeReader;
|
||||
|
||||
/**
|
||||
* An implementation of {@link Configuration} which saves all files in Yaml.
|
||||
* Note that this implementation is not synchronized.
|
||||
*/
|
||||
public class YamlConfiguration extends FileConfiguration {
|
||||
protected static final String COMMENT_PREFIX = "# ";
|
||||
protected static final String BLANK_CONFIG = "{}\n";
|
||||
private final DumperOptions yamlOptions = new DumperOptions();
|
||||
private final LoaderOptions loaderOptions = new LoaderOptions();
|
||||
private final Representer yamlRepresenter = new YamlRepresenter();
|
||||
private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions, loaderOptions);
|
||||
private final DumperOptions yamlDumperOptions;
|
||||
private final LoaderOptions yamlLoaderOptions;
|
||||
private final YamlConstructor constructor;
|
||||
private final YamlRepresenter representer;
|
||||
private final Yaml yaml;
|
||||
|
||||
public YamlConfiguration() {
|
||||
constructor = new YamlConstructor();
|
||||
representer = new YamlRepresenter();
|
||||
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
|
||||
yamlDumperOptions = new DumperOptions();
|
||||
yamlDumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
yamlLoaderOptions = new LoaderOptions();
|
||||
yamlLoaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE); // SPIGOT-5881: Not ideal, but was default pre SnakeYAML 1.26
|
||||
|
||||
yaml = new Yaml(constructor, representer, yamlDumperOptions, yamlLoaderOptions);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String saveToString() {
|
||||
yamlOptions.setIndent(options().indent());
|
||||
yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
yamlDumperOptions.setIndent(options().indent());
|
||||
yamlDumperOptions.setProcessComments(options().parseComments());
|
||||
|
||||
String header = buildHeader();
|
||||
String dump = yaml.dump(getValues(false));
|
||||
MappingNode node = toNodeTree(this);
|
||||
|
||||
if (dump.equals(BLANK_CONFIG)) {
|
||||
dump = "";
|
||||
node.setBlockComments(getCommentLines(saveHeader(options().getHeader()), CommentType.BLOCK));
|
||||
node.setEndComments(getCommentLines(options().getFooter(), CommentType.BLOCK));
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
if (node.getEndComments().isEmpty() && node.getEndComments().isEmpty() && node.getValue().isEmpty()) {
|
||||
writer.write("");
|
||||
} else {
|
||||
if (node.getValue().isEmpty()) {
|
||||
node.setFlowStyle(DumperOptions.FlowStyle.FLOW);
|
||||
}
|
||||
yaml.serialize(node, writer);
|
||||
}
|
||||
|
||||
return header + dump;
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromString(@NotNull String contents) throws InvalidConfigurationException {
|
||||
Validate.notNull(contents, "Contents cannot be null");
|
||||
Validate.notNull(contents, "String cannot be null");
|
||||
yamlLoaderOptions.setProcessComments(options().parseComments());
|
||||
|
||||
Map<?, ?> input;
|
||||
try {
|
||||
loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE); // SPIGOT-5881: Not ideal, but was default pre SnakeYAML 1.26
|
||||
input = (Map<?, ?>) yaml.load(contents);
|
||||
} catch (YAMLException e) {
|
||||
MappingNode node;
|
||||
try (Reader reader = new UnicodeReader(new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)))) {
|
||||
node = (MappingNode) yaml.compose(reader);
|
||||
} catch (YAMLException | IOException e) {
|
||||
throw new InvalidConfigurationException(e);
|
||||
} catch (ClassCastException e) {
|
||||
throw new InvalidConfigurationException("Top level is not a Map.");
|
||||
}
|
||||
|
||||
String header = parseHeader(contents);
|
||||
if (header.length() > 0) {
|
||||
options().header(header);
|
||||
}
|
||||
|
||||
this.map.clear();
|
||||
|
||||
if (input != null) {
|
||||
convertMapsToSections(input, this);
|
||||
if (node != null) {
|
||||
adjustNodeComments(node);
|
||||
options().setHeader(loadHeader(getCommentLines(node.getBlockComments())));
|
||||
options().setFooter(getCommentLines(node.getEndComments()));
|
||||
fromNodeTree(node, this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void convertMapsToSections(@NotNull Map<?, ?> input, @NotNull ConfigurationSection section) {
|
||||
for (Map.Entry<?, ?> entry : input.entrySet()) {
|
||||
String key = entry.getKey().toString();
|
||||
Object value = entry.getValue();
|
||||
/**
|
||||
* This method splits the header on the last empty line, and sets the
|
||||
* comments below this line as comments for the first key on the map object.
|
||||
*
|
||||
* @param node The root node of the yaml object
|
||||
*/
|
||||
private void adjustNodeComments(final MappingNode node) {
|
||||
if (node.getBlockComments() == null && !node.getValue().isEmpty()) {
|
||||
Node firstNode = node.getValue().get(0).getKeyNode();
|
||||
List<CommentLine> lines = firstNode.getBlockComments();
|
||||
if (lines != null) {
|
||||
int index = -1;
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
if (lines.get(i).getCommentType() == CommentType.BLANK_LINE) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
if (index != -1) {
|
||||
node.setBlockComments(lines.subList(0, index + 1));
|
||||
firstNode.setBlockComments(lines.subList(index + 1, lines.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value instanceof Map) {
|
||||
convertMapsToSections((Map<?, ?>) value, section.createSection(key));
|
||||
protected void fromNodeTree(@NotNull MappingNode input, @NotNull ConfigurationSection section) {
|
||||
for (NodeTuple nodeTuple : input.getValue()) {
|
||||
ScalarNode key = (ScalarNode) nodeTuple.getKeyNode();
|
||||
String keyString = key.getValue();
|
||||
Node value = nodeTuple.getValueNode();
|
||||
|
||||
while (value instanceof AnchorNode) {
|
||||
value = ((AnchorNode) value).getRealNode();
|
||||
}
|
||||
|
||||
if (value instanceof MappingNode && !hasSerializedTypeKey((MappingNode) value)) {
|
||||
fromNodeTree((MappingNode) value, section.createSection(keyString));
|
||||
} else {
|
||||
section.set(key, value);
|
||||
section.set(keyString, constructor.construct(value));
|
||||
}
|
||||
|
||||
section.setComments(keyString, getCommentLines(key.getBlockComments()));
|
||||
if (value instanceof MappingNode || value instanceof SequenceNode) {
|
||||
section.setInlineComments(keyString, getCommentLines(key.getInLineComments()));
|
||||
} else {
|
||||
section.setInlineComments(keyString, getCommentLines(value.getInLineComments()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected String parseHeader(@NotNull String input) {
|
||||
String[] lines = input.split("\r?\n", -1);
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean readingHeader = true;
|
||||
boolean foundHeader = false;
|
||||
|
||||
for (int i = 0; (i < lines.length) && (readingHeader); i++) {
|
||||
String line = lines[i];
|
||||
|
||||
if (line.startsWith(COMMENT_PREFIX)) {
|
||||
if (i > 0) {
|
||||
result.append("\n");
|
||||
}
|
||||
|
||||
if (line.length() > COMMENT_PREFIX.length()) {
|
||||
result.append(line.substring(COMMENT_PREFIX.length()));
|
||||
}
|
||||
|
||||
foundHeader = true;
|
||||
} else if ((foundHeader) && (line.length() == 0)) {
|
||||
result.append("\n");
|
||||
} else if (foundHeader) {
|
||||
readingHeader = false;
|
||||
private boolean hasSerializedTypeKey(MappingNode node) {
|
||||
for (NodeTuple nodeTuple : node.getValue()) {
|
||||
String key = ((ScalarNode) nodeTuple.getKeyNode()).getValue();
|
||||
if (key.equals(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String buildHeader() {
|
||||
String header = options().header();
|
||||
private MappingNode toNodeTree(@NotNull ConfigurationSection section) {
|
||||
List<NodeTuple> nodeTuples = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : section.getValues(false).entrySet()) {
|
||||
ScalarNode key = (ScalarNode) representer.represent(entry.getKey());
|
||||
Node value;
|
||||
if (entry.getValue() instanceof ConfigurationSection) {
|
||||
value = toNodeTree((ConfigurationSection) entry.getValue());
|
||||
} else {
|
||||
value = representer.represent(entry.getValue());
|
||||
}
|
||||
key.setBlockComments(getCommentLines(section.getComments(entry.getKey()), CommentType.BLOCK));
|
||||
if (value instanceof MappingNode || value instanceof SequenceNode) {
|
||||
key.setInLineComments(getCommentLines(section.getInlineComments(entry.getKey()), CommentType.IN_LINE));
|
||||
} else {
|
||||
value.setInLineComments(getCommentLines(section.getInlineComments(entry.getKey()), CommentType.IN_LINE));
|
||||
}
|
||||
|
||||
if (options().copyHeader()) {
|
||||
Configuration def = getDefaults();
|
||||
nodeTuples.add(new NodeTuple(key, value));
|
||||
}
|
||||
|
||||
if ((def != null) && (def instanceof FileConfiguration)) {
|
||||
FileConfiguration filedefaults = (FileConfiguration) def;
|
||||
String defaultsHeader = filedefaults.buildHeader();
|
||||
return new MappingNode(Tag.MAP, nodeTuples, DumperOptions.FlowStyle.BLOCK);
|
||||
}
|
||||
|
||||
if ((defaultsHeader != null) && (defaultsHeader.length() > 0)) {
|
||||
return defaultsHeader;
|
||||
private List<String> getCommentLines(List<CommentLine> comments) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
if (comments != null) {
|
||||
for (CommentLine comment : comments) {
|
||||
if (comment.getCommentType() == CommentType.BLANK_LINE) {
|
||||
lines.add(null);
|
||||
} else {
|
||||
lines.add(comment.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
if (header == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String[] lines = header.split("\r?\n", -1);
|
||||
boolean startedHeader = false;
|
||||
|
||||
for (int i = lines.length - 1; i >= 0; i--) {
|
||||
builder.insert(0, "\n");
|
||||
|
||||
if ((startedHeader) || (lines[i].length() != 0)) {
|
||||
builder.insert(0, lines[i]);
|
||||
builder.insert(0, COMMENT_PREFIX);
|
||||
startedHeader = true;
|
||||
private List<CommentLine> getCommentLines(List<String> comments, CommentType commentType) {
|
||||
List<CommentLine> lines = new ArrayList<CommentLine>();
|
||||
for (String comment : comments) {
|
||||
if (comment == null) {
|
||||
lines.add(new CommentLine(null, null, "", CommentType.BLANK_LINE));
|
||||
} else {
|
||||
lines.add(new CommentLine(null, null, comment, commentType));
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
/**
|
||||
* Removes the empty line at the end of the header that separates the header
|
||||
* from further comments.
|
||||
*
|
||||
* @param header The list of heading comments
|
||||
* @return The modified list
|
||||
*/
|
||||
private List<String> loadHeader(List<String> header) {
|
||||
ArrayList<String> list = new ArrayList<String>(header);
|
||||
if (list.size() != 0) {
|
||||
list.remove(list.size() - 1);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the empty line at the end of the header that separates the header
|
||||
* from further comments.
|
||||
*
|
||||
* @param header The list of heading comments
|
||||
* @return The modified list
|
||||
*/
|
||||
private List<String> saveHeader(List<String> header) {
|
||||
ArrayList<String> list = new ArrayList<String>(header);
|
||||
if (list.size() != 0) {
|
||||
list.add(null);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.bukkit.configuration.file;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -37,6 +38,14 @@ public class YamlConfigurationOptions extends FileConfigurationOptions {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public YamlConfigurationOptions setHeader(@Nullable List<String> value) {
|
||||
super.setHeader(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@Deprecated
|
||||
public YamlConfigurationOptions header(@Nullable String value) {
|
||||
super.header(value);
|
||||
return this;
|
||||
@ -44,6 +53,21 @@ public class YamlConfigurationOptions extends FileConfigurationOptions {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public YamlConfigurationOptions setFooter(@Nullable List<String> value) {
|
||||
super.setFooter(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public YamlConfigurationOptions parseComments(boolean value) {
|
||||
super.parseComments(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@Deprecated
|
||||
public YamlConfigurationOptions copyHeader(boolean value) {
|
||||
super.copyHeader(value);
|
||||
return this;
|
||||
|
@ -16,6 +16,11 @@ public class YamlConstructor extends SafeConstructor {
|
||||
this.yamlConstructors.put(Tag.MAP, new ConstructCustomObject());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Object construct(@NotNull Node node) {
|
||||
return constructObject(node);
|
||||
}
|
||||
|
||||
private class ConstructCustomObject extends ConstructYamlMap {
|
||||
|
||||
@Nullable
|
||||
|
@ -2,7 +2,6 @@ package org.bukkit.configuration.file;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -12,22 +11,12 @@ import org.yaml.snakeyaml.representer.Representer;
|
||||
public class YamlRepresenter extends Representer {
|
||||
|
||||
public YamlRepresenter() {
|
||||
this.multiRepresenters.put(ConfigurationSection.class, new RepresentConfigurationSection());
|
||||
this.multiRepresenters.put(ConfigurationSerializable.class, new RepresentConfigurationSerializable());
|
||||
// SPIGOT-6234: We could just switch YamlConstructor to extend Constructor rather than SafeConstructor, however there is a very small risk of issues with plugins treating config as untrusted input
|
||||
// So instead we will just allow future plugins to have their enums extend ConfigurationSerializable
|
||||
this.multiRepresenters.remove(Enum.class);
|
||||
}
|
||||
|
||||
private class RepresentConfigurationSection extends RepresentMap {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Node representData(@NotNull Object data) {
|
||||
return super.representData(((ConfigurationSection) data).getValues(false));
|
||||
}
|
||||
}
|
||||
|
||||
private class RepresentConfigurationSerializable extends RepresentMap {
|
||||
|
||||
@NotNull
|
||||
|
@ -4,6 +4,8 @@ import static org.junit.Assert.*;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.bukkit.configuration.MemoryConfigurationTest;
|
||||
import org.junit.Rule;
|
||||
@ -19,9 +21,17 @@ public abstract class FileConfigurationTest extends MemoryConfigurationTest {
|
||||
|
||||
public abstract String getTestValuesString();
|
||||
|
||||
public abstract String getTestHeaderInput();
|
||||
public abstract List<String> getTestCommentInput();
|
||||
|
||||
public abstract String getTestHeaderResult();
|
||||
public abstract String getTestCommentResult();
|
||||
|
||||
public abstract List<String> getTestHeaderComments();
|
||||
|
||||
public abstract String getTestHeaderCommentsResult();
|
||||
|
||||
public abstract List<String> getTestKeyComments();
|
||||
|
||||
public abstract String getTestHeaderKeyCommentResult();
|
||||
|
||||
@Test
|
||||
public void testSave_File() throws Exception {
|
||||
@ -127,69 +137,6 @@ public abstract class FileConfigurationTest extends MemoryConfigurationTest {
|
||||
assertEquals(saved, config.saveToString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveToStringWithHeader() {
|
||||
FileConfiguration config = getConfig();
|
||||
config.options().header(getTestHeaderInput());
|
||||
|
||||
for (Map.Entry<String, Object> entry : getTestValues().entrySet()) {
|
||||
config.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
String result = config.saveToString();
|
||||
String expected = getTestHeaderResult() + "\n" + getTestValuesString();
|
||||
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHeader() throws Exception {
|
||||
FileConfiguration config = getConfig();
|
||||
Map<String, Object> values = getTestValues();
|
||||
String saved = getTestValuesString();
|
||||
String header = getTestHeaderResult();
|
||||
String expected = getTestHeaderInput();
|
||||
|
||||
config.loadFromString(header + "\n" + saved);
|
||||
|
||||
assertEquals(expected, config.options().header());
|
||||
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
assertEquals(entry.getValue(), config.get(entry.getKey()));
|
||||
}
|
||||
|
||||
assertEquals(values.keySet(), config.getKeys(true));
|
||||
assertEquals(header + "\n" + saved, config.saveToString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyHeader() throws Exception {
|
||||
FileConfiguration config = getConfig();
|
||||
FileConfiguration defaults = getConfig();
|
||||
Map<String, Object> values = getTestValues();
|
||||
String saved = getTestValuesString();
|
||||
String header = getTestHeaderResult();
|
||||
String expected = getTestHeaderInput();
|
||||
|
||||
defaults.loadFromString(header);
|
||||
config.loadFromString(saved);
|
||||
config.setDefaults(defaults);
|
||||
|
||||
assertNull(config.options().header());
|
||||
assertEquals(expected, defaults.options().header());
|
||||
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
assertEquals(entry.getValue(), config.get(entry.getKey()));
|
||||
}
|
||||
|
||||
assertEquals(values.keySet(), config.getKeys(true));
|
||||
assertEquals(header + "\n" + saved, config.saveToString());
|
||||
|
||||
config = getConfig();
|
||||
config.loadFromString(getTestHeaderResult() + saved);
|
||||
assertEquals(getTestHeaderResult() + saved, config.saveToString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReloadEmptyConfig() throws Exception {
|
||||
FileConfiguration config = getConfig();
|
||||
@ -271,4 +218,178 @@ public abstract class FileConfigurationTest extends MemoryConfigurationTest {
|
||||
assertFalse(config.contains("test"));
|
||||
assertFalse(config.getBoolean("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveWithComments() {
|
||||
FileConfiguration config = getConfig();
|
||||
config.options().parseComments(true);
|
||||
|
||||
for (Map.Entry<String, Object> entry : getTestValues().entrySet()) {
|
||||
config.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
String key = getTestValues().keySet().iterator().next();
|
||||
config.setComments(key, getTestCommentInput());
|
||||
|
||||
String result = config.saveToString();
|
||||
String expected = getTestCommentResult() + "\n" + getTestValuesString();
|
||||
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveWithoutComments() {
|
||||
FileConfiguration config = getConfig();
|
||||
config.options().parseComments(false);
|
||||
|
||||
for (Map.Entry<String, Object> entry : getTestValues().entrySet()) {
|
||||
config.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
String key = getTestValues().keySet().iterator().next();
|
||||
config.setComments(key, getTestCommentInput());
|
||||
|
||||
String result = config.saveToString();
|
||||
String expected = getTestValuesString();
|
||||
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWithComments() throws Exception {
|
||||
FileConfiguration config = getConfig();
|
||||
Map<String, Object> values = getTestValues();
|
||||
String saved = getTestValuesString();
|
||||
String comments = getTestCommentResult();
|
||||
|
||||
config.options().parseComments(true);
|
||||
config.loadFromString(comments + "\n" + saved);
|
||||
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
assertEquals(entry.getValue(), config.get(entry.getKey()));
|
||||
}
|
||||
|
||||
assertEquals(values.keySet(), config.getKeys(true));
|
||||
assertEquals(comments + "\n" + saved, config.saveToString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWithoutComments() throws Exception {
|
||||
FileConfiguration config = getConfig();
|
||||
Map<String, Object> values = getTestValues();
|
||||
String saved = getTestValuesString();
|
||||
String comments = getTestCommentResult();
|
||||
|
||||
config.options().parseComments(false);
|
||||
config.loadFromString(comments + "\n" + saved);
|
||||
config.options().parseComments(true);
|
||||
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
assertEquals(entry.getValue(), config.get(entry.getKey()));
|
||||
}
|
||||
|
||||
assertEquals(values.keySet(), config.getKeys(true));
|
||||
assertEquals(saved, config.saveToString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveWithCommentsHeader() {
|
||||
FileConfiguration config = getConfig();
|
||||
config.options().parseComments(true);
|
||||
|
||||
for (Map.Entry<String, Object> entry : getTestValues().entrySet()) {
|
||||
config.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
String key = getTestValues().keySet().iterator().next();
|
||||
config.options().setHeader(getTestHeaderComments());
|
||||
config.setComments(key, getTestKeyComments());
|
||||
|
||||
String result = config.saveToString();
|
||||
String expected = getTestHeaderKeyCommentResult() + getTestValuesString();
|
||||
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWithCommentsHeader() throws Exception {
|
||||
FileConfiguration config = getConfig();
|
||||
Map<String, Object> values = getTestValues();
|
||||
String saved = getTestValuesString();
|
||||
String comments = getTestHeaderKeyCommentResult();
|
||||
|
||||
config.options().parseComments(true);
|
||||
config.loadFromString(comments + saved);
|
||||
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
assertEquals(entry.getValue(), config.get(entry.getKey()));
|
||||
}
|
||||
|
||||
String key = getTestValues().keySet().iterator().next();
|
||||
assertEquals(getTestHeaderComments(), config.options().getHeader());
|
||||
assertEquals(getTestKeyComments(), config.getComments(key));
|
||||
|
||||
assertEquals(values.keySet(), config.getKeys(true));
|
||||
assertEquals(comments + saved, config.saveToString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveWithCommentsFooter() {
|
||||
FileConfiguration config = getConfig();
|
||||
config.options().parseComments(true);
|
||||
|
||||
for (Map.Entry<String, Object> entry : getTestValues().entrySet()) {
|
||||
config.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
config.options().setFooter(getTestHeaderComments());
|
||||
|
||||
String result = config.saveToString();
|
||||
String expected = getTestValuesString() + getTestHeaderCommentsResult();
|
||||
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWithCommentsFooter() throws Exception {
|
||||
FileConfiguration config = getConfig();
|
||||
Map<String, Object> values = getTestValues();
|
||||
String saved = getTestValuesString();
|
||||
String comments = getTestHeaderCommentsResult();
|
||||
|
||||
config.options().parseComments(true);
|
||||
config.loadFromString(saved + comments);
|
||||
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
assertEquals(entry.getValue(), config.get(entry.getKey()));
|
||||
}
|
||||
|
||||
assertEquals(getTestHeaderComments(), config.options().getFooter());
|
||||
|
||||
assertEquals(values.keySet(), config.getKeys(true));
|
||||
assertEquals(saved + comments, config.saveToString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWithCommentsInline() throws Exception {
|
||||
FileConfiguration config = getConfig();
|
||||
|
||||
config.options().parseComments(true);
|
||||
config.loadFromString("key1: value1\nkey2: value2 # Test inline\nkey3: value3");
|
||||
|
||||
assertEquals(Arrays.asList(" Test inline"), config.getInlineComments("key2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveWithCommentsInline() {
|
||||
FileConfiguration config = getConfig();
|
||||
|
||||
config.options().parseComments(true);
|
||||
config.set("key1", "value1");
|
||||
config.set("key2", "value2");
|
||||
config.set("key3", "value3");
|
||||
config.setInlineComments("key2", Arrays.asList(" Test inline"));
|
||||
|
||||
String result = config.saveToString();
|
||||
String expected = "key1: value1\nkey2: value2 # Test inline\nkey3: value3\n";
|
||||
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package org.bukkit.configuration.file;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.junit.Test;
|
||||
|
||||
public class YamlConfigurationTest extends FileConfigurationTest {
|
||||
@ -11,13 +14,43 @@ public class YamlConfigurationTest extends FileConfigurationTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTestHeaderInput() {
|
||||
return "This is a sample\nheader.\n\nNewline above should be commented.\n\n";
|
||||
public List<String> getTestCommentInput() {
|
||||
List<String> comments = new ArrayList<>();
|
||||
comments.add(" This is a sample");
|
||||
comments.add(" header.");
|
||||
comments.add(" Newline above should be commented.");
|
||||
comments.add("");
|
||||
comments.add("");
|
||||
comments.add(null);
|
||||
comments.add(null);
|
||||
comments.add(" Comment of first Key");
|
||||
comments.add(" and a second line.");
|
||||
return comments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTestHeaderResult() {
|
||||
return "# This is a sample\n# header.\n# \n# Newline above should be commented.\n\n";
|
||||
public String getTestCommentResult() {
|
||||
return "# This is a sample\n# header.\n# Newline above should be commented.\n#\n#\n\n\n# Comment of first Key\n# and a second line.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTestHeaderComments() {
|
||||
return Arrays.asList(" Header", " Second Line");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTestHeaderCommentsResult() {
|
||||
return "# Header\n# Second Line\n";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTestKeyComments() {
|
||||
return Arrays.asList(" First key Comment", " Second Line");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTestHeaderKeyCommentResult() {
|
||||
return "# Header\n# Second Line\n\n# First key Comment\n# Second Line\n";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user