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.GenericTypeReflector;
020import io.leangen.geantyref.TypeToken;
021import org.checkerframework.checker.nullness.qual.Nullable;
022import org.spongepowered.configurate.serialize.SerializationException;
023import org.spongepowered.configurate.serialize.TypeSerializer;
024import org.spongepowered.configurate.util.CheckedConsumer;
025import org.spongepowered.configurate.util.Types;
026
027import java.lang.reflect.AnnotatedType;
028import java.lang.reflect.Type;
029import java.util.List;
030import java.util.Map;
031import java.util.stream.Collector;
032
033/**
034 * Intermediate node type to reduce need for casting.
035 *
036 * <p>Any methods that return {@link ConfigurationNode} in
037 * {@link ConfigurationNode} should be overridden to return the {@code N}
038 * self-type instead.</p>
039 *
040 * @param <N> self type
041 * @since 4.0.0
042 */
043public interface ScopedConfigurationNode<N extends ScopedConfigurationNode<N>> extends ConfigurationNode {
044
045    /**
046     * Get a correctly typed instance of this node.
047     *
048     * @return the node type
049     * @since 4.0.0
050     */
051    N self();
052
053    /**
054     * {@inheritDoc}
055     */
056    @Override
057    N appendListNode();
058
059    /**
060     * {@inheritDoc}
061     */
062    @Override
063    N copy();
064
065    /**
066     * {@inheritDoc}
067     */
068    @Override
069    N node(Object... path);
070
071    /**
072     * {@inheritDoc}
073     */
074    @Override
075    N node(Iterable<?> path);
076
077    /**
078     * {@inheritDoc}
079     */
080    @Override
081    @Nullable N parent();
082
083    /**
084     * {@inheritDoc}
085     */
086    @Override
087    N from(ConfigurationNode other);
088
089    /**
090     * {@inheritDoc}
091     */
092    @Override
093    N mergeFrom(ConfigurationNode other);
094
095    /**
096     * {@inheritDoc}
097     */
098    @Override
099    N set(@Nullable Object value) throws SerializationException;
100
101    /**
102     * {@inheritDoc}
103     */
104    @Override
105    @SuppressWarnings({"unchecked", "rawtypes"}) // for TypeSerializer.serialize
106    default N set(final Type type, final @Nullable Object value) throws SerializationException {
107        if (value == null) {
108            return this.set(null);
109        }
110        final Type boxed = Types.box(type);
111        final Class<?> erasedType = GenericTypeReflector.erase(boxed);
112        if (!erasedType.isInstance(value)) {
113            throw new SerializationException(this, type, "Got a value of unexpected type "
114                + value.getClass().getName() + ", when the value should be an instance of " + erasedType.getSimpleName());
115        }
116
117        final @Nullable TypeSerializer<?> serial = this.options().serializers().get(boxed);
118        if (serial != null) {
119            try {
120                ((TypeSerializer) serial).serialize(boxed, value, this.self());
121            } catch (final SerializationException ex) {
122                ex.initPath(this::path);
123                ex.initType(type);
124                throw ex;
125            }
126        } else if (this.options().acceptsType(value.getClass())) {
127            this.raw(value); // Just write if no applicable serializer exists?
128        } else {
129            throw new SerializationException(this, type, "No serializer available for type " + type);
130        }
131        return this.self();
132    }
133
134    /**
135     * {@inheritDoc}
136     */
137    @Override
138    @SuppressWarnings({"unchecked", "rawtypes"}) // for TypeSerializer.serialize
139    default N set(final AnnotatedType type, final @Nullable Object value) throws SerializationException {
140        if (value == null) {
141            return this.set(null);
142        }
143        final Class<?> erasedType = GenericTypeReflector.erase(type.getType());
144        if (!erasedType.isInstance(value)) {
145            throw new SerializationException(this, type, "Got a value of unexpected type "
146                + value.getClass().getName() + ", when the value should be an instance of " + erasedType.getSimpleName());
147        }
148
149        final @Nullable TypeSerializer<?> serial = this.options().serializers().get(type);
150        if (serial != null) {
151            try {
152                ((TypeSerializer) serial).serialize(type, value, this);
153            } catch (final SerializationException ex) {
154                ex.initPath(this::path);
155                ex.initType(type);
156                throw ex;
157            }
158        } else if (this.options().acceptsType(value.getClass())) {
159            this.raw(value); // Just write if no applicable serializer exists?
160        } else {
161            throw new SerializationException(this, type, "No serializer available for type " + type);
162        }
163        return this.self();
164    }
165
166    @Override
167    default <V> N set(final Class<V> type, final @Nullable V value) throws SerializationException {
168        return this.set((Type) type, value);
169    }
170
171    @Override
172    default <V> N set(final TypeToken<V> type, final @Nullable V value) throws SerializationException {
173        return this.set(type.getType(), value);
174    }
175
176    @Override
177    default <V> N setList(final Class<V> elementType, final @Nullable List<V> items) throws SerializationException {
178        ConfigurationNode.super.setList(elementType, items);
179        return this.self();
180    }
181
182    @Override
183    default <V> N setList(final TypeToken<V> elementType, final @Nullable List<V> items) throws SerializationException {
184        ConfigurationNode.super.setList(elementType, items);
185        return this.self();
186    }
187
188    /**
189     * {@inheritDoc}
190     */
191    @Override
192    N raw(@Nullable Object value);
193
194    /**
195     * {@inheritDoc}
196     */
197    @Override
198    List<N> childrenList();
199
200    /**
201     * {@inheritDoc}
202     */
203    @Override
204    Map<Object, N> childrenMap();
205
206    /**
207     * {@inheritDoc}
208     */
209    @SuppressWarnings({"unchecked", "rawtypes"})
210    @Override
211    default <V> Collector<Map.Entry<?, V>, N, N> toMapCollector(final TypeToken<V> valueType) {
212        return (Collector) ConfigurationNode.super.toMapCollector(valueType);
213    }
214
215    /**
216     * {@inheritDoc}
217     */
218    @SuppressWarnings({"unchecked", "rawtypes"})
219    @Override
220    default <V> Collector<Map.Entry<?, V>, N, N> toMapCollector(final Class<V> valueType) {
221        return (Collector) ConfigurationNode.super.toMapCollector(valueType);
222    }
223
224    /**
225     * {@inheritDoc}
226     */
227    @SuppressWarnings({"unchecked", "rawtypes"})
228    @Override
229    default <V> Collector<V, N, N> toListCollector(final TypeToken<V> valueType) {
230        return (Collector) ConfigurationNode.super.toListCollector(valueType);
231    }
232
233    /**
234     * {@inheritDoc}
235     */
236    @SuppressWarnings({"unchecked", "rawtypes"})
237    @Override
238    default <V> Collector<V, N, N> toListCollector(final Class<V> valueType) {
239        return (Collector) ConfigurationNode.super.toListCollector(valueType);
240    }
241
242    /**
243     * Execute an action on this node. This allows performing multiple
244     * operations on a single node without having to clutter up the surrounding
245     * scope.
246     *
247     * @param <E> thrown type
248     * @param action the action to perform on this node
249     * @return this node
250     * @throws E when thrown by callback {@code action}
251     * @since 4.0.0
252     */
253    default <E extends Exception> N act(final CheckedConsumer<? super N, E> action) throws E {
254        action.accept(this.self());
255        return this.self();
256    }
257
258    @Override
259    <V> N hint(RepresentationHint<V> hint, @Nullable V value);
260
261}