001/* 002 * Configurate 003 * Copyright (C) zml and Configurate contributors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.spongepowered.configurate.yaml; 018 019import org.checkerframework.checker.nullness.qual.Nullable; 020import org.spongepowered.configurate.CommentedConfigurationNode; 021import org.spongepowered.configurate.ConfigurationNode; 022import org.spongepowered.configurate.ConfigurationOptions; 023import org.spongepowered.configurate.loader.AbstractConfigurationLoader; 024import org.spongepowered.configurate.loader.CommentHandler; 025import org.spongepowered.configurate.loader.CommentHandlers; 026import org.spongepowered.configurate.util.UnmodifiableCollections; 027import org.yaml.snakeyaml.DumperOptions; 028import org.yaml.snakeyaml.Yaml; 029 030import java.io.BufferedReader; 031import java.io.Writer; 032import java.math.BigInteger; 033import java.sql.Timestamp; 034import java.util.Date; 035import java.util.Set; 036 037/** 038 * A loader for YAML-formatted configurations, using the SnakeYAML library for 039 * parsing and generation. 040 * 041 * @since 4.0.0 042 */ 043public final class YamlConfigurationLoader extends AbstractConfigurationLoader<CommentedConfigurationNode> { 044 045 /** 046 * YAML native types from <a href="https://yaml.org/type/">YAML 1.1 Global tags</a>. 047 * 048 * <p>using SnakeYaml representation: https://bitbucket.org/asomov/snakeyaml/wiki/Documentation#markdown-header-yaml-tags-and-java-types 049 */ 050 private static final Set<Class<?>> NATIVE_TYPES = UnmodifiableCollections.toSet( 051 Boolean.class, Integer.class, Long.class, BigInteger.class, Double.class, // numeric 052 byte[].class, String.class, Date.class, java.sql.Date.class, Timestamp.class); // complex types 053 054 /** 055 * Creates a new {@link YamlConfigurationLoader} builder. 056 * 057 * @return a new builder 058 * @since 4.0.0 059 */ 060 public static Builder builder() { 061 return new Builder(); 062 } 063 064 /** 065 * Builds a {@link YamlConfigurationLoader}. 066 * 067 * @since 4.0.0 068 */ 069 public static final class Builder extends AbstractConfigurationLoader.Builder<Builder, YamlConfigurationLoader> { 070 private final DumperOptions options = new DumperOptions(); 071 private @Nullable NodeStyle style; 072 073 Builder() { 074 indent(4); 075 defaultOptions(o -> o.nativeTypes(NATIVE_TYPES)); 076 } 077 078 /** 079 * Sets the level of indentation the resultant loader should use. 080 * 081 * @param indent the indent level 082 * @return this builder (for chaining) 083 * @since 4.0.0 084 */ 085 public Builder indent(final int indent) { 086 this.options.setIndent(indent); 087 return this; 088 } 089 090 /** 091 * Gets the level of indentation to be used by the resultant loader. 092 * 093 * @return the indent level 094 * @since 4.0.0 095 */ 096 public int indent() { 097 return this.options.getIndent(); 098 } 099 100 /** 101 * Sets the node style the built loader should use. 102 * 103 * <dl><dt>Flow</dt> 104 * <dd>the compact, json-like representation.<br> 105 * Example: <code> 106 * {value: [list, of, elements], another: value} 107 * </code></dd> 108 * 109 * <dt>Block</dt> 110 * <dd>expanded, traditional YAML<br> 111 * Example: <code> 112 * value: 113 * - list 114 * - of 115 * - elements 116 * another: value 117 * </code></dd> 118 * </dl> 119 * 120 * <p>A {@code null} value will tell the loader to pick a value 121 * automatically based on the contents of each non-scalar node.</p> 122 * 123 * @param style the node style to use 124 * @return this builder (for chaining) 125 * @since 4.0.0 126 */ 127 public Builder nodeStyle(final @Nullable NodeStyle style) { 128 this.style = style; 129 return this; 130 } 131 132 /** 133 * Gets the node style to be used by the resultant loader. 134 * 135 * @return the node style 136 * @since 4.0.0 137 */ 138 public @Nullable NodeStyle nodeStyle() { 139 return this.style; 140 } 141 142 @Override 143 public YamlConfigurationLoader build() { 144 return new YamlConfigurationLoader(this); 145 } 146 } 147 148 private final ThreadLocal<Yaml> yaml; 149 150 private YamlConfigurationLoader(final Builder builder) { 151 super(builder, new CommentHandler[] {CommentHandlers.HASH}); 152 final DumperOptions opts = builder.options; 153 opts.setDefaultFlowStyle(NodeStyle.asSnakeYaml(builder.style)); 154 this.yaml = ThreadLocal.withInitial(() -> new ConfigurateYaml(opts)); 155 } 156 157 @Override 158 protected void loadInternal(final CommentedConfigurationNode node, final BufferedReader reader) { 159 node.raw(this.yaml.get().load(reader)); 160 } 161 162 @Override 163 protected void saveInternal(final ConfigurationNode node, final Writer writer) { 164 this.yaml.get().dump(node.raw(), writer); 165 } 166 167 @Override 168 public CommentedConfigurationNode createNode(final ConfigurationOptions options) { 169 return CommentedConfigurationNode.root(options); 170 } 171 172}