## 在 bukkit 配置框架下读取并保存配置文件
### 新版本的操作
自 https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/commits/3e2dd2bc120754ea4db193e878050d0eb31a6894
之后的版本,bukkit 添加了注释相关的支持。如您的插件只服务于新版本,您可以跳过此篇。
### 旧版本的方法
SnakeYaml 自 1.28 开始添加了对 comments 的解析。在 Node 实例中可通过读取 `inLineComments` `blockComments` 与 `endComments` 来
存储注释。可通过以下代码启用
```java// 启用读取注释
loaderOptions.setProcessComments(true);
// 启用保存注释
dumperOptions.setProcessComments(true);
复制代码```
随后可以实现 `bukkit` 中的 `FileConfiguration` 类,在 `loadFromString` 中将 `Node` 中的 `comments` 保存,
并在 `saveToString` 时还原。
### 存在的问题
#### 保存序列时注释与习惯的位置不一致
```yaml
# AAA
id: 101
# BBB
test:
# ccc
- 0复制代码
```
```yaml
# AAA
id: 101
# BBB
test:
- # ccc
0
复制代码```
### 实现
本代码以 WTFPL 授权,请随意修改并使用
```java
/*
* DO WHAT THE ** YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) 2004 HongYunCloud
*
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE ** YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE ** YOU WANT TO.
*/
package io.github.hongyuncloud.config;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
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.constructor.SafeConstructor;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.*;
import org.yaml.snakeyaml.reader.UnicodeReader;
import org.yaml.snakeyaml.representer.Representer;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
// HongYunCloud Yaml Configuration
// version 1.0-SNAPSHOT
//
// required snakeyaml 1.28+
// it provide on 1.13+, so you need shadow yourself version before 1.13
// implementation("org.yaml:snakeyaml:2.0")
public final class HcYamlConfiguration extends FileConfiguration {
private static final /* @Nonnull */ Logger logger = Logger.getLogger(HcYamlConfiguration.class.getName());
private final /* @Nonnull */ HcYamlConstructor constructor;
private final /* @Nonnull */ HcYamlRepresenter representer;
private final /* @Nonnull */ Yaml yaml;
private final /* @Nonnull */ Map commentMap;
public HcYamlConfiguration() {
this(null);
}
public HcYamlConfiguration(final /* @Nullable */ Configuration defaults) {
super(defaults);
final DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
dumperOptions.setProcessComments(true);
final LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE);
loaderOptions.setCodePointLimit(Integer.MAX_VALUE);
loaderOptions.setProcessComments(true);
constructor = new HcYamlConstructor(loaderOptions);
representer = new HcYamlRepresenter(dumperOptions);
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
yaml = new Yaml(constructor, representer, dumperOptions, loaderOptions);
commentMap = new LinkedHashMap
}
public static /* @Nonnull */ HcYamlConfiguration loadConfiguration(final /* @Nonnull */ File file) {
final HcYamlConfiguration config = new HcYamlConfiguration();
try {
config.load(file);
} catch (final FileNotFoundException e) {
// ignored
} catch (final IOException | InvalidConfigurationException e) {
logger.log(Level.SEVERE, "Cannot load " + file, e);
}
return config;
}
public static /* @Nonnull */ HcYamlConfiguration loadConfiguration(final /* @Nonnull */ Reader reader) {
final HcYamlConfiguration config = new HcYamlConfiguration();
try {
config.load(reader);
} catch (final IOException | InvalidConfigurationException e) {
logger.log(Level.SEVERE, "Cannot load configuration from stream", e);
}
return config;
}
@Override
public void loadFromString(final /* @Nonnull */ String contents) throws InvalidConfigurationException {
final MappingNode node;
try (final Reader reader = new UnicodeReader(new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)))) {
final Node rawNode = yaml.compose(reader);
try {
node = (MappingNode) rawNode;
} catch (final ClassCastException e) {
throw new InvalidConfigurationException("Top level is not a Map.");
}
} catch (final YAMLException | IOException | ClassCastException e) {
throw new InvalidConfigurationException(e);
}
this.map.clear();
this.commentMap.clear();
if (node != null) {
fromNodeTree(node, this);
}
}
/* [url=home.php?mod=space&uid=520054]@override[/url] */
protected /* @Nonnull */ String buildHeader() {
return "";
}
@Override
public /* @Nonnull */ String saveToString() {
try(final StringWriter writer = new StringWriter()){
final MappingNode node = toNodeTree(this);
if (node.getValue().isEmpty()) {
node.setFlowStyle(DumperOptions.FlowStyle.FLOW);
}
yaml.serialize(node, writer);
return writer.toString();
}catch (final IOException e){
throw new UncheckedIOException(e);
}
}
private void fromNodeTree(final /* @Nonnull */ MappingNode input, final /* @Nonnull */ ConfigurationSection section) {
constructor.flattenMapping(input);
commentMap.put(section.getCurrentPath() + options().pathSeparator() + "v", createCommentStorage(input));
for (final NodeTuple nodeTuple : input.getValue()) {
final Node key = nodeTuple.getKeyNode();
final String keyString = String.valueOf(constructor.construct(key));
final String path = section.getCurrentPath() + options().pathSeparator() + keyString;
commentMap.put(path + options().pathSeparator() + "k", createCommentStorage(key));
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 {
final String valuePath = path + options().pathSeparator() + "v";
section.set(keyString, constructor.construct(value));
if (value instanceof SequenceNode) {
final SequenceNode sequenceValue = (SequenceNode) value;
for (int i = 0; i
commentMap.put(
valuePath + options().pathSeparator() + i,
createCommentStorage(sequenceValue.getValue().get(i))
);
}
}
commentMap.put(valuePath, createCommentStorage(value));
}
}
}
private boolean hasSerializedTypeKey(final /* @Nonnull */ MappingNode node) {
for (final NodeTuple nodeTuple : node.getValue()) {
final Node keyNode = nodeTuple.getKeyNode();
if (!(keyNode instanceof ScalarNode)) continue;
final String key = ((ScalarNode) keyNode).getValue();
if (key.equals(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) {
return true;
}
}
return false;
}
private /* @Nonnull */ MappingNode toNodeTree(final /* @Nonnull */ ConfigurationSection section) {
final List nodeTuples = new ArrayList
for (final Map.Entry entry : section.getValues(false).entrySet()) {
final Node key = representer.represent(entry.getKey());
final String path = section.getCurrentPath() + options().pathSeparator() + entry.getKey();
applyCommentStorage(key, commentMap.get(path + options.pathSeparator() + "k"));
final Node value;
if (entry.getValue() instanceof ConfigurationSection) {
value = toNodeTree((ConfigurationSection) entry.getValue());
} else {
final String valuePath = path + options().pathSeparator() + "v";
value = representer.represent(entry.getValue());
applyCommentStorage(value, commentMap.get(valuePath));
if(value instanceof SequenceNode) {
final List sequenceValue = ((SequenceNode) value).getValue();
for (int i = 0; i
applyCommentStorage(sequenceValue.get(i), commentMap.get(valuePath + options().pathSeparator() + i));
}
}
}
nodeTuples.add(new NodeTuple(key, value));
}
final MappingNode result = new MappingNode(Tag.MAP, nodeTuples, DumperOptions.FlowStyle.BLOCK);
applyCommentStorage(result, commentMap.get(section.getCurrentPath() + options().pathSeparator() + "v"));
return result;
}
private /* @Nonnull */ HcCommentStorage createCommentStorage(final /* @Nullable */ Node node) {
if(node == null){
return new HcCommentStorage(Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
}
return new HcCommentStorage(node.getInLineComments(), node.getBlockComments(), node.getEndComments());
}
private void applyCommentStorage(final /* @Nullable */ Node node, final /* @Nullable */ HcCommentStorage storage) {
if (node == null || storage == null) {
return;
}
node.setInLineComments(storage.inLineComments);
node.setBlockComments(storage.blockComments);
node.setEndComments(storage.endComments);
}
private static final class HcCommentStorage {
private final /* @Nonnull */ List inLineComments;
private final /* @Nonnull */ List blockComments;
private final /* @Nonnull */ List endComments;
private HcCommentStorage(
final /* @Nonnull */ List inLineComments,
final /* @Nonnull */ List blockComments,
final /* @Nonnull */ List endComments
) {
this.inLineComments = inLineComments == null ? Collections.emptyList() : new ArrayList
this.blockComments = blockComments == null ? Collections.emptyList() : new ArrayList
this.endComments = endComments == null ? Collections.emptyList() : new ArrayList
}
}
private static final class HcYamlConstructor extends SafeConstructor {
public HcYamlConstructor(final /* @Nonnull */ LoaderOptions loaderOptions) {
super(loaderOptions);
this.yamlConstructors.put(Tag.MAP, new HcYamlConstructor.ConstructCustomObject());
}
@Override
public void flattenMapping(final /* @Nonnull */ MappingNode node) {
super.flattenMapping(node);
}
public /* @Nonnull */ Object construct(final /* @Nonnull */ Node node) {
return constructObject(node);
}
private class ConstructCustomObject extends ConstructYamlMap {
@Override
public final /* @Nonnull */ Object construct(final /* @Nonnull */ Node node) {
if (node.isTwoStepsConstruction()) {
throw new YAMLException("Unexpected referential mapping structure. Node: " + node);
}
Map raw = (Map) super.construct(node);
if (raw.containsKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) {
Map typed = new LinkedHashMap
for (Map.Entry entry : raw.entrySet()) {
typed.put(entry.getKey().toString(), entry.getValue());
}
try {
return ConfigurationSerialization.deserializeObject(typed);
} catch (IllegalArgumentException ex) {
throw new YAMLException("Could not deserialize object", ex);
}
}
return raw;
}
@Override
public void construct2ndStep(final /* @Nonnull */ Node node, final /* @Nonnull */ Object object) {
throw new YAMLException("Unexpected referential mapping structure. Node: " + node);
}
}
}
private static final class HcYamlRepresenter extends Representer {
public HcYamlRepresenter(final /* @Nonnull */ DumperOptions options) {
super(options);
this.multiRepresenters.put(ConfigurationSection.class, new RepresentConfigurationSection());
this.multiRepresenters.put(ConfigurationSerializable.class, new RepresentConfigurationSerializable());
this.multiRepresenters.remove(Enum.class);
}
// SPIGOT-6949: Used by configuration sections that are nested within lists or maps.
private final class RepresentConfigurationSection extends RepresentMap {
@Override
public /* @Nonnull */ Node representData(final /* @Nonnull */ Object data) {
return super.representData(((ConfigurationSection) data).getValues(false));
}
}
private final class RepresentConfigurationSerializable extends RepresentMap {
@Override
public /* @Nonnull */ Node representData(final /* @Nonnull */ Object data) {
final ConfigurationSerializable serializable = (ConfigurationSerializable) data;
final Map values = new LinkedHashMap
values.put(ConfigurationSerialization.SERIALIZED_TYPE_KEY, ConfigurationSerialization.getAlias(serializable.getClass()));
values.putAll(serializable.serialize());
return super.representData(values);
}
}
}
}
复制代码
```
### 新版本的操作
自 https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/commits/3e2dd2bc120754ea4db193e878050d0eb31a6894
之后的版本,bukkit 添加了注释相关的支持。如您的插件只服务于新版本,您可以跳过此篇。
### 旧版本的方法
SnakeYaml 自 1.28 开始添加了对 comments 的解析。在 Node 实例中可通过读取 `inLineComments` `blockComments` 与 `endComments` 来
存储注释。可通过以下代码启用
```java// 启用读取注释
loaderOptions.setProcessComments(true);
// 启用保存注释
dumperOptions.setProcessComments(true);
复制代码```
随后可以实现 `bukkit` 中的 `FileConfiguration` 类,在 `loadFromString` 中将 `Node` 中的 `comments` 保存,
并在 `saveToString` 时还原。
### 存在的问题
#### 保存序列时注释与习惯的位置不一致
```yaml
# AAA
id: 101
# BBB
test:
# ccc
- 0复制代码
```
```yaml
# AAA
id: 101
# BBB
test:
- # ccc
0
复制代码```
### 实现
本代码以 WTFPL 授权,请随意修改并使用
```java
/*
* DO WHAT THE ** YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) 2004 HongYunCloud
*
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE ** YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE ** YOU WANT TO.
*/
package io.github.hongyuncloud.config;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
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.constructor.SafeConstructor;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.*;
import org.yaml.snakeyaml.reader.UnicodeReader;
import org.yaml.snakeyaml.representer.Representer;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
// HongYunCloud Yaml Configuration
// version 1.0-SNAPSHOT
//
// required snakeyaml 1.28+
// it provide on 1.13+, so you need shadow yourself version before 1.13
// implementation("org.yaml:snakeyaml:2.0")
public final class HcYamlConfiguration extends FileConfiguration {
private static final /* @Nonnull */ Logger logger = Logger.getLogger(HcYamlConfiguration.class.getName());
private final /* @Nonnull */ HcYamlConstructor constructor;
private final /* @Nonnull */ HcYamlRepresenter representer;
private final /* @Nonnull */ Yaml yaml;
private final /* @Nonnull */ Map commentMap;
public HcYamlConfiguration() {
this(null);
}
public HcYamlConfiguration(final /* @Nullable */ Configuration defaults) {
super(defaults);
final DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
dumperOptions.setProcessComments(true);
final LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE);
loaderOptions.setCodePointLimit(Integer.MAX_VALUE);
loaderOptions.setProcessComments(true);
constructor = new HcYamlConstructor(loaderOptions);
representer = new HcYamlRepresenter(dumperOptions);
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
yaml = new Yaml(constructor, representer, dumperOptions, loaderOptions);
commentMap = new LinkedHashMap
}
public static /* @Nonnull */ HcYamlConfiguration loadConfiguration(final /* @Nonnull */ File file) {
final HcYamlConfiguration config = new HcYamlConfiguration();
try {
config.load(file);
} catch (final FileNotFoundException e) {
// ignored
} catch (final IOException | InvalidConfigurationException e) {
logger.log(Level.SEVERE, "Cannot load " + file, e);
}
return config;
}
public static /* @Nonnull */ HcYamlConfiguration loadConfiguration(final /* @Nonnull */ Reader reader) {
final HcYamlConfiguration config = new HcYamlConfiguration();
try {
config.load(reader);
} catch (final IOException | InvalidConfigurationException e) {
logger.log(Level.SEVERE, "Cannot load configuration from stream", e);
}
return config;
}
@Override
public void loadFromString(final /* @Nonnull */ String contents) throws InvalidConfigurationException {
final MappingNode node;
try (final Reader reader = new UnicodeReader(new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)))) {
final Node rawNode = yaml.compose(reader);
try {
node = (MappingNode) rawNode;
} catch (final ClassCastException e) {
throw new InvalidConfigurationException("Top level is not a Map.");
}
} catch (final YAMLException | IOException | ClassCastException e) {
throw new InvalidConfigurationException(e);
}
this.map.clear();
this.commentMap.clear();
if (node != null) {
fromNodeTree(node, this);
}
}
/* [url=home.php?mod=space&uid=520054]@override[/url] */
protected /* @Nonnull */ String buildHeader() {
return "";
}
@Override
public /* @Nonnull */ String saveToString() {
try(final StringWriter writer = new StringWriter()){
final MappingNode node = toNodeTree(this);
if (node.getValue().isEmpty()) {
node.setFlowStyle(DumperOptions.FlowStyle.FLOW);
}
yaml.serialize(node, writer);
return writer.toString();
}catch (final IOException e){
throw new UncheckedIOException(e);
}
}
private void fromNodeTree(final /* @Nonnull */ MappingNode input, final /* @Nonnull */ ConfigurationSection section) {
constructor.flattenMapping(input);
commentMap.put(section.getCurrentPath() + options().pathSeparator() + "v", createCommentStorage(input));
for (final NodeTuple nodeTuple : input.getValue()) {
final Node key = nodeTuple.getKeyNode();
final String keyString = String.valueOf(constructor.construct(key));
final String path = section.getCurrentPath() + options().pathSeparator() + keyString;
commentMap.put(path + options().pathSeparator() + "k", createCommentStorage(key));
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 {
final String valuePath = path + options().pathSeparator() + "v";
section.set(keyString, constructor.construct(value));
if (value instanceof SequenceNode) {
final SequenceNode sequenceValue = (SequenceNode) value;
for (int i = 0; i
commentMap.put(
valuePath + options().pathSeparator() + i,
createCommentStorage(sequenceValue.getValue().get(i))
);
}
}
commentMap.put(valuePath, createCommentStorage(value));
}
}
}
private boolean hasSerializedTypeKey(final /* @Nonnull */ MappingNode node) {
for (final NodeTuple nodeTuple : node.getValue()) {
final Node keyNode = nodeTuple.getKeyNode();
if (!(keyNode instanceof ScalarNode)) continue;
final String key = ((ScalarNode) keyNode).getValue();
if (key.equals(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) {
return true;
}
}
return false;
}
private /* @Nonnull */ MappingNode toNodeTree(final /* @Nonnull */ ConfigurationSection section) {
final List nodeTuples = new ArrayList
for (final Map.Entry entry : section.getValues(false).entrySet()) {
final Node key = representer.represent(entry.getKey());
final String path = section.getCurrentPath() + options().pathSeparator() + entry.getKey();
applyCommentStorage(key, commentMap.get(path + options.pathSeparator() + "k"));
final Node value;
if (entry.getValue() instanceof ConfigurationSection) {
value = toNodeTree((ConfigurationSection) entry.getValue());
} else {
final String valuePath = path + options().pathSeparator() + "v";
value = representer.represent(entry.getValue());
applyCommentStorage(value, commentMap.get(valuePath));
if(value instanceof SequenceNode) {
final List sequenceValue = ((SequenceNode) value).getValue();
for (int i = 0; i
applyCommentStorage(sequenceValue.get(i), commentMap.get(valuePath + options().pathSeparator() + i));
}
}
}
nodeTuples.add(new NodeTuple(key, value));
}
final MappingNode result = new MappingNode(Tag.MAP, nodeTuples, DumperOptions.FlowStyle.BLOCK);
applyCommentStorage(result, commentMap.get(section.getCurrentPath() + options().pathSeparator() + "v"));
return result;
}
private /* @Nonnull */ HcCommentStorage createCommentStorage(final /* @Nullable */ Node node) {
if(node == null){
return new HcCommentStorage(Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
}
return new HcCommentStorage(node.getInLineComments(), node.getBlockComments(), node.getEndComments());
}
private void applyCommentStorage(final /* @Nullable */ Node node, final /* @Nullable */ HcCommentStorage storage) {
if (node == null || storage == null) {
return;
}
node.setInLineComments(storage.inLineComments);
node.setBlockComments(storage.blockComments);
node.setEndComments(storage.endComments);
}
private static final class HcCommentStorage {
private final /* @Nonnull */ List inLineComments;
private final /* @Nonnull */ List blockComments;
private final /* @Nonnull */ List endComments;
private HcCommentStorage(
final /* @Nonnull */ List inLineComments,
final /* @Nonnull */ List blockComments,
final /* @Nonnull */ List endComments
) {
this.inLineComments = inLineComments == null ? Collections.emptyList() : new ArrayList
this.blockComments = blockComments == null ? Collections.emptyList() : new ArrayList
this.endComments = endComments == null ? Collections.emptyList() : new ArrayList
}
}
private static final class HcYamlConstructor extends SafeConstructor {
public HcYamlConstructor(final /* @Nonnull */ LoaderOptions loaderOptions) {
super(loaderOptions);
this.yamlConstructors.put(Tag.MAP, new HcYamlConstructor.ConstructCustomObject());
}
@Override
public void flattenMapping(final /* @Nonnull */ MappingNode node) {
super.flattenMapping(node);
}
public /* @Nonnull */ Object construct(final /* @Nonnull */ Node node) {
return constructObject(node);
}
private class ConstructCustomObject extends ConstructYamlMap {
@Override
public final /* @Nonnull */ Object construct(final /* @Nonnull */ Node node) {
if (node.isTwoStepsConstruction()) {
throw new YAMLException("Unexpected referential mapping structure. Node: " + node);
}
Map raw = (Map) super.construct(node);
if (raw.containsKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) {
Map typed = new LinkedHashMap
for (Map.Entry entry : raw.entrySet()) {
typed.put(entry.getKey().toString(), entry.getValue());
}
try {
return ConfigurationSerialization.deserializeObject(typed);
} catch (IllegalArgumentException ex) {
throw new YAMLException("Could not deserialize object", ex);
}
}
return raw;
}
@Override
public void construct2ndStep(final /* @Nonnull */ Node node, final /* @Nonnull */ Object object) {
throw new YAMLException("Unexpected referential mapping structure. Node: " + node);
}
}
}
private static final class HcYamlRepresenter extends Representer {
public HcYamlRepresenter(final /* @Nonnull */ DumperOptions options) {
super(options);
this.multiRepresenters.put(ConfigurationSection.class, new RepresentConfigurationSection());
this.multiRepresenters.put(ConfigurationSerializable.class, new RepresentConfigurationSerializable());
this.multiRepresenters.remove(Enum.class);
}
// SPIGOT-6949: Used by configuration sections that are nested within lists or maps.
private final class RepresentConfigurationSection extends RepresentMap {
@Override
public /* @Nonnull */ Node representData(final /* @Nonnull */ Object data) {
return super.representData(((ConfigurationSection) data).getValues(false));
}
}
private final class RepresentConfigurationSerializable extends RepresentMap {
@Override
public /* @Nonnull */ Node representData(final /* @Nonnull */ Object data) {
final ConfigurationSerializable serializable = (ConfigurationSerializable) data;
final Map values = new LinkedHashMap
values.put(ConfigurationSerialization.SERIALIZED_TYPE_KEY, ConfigurationSerialization.getAlias(serializable.getClass()));
values.putAll(serializable.serialize());
return super.representData(values);
}
}
}
}
复制代码
```