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; 028 029import java.io.BufferedReader; 030import java.io.Writer; 031import java.math.BigInteger; 032import java.sql.Timestamp; 033import java.util.Date; 034import java.util.Set; 035 036/** 037 * A loader for YAML-formatted configurations, using the SnakeYAML library for 038 * parsing and generation. 039 * 040 * @since 4.0.0 041 */ 042public final class YamlConfigurationLoader extends AbstractConfigurationLoader<CommentedConfigurationNode> { 043 044 /** 045 * YAML native types from <a href="https://yaml.org/type/">YAML 1.1 Global tags</a>. 046 * 047 * <p>using SnakeYaml representation: https://bitbucket.org/asomov/snakeyaml/wiki/Documentation#markdown-header-yaml-tags-and-java-types 048 */ 049 private static final Set<Class<?>> NATIVE_TYPES = UnmodifiableCollections.toSet( 050 Boolean.class, Integer.class, Long.class, BigInteger.class, Double.class, // numeric 051 byte[].class, String.class, Date.class, java.sql.Date.class, Timestamp.class); // complex types 052 053 /** 054 * Creates a new {@link YamlConfigurationLoader} builder. 055 * 056 * @return a new builder 057 * @since 4.0.0 058 */ 059 public static Builder builder() { 060 return new Builder(); 061 } 062 063 /** 064 * Builds a {@link YamlConfigurationLoader}. 065 * 066 * @since 4.0.0 067 */ 068 public static final class Builder extends AbstractConfigurationLoader.Builder<Builder, YamlConfigurationLoader> { 069 private final DumperOptions options = new DumperOptions(); 070 private @Nullable NodeStyle style; 071 072 Builder() { 073 indent(4); 074 defaultOptions(o -> o.nativeTypes(NATIVE_TYPES)); 075 } 076 077 /** 078 * Sets the level of indentation the resultant loader should use. 079 * 080 * @param indent the indent level 081 * @return this builder (for chaining) 082 * @since 4.0.0 083 */ 084 public Builder indent(final int indent) { 085 this.options.setIndent(indent); 086 return this; 087 } 088 089 /** 090 * Gets the level of indentation to be used by the resultant loader. 091 * 092 * @return the indent level 093 * @since 4.0.0 094 */ 095 public int indent() { 096 return this.options.getIndent(); 097 } 098 099 /** 100 * Sets the node style the built loader should use. 101 * 102 * <dl><dt>Flow</dt> 103 * <dd>the compact, json-like representation.<br> 104 * Example: <code> 105 * {value: [list, of, elements], another: value} 106 * </code></dd> 107 * 108 * <dt>Block</dt> 109 * <dd>expanded, traditional YAML<br> 110 * Example: <code> 111 * value: 112 * - list 113 * - of 114 * - elements 115 * another: value 116 * </code></dd> 117 * </dl> 118 * 119 * <p>A {@code null} value will tell the loader to pick a value 120 * automatically based on the contents of each non-scalar node.</p> 121 * 122 * @param style the node style to use 123 * @return this builder (for chaining) 124 * @since 4.0.0 125 */ 126 public Builder nodeStyle(final @Nullable NodeStyle style) { 127 this.style = style; 128 return this; 129 } 130 131 /** 132 * Gets the node style to be used by the resultant loader. 133 * 134 * @return the node style 135 * @since 4.0.0 136 */ 137 public @Nullable NodeStyle nodeStyle() { 138 return this.style; 139 } 140 141 @Override 142 public YamlConfigurationLoader build() { 143 return new YamlConfigurationLoader(this); 144 } 145 } 146 147 private final ThreadLocal<ConfigurateYaml> yaml; 148 149 private YamlConfigurationLoader(final Builder builder) { 150 super(builder, new CommentHandler[] {CommentHandlers.HASH}); 151 final DumperOptions opts = builder.options; 152 opts.setDefaultFlowStyle(NodeStyle.asSnakeYaml(builder.style)); 153 this.yaml = ThreadLocal.withInitial(() -> new ConfigurateYaml(opts)); 154 } 155 156 @Override 157 protected void loadInternal(final CommentedConfigurationNode node, final BufferedReader reader) { 158 node.raw(this.yaml.get().loadConfigurate(reader)); 159 } 160 161 @Override 162 protected void saveInternal(final ConfigurationNode node, final Writer writer) { 163 this.yaml.get().dump(node.raw(), writer); 164 } 165 166 @Override 167 public CommentedConfigurationNode createNode(final ConfigurationOptions options) { 168 return CommentedConfigurationNode.root(options); 169 } 170 171}