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