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 ninja.leaping.configurate.yaml;
018
019import ninja.leaping.configurate.ConfigurationNode;
020import ninja.leaping.configurate.ConfigurationOptions;
021import ninja.leaping.configurate.loader.AbstractConfigurationLoader;
022import ninja.leaping.configurate.loader.CommentHandler;
023import ninja.leaping.configurate.loader.CommentHandlers;
024import org.checkerframework.checker.nullness.qual.NonNull;
025import org.yaml.snakeyaml.DumperOptions;
026import org.yaml.snakeyaml.DumperOptions.FlowStyle;
027import org.yaml.snakeyaml.Yaml;
028
029import java.io.BufferedReader;
030import java.io.IOException;
031import java.io.Writer;
032
033/**
034 * A loader for YAML-formatted configurations, using the SnakeYAML library for parsing and generation.
035 *
036 */
037public class YAMLConfigurationLoader extends AbstractConfigurationLoader<ConfigurationNode> {
038
039    /**
040     * Creates a new {@link YAMLConfigurationLoader} builder.
041     *
042     * @return A new builder
043     */
044    @NonNull
045    public static Builder builder() {
046        return new Builder();
047    }
048
049    /**
050     * Builds a {@link YAMLConfigurationLoader}.
051     */
052    public static class Builder extends AbstractConfigurationLoader.Builder<Builder> {
053        private final DumperOptions options = new DumperOptions();
054
055        protected Builder() {
056            setIndent(4);
057        }
058
059        /**
060         * Sets the level of indentation the resultant loader should use.
061         *
062         * @param indent The indent level
063         * @return This builder (for chaining)
064         */
065        @NonNull
066        public Builder setIndent(int indent) {
067            options.setIndent(indent);
068            return this;
069        }
070
071        /**
072         * Gets the level of indentation to be used by the resultant loader.
073         *
074         * @return The indent level
075         */
076        public int getIndent() {
077            return options.getIndent();
078        }
079
080        /**
081         * Sets the flow style the resultant loader should use.
082         *
083         * Flow: the compact, json-like representation.<br>
084         * Example: <code>
085         *     {value: [list, of, elements], another: value}
086         * </code>
087         *
088         * Block: expanded, traditional YAML<br>
089         * Example: <code>
090         *     value:
091         *     - list
092         *     - of
093         *     - elements
094         *     another: value
095         * </code>
096         *
097         * @param style The flow style to use
098         * @return This builder (for chaining)
099         */
100        @NonNull
101        public Builder setFlowStyle(@NonNull FlowStyle style) {
102            options.setDefaultFlowStyle(style);
103            return this;
104        }
105
106        /**
107         * Gets the flow style to be used by the resultant loader.
108         *
109         * @return The flow style
110         */
111        @NonNull
112        public FlowStyle getFlowSyle() {
113            return options.getDefaultFlowStyle();
114        }
115
116        @NonNull
117        @Override
118        public YAMLConfigurationLoader build() {
119            return new YAMLConfigurationLoader(this);
120        }
121    }
122
123    private final ThreadLocal<Yaml> yaml;
124
125    private YAMLConfigurationLoader(Builder builder) {
126        super(builder, new CommentHandler[] {CommentHandlers.HASH});
127        final DumperOptions opts = builder.options;
128        this.yaml = ThreadLocal.withInitial(() -> new Yaml(opts));
129    }
130
131    @Override
132    protected void loadInternal(ConfigurationNode node, BufferedReader reader) throws IOException {
133        node.setValue(yaml.get().load(reader));
134    }
135
136    @Override
137    protected void saveInternal(ConfigurationNode node, Writer writer) throws IOException {
138        yaml.get().dump(node.getValue(), writer);
139    }
140
141    @NonNull
142    @Override
143    public ConfigurationNode createEmptyNode(@NonNull ConfigurationOptions options) {
144        return ConfigurationNode.root(options);
145    }
146}