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;
018
019import io.leangen.geantyref.TypeToken;
020import org.spongepowered.configurate.serialize.SerializationException;
021import org.spongepowered.configurate.util.CheckedConsumer;
022
023import java.util.Map;
024import java.util.stream.Collector;
025
026/**
027 * Something that can create a customized node.
028 *
029 * @since 4.0.0
030 */
031@FunctionalInterface
032public interface ConfigurationNodeFactory<N extends ConfigurationNode> {
033
034    /**
035     * Default options for the types of nodes created by this factory.
036     *
037     * <p>All values must match what a created node will see, but some values
038     * may be determined by this factory to be non user-modifiable. These should
039     * be documented for any factory implementation.
040     *
041     * @return default options
042     * @since 4.0.0
043     */
044    default ConfigurationOptions defaultOptions() {
045        return ConfigurationOptions.defaults();
046    }
047
048    /**
049     * Create an empty node with the provided options.
050     *
051     * <p>Node options may be overridden if the factory enforces specific
052     * requirements on options.
053     *
054     * @param options node options
055     * @return newly created empty node
056     * @since 4.0.0
057     */
058    N createNode(ConfigurationOptions options);
059
060    /**
061     * Create a new node with default options.
062     *
063     * @return newly created empty node
064     * @since 4.0.0
065     */
066    default N createNode() {
067        return createNode(defaultOptions());
068    }
069
070    /**
071     * Create a new node with default options and initialize it with the
072     * provided action.
073     *
074     * @param <E> thrown type
075     * @param action action to initialize node with
076     * @return newly created empty node
077     * @throws E when thrown from inner action
078     * @since 4.0.0
079     */
080    default <E extends Exception> N createNode(final CheckedConsumer<N, E> action) throws E {
081        final N node = createNode();
082        action.accept(node);
083        return node;
084    }
085
086    /**
087     * Create a new node with the provided options and initialize it with the
088     * provided action.
089     *
090     * <p>Node options may be overridden if the factory enforces specific
091     * requirements on options.
092     *
093     * @param <E> thrown type
094     * @param options node options
095     * @param action action to initialize node with
096     * @return newly created empty node
097     * @throws E when thrown from inner action
098     * @since 4.0.0
099     */
100    default <E extends Exception> N createNode(final ConfigurationOptions options, final CheckedConsumer<N, E> action) throws E {
101        final N node = createNode(options);
102        action.accept(node);
103        return node;
104    }
105
106    /**
107     * Create a collector that appends values to a newly created node as
108     * map children.
109     *
110     * <p>This collector does not accept values in parallel.</p>
111     *
112     * @param valueType marker for value type
113     * @param <V> value type
114     * @return a new collector
115     * @since 4.0.0
116     */
117    default <V> Collector<Map.Entry<?, V>, N, N> toMapCollector(final TypeToken<V> valueType) {
118        return Collector.of(this::createNode, (node, entry) -> {
119            try {
120                node.node(entry.getKey()).set(valueType, entry.getValue());
121            } catch (SerializationException e) {
122                throw new IllegalArgumentException(e);
123            }
124        }, (a, b) -> {
125                a.mergeFrom(b);
126                return a;
127            });
128    }
129
130    /**
131     * Create a collector that appends values to a newly created node as
132     * map children.
133     *
134     * <p>This collector does not accept values in parallel.</p>
135     *
136     * @param valueType marker for value type
137     * @param <V> value type
138     * @return a new collector
139     * @since 4.0.0
140     */
141    default <V> Collector<Map.Entry<?, V>, N, N> toMapCollector(final Class<V> valueType) {
142        return Collector.of(this::createNode, (node, entry) -> {
143            try {
144                node.node(entry.getKey()).set(valueType, entry.getValue());
145            } catch (SerializationException e) {
146                throw new IllegalArgumentException(e);
147            }
148        }, (a, b) -> {
149                a.mergeFrom(b);
150                return a;
151            });
152    }
153
154    /**
155     * Create a collector that appends values to a newly created node as
156     * list children.
157     *
158     * <p>This collector does not accept values in parallel.</p>
159     *
160     * @param valueType marker for value type
161     * @param <V> value type
162     * @return a new collector
163     * @since 4.0.0
164     */
165    default <V> Collector<V, N, N> toListCollector(final TypeToken<V> valueType) {
166        return Collector.of(this::createNode, (node, value) -> {
167            try {
168                node.appendListNode().set(valueType, value);
169            } catch (SerializationException e) {
170                throw new IllegalArgumentException(e);
171            }
172        }, (a, b) -> {
173                a.mergeFrom(b);
174                return a;
175            });
176    }
177
178    /**
179     * Create a collector that appends values to a newly created node as
180     * list children.
181     *
182     * <p>This collector does not accept values in parallel.</p>
183     *
184     * @param valueType marker for value type
185     * @param <V> value type
186     * @return a new collector
187     * @since 4.0.0
188     */
189    default <V> Collector<V, N, N> toListCollector(final Class<V> valueType) {
190        return Collector.of(this::createNode, (node, value) -> {
191            try {
192                node.appendListNode().set(valueType, value);
193            } catch (SerializationException e) {
194                throw new IllegalArgumentException(e);
195            }
196        }, (a, b) -> {
197                a.mergeFrom(b);
198                return a;
199            });
200    }
201
202}