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;
018
019/**
020 * A visitor to traverse node hierarchies in a depth-first order
021 * <p>
022 * Instances of stateful implementations may be reusable by taking advantage of the state object. During each
023 * visitation, a visitor will experience the node tree as a sequence of events, described by the following
024 * pseudo-grammar:
025 *
026 * <pre>
027 * mappingNode: enterMappingNode node* exitMappingNode
028 * listNode: enterListNode node* exitListNode
029 *
030 * node: enterNode
031 *      (mappingNode
032 *       | listNode
033 *       | enterScalarNode)
034 *
035 * visit: newState?
036 *        beginVisit
037 *        node*
038 *        endVisit
039 * </pre>
040 * <p>
041 * If the starting node has no value, no node events will be received. Otherwise, the first event received will be for
042 * the starting node itself, and will continue from there.
043 * <p>
044 * The children to visit for list and mapping nodes will only be collected after both the {@code enterNode} and {@code
045 * enter(List|Mapping)Node} methods have been executed for the node, and changes to the node values may be made to
046 * control which nodes will be visited.
047 * <p>
048 * Any exceptions thrown within the visitor will result in the visitation ending immediately and the exception being
049 * rethrown within the visit method
050 * <p>
051 * There are a few specializations of the visitor interface available: {@link Stateless} carries no state and can act as
052 * a functional interface type, and {@link Safe} which throws no checked exceptions and therefore can be visited without
053 * having to handle any exceptions.
054 *
055 * @param <S> A state object that will be used for one visit
056 * @param <T> The terminal value, that can be returned at the end of the visit
057 * @param <E> exception type that may be thrown
058 * @see ConfigurationNode#visit(ConfigurationVisitor) to execute this configuration visitation
059 */
060public interface ConfigurationVisitor<S, T, E extends Exception> {
061
062    /**
063     * Called to provide a state object if a visit is initiated without one already existing
064     *
065     * @return A new state object to be passed through the rest of this visit
066     * @throws E when thrown by implementation
067     */
068    S newState() throws E;
069
070    /**
071     * Called at the beginning of the visit with a state object created.
072     *
073     * @param node  the root node
074     * @param state the state
075     * @throws E when thrown by implementation
076     */
077    void beginVisit(ConfigurationNode node, S state) throws E;
078
079    /**
080     * Called once per node, for every node
081     *
082     * @param node  The current node
083     * @param state provided state
084     * @throws E when thrown by implementation
085     */
086    void enterNode(ConfigurationNode node, S state) throws E;
087
088    /**
089     * Called after {@link #enterNode(ConfigurationNode, Object)} for mapping nodes
090     *
091     * @param node  current node
092     * @param state provided state
093     * @throws E when thrown by implementation
094     */
095    void enterMappingNode(ConfigurationNode node, S state) throws E;
096
097    /**
098     * Called after {@link #enterNode(ConfigurationNode, Object)} for list nodes
099     *
100     * @param node  current node
101     * @param state provided state
102     * @throws E when thrown by implementation
103     */
104    void enterListNode(ConfigurationNode node, S state) throws E;
105
106    /**
107     * Called after {@link #enterNode(ConfigurationNode, Object)} for scalar nodes
108     *
109     * @param node  current node
110     * @param state provided state
111     * @throws E when thrown by implementation
112     */
113    void enterScalarNode(ConfigurationNode node, S state) throws E;
114
115    /**
116     * Called for a list node after the node and any of its children have been visited
117     *
118     * @param node  The node that has been visited
119     * @param state provided state
120     * @throws E when thrown by implementation
121     */
122    void exitMappingNode(ConfigurationNode node, S state) throws E;
123
124    /**
125     * Called for a list node after the node and any of its children have been visited
126     *
127     * @param node  The node that has been visited
128     * @param state provided state
129     * @throws E when thrown by implementation
130     */
131    void exitListNode(ConfigurationNode node, S state) throws E;
132
133    /**
134     * Called after every node has been visited, to allow for cleanup and validation
135     *
136     * @param state provided state
137     * @return a terminal value
138     * @throws E when thrown by implementation
139     */
140    T endVisit(S state) throws E;
141
142    /**
143     * Stateless specialization of visitors, where both the state and terminal type are Void
144     *
145     */
146    @FunctionalInterface
147    interface Stateless<E extends Exception> extends ConfigurationVisitor<Void, Void, E> {
148        @Override
149        default Void newState() {
150            return null;
151        }
152
153        @Override
154        default void beginVisit(ConfigurationNode node, Void state) throws E {
155            beginVisit(node);
156        }
157
158        @Override
159        default void enterNode(ConfigurationNode node, Void state) throws E {
160            enterNode(node);
161        }
162
163        @Override
164        default void enterMappingNode(ConfigurationNode node, Void state) throws E {
165            enterMappingNode(node);
166        }
167
168        @Override
169        default void enterListNode(ConfigurationNode node, Void state) throws E {
170            enterListNode(node);
171        }
172
173        @Override
174        default void enterScalarNode(ConfigurationNode node, Void state) throws E {
175            enterScalarNode(node);
176        }
177
178        @Override
179        default void exitMappingNode(ConfigurationNode node, Void state) throws E {
180            exitMappingNode(node);
181        }
182
183        @Override
184        default void exitListNode(ConfigurationNode node, Void state) throws E {
185            exitListNode(node);
186        }
187
188        @Override
189        default Void endVisit(Void state) throws E {
190            endVisit();
191            return null;
192        }
193
194        /**
195         * Called at the beginning of the visit with a state object created.
196         *
197         * @param node The root node
198         * @throws E as required by implementation
199         */
200        default void beginVisit(ConfigurationNode node) throws E {
201        }
202
203        /**
204         * Called once per node, for every node
205         *
206         * @param node The current node
207         * @throws E as required by implementation
208         */
209        void enterNode(ConfigurationNode node) throws E;
210
211        /**
212         * Called after {@link #enterNode(ConfigurationNode, Object)} for mapping nodes
213         *
214         * @param node current node
215         * @throws E when thrown by implementation
216         */
217        default void enterMappingNode(ConfigurationNode node) throws E {
218        }
219
220        /**
221         * Called after {@link #enterNode(ConfigurationNode, Object)} for list nodes
222         *
223         * @param node current node
224         * @throws E when thrown by implementation
225         */
226        default void enterListNode(ConfigurationNode node) throws E {
227        }
228
229        /**
230         * Called after {@link #enterNode(ConfigurationNode, Object)} for scalar nodes
231         *
232         * @param node current node
233         * @throws E when thrown by implementation
234         */
235        default void enterScalarNode(ConfigurationNode node) throws E {
236        }
237
238        /**
239         * Called for a mapping node after the node and any of its children have been visited
240         *
241         * @param node The node that has been visited
242         * @throws E when thrown by implementation
243         */
244        default void exitMappingNode(ConfigurationNode node) throws E {
245        }
246
247        /**
248         * Called for a list node after the node and any of its children have been visited
249         *
250         * @param node The node that has been visited
251         * @throws E when thrown by implementation
252         */
253        default void exitListNode(ConfigurationNode node) throws E {
254        }
255
256        /**
257         * Called after every node has been visited, to allow for cleanup and validation
258         *
259         * @throws E when thrown by implementation
260         */
261        default void endVisit() throws E {
262        }
263    }
264
265    /**
266     * A subinterface for visitors that do not throw any checked exceptions during their execution
267     *
268     * @param <S> state type
269     * @param <T> terminal value type
270     */
271    interface Safe<S, T> extends ConfigurationVisitor<S, T, VisitorSafeNoopException> {
272
273        @Override
274        S newState();
275
276        @Override
277        void beginVisit(ConfigurationNode node, S state);
278
279        @Override
280        void enterNode(ConfigurationNode node, S state);
281
282        @Override
283        void enterMappingNode(ConfigurationNode node, S state);
284
285        @Override
286        void enterListNode(ConfigurationNode node, S state);
287
288        @Override
289        void enterScalarNode(ConfigurationNode node, S state);
290
291        @Override
292        void exitMappingNode(ConfigurationNode node, S state);
293
294        @Override
295        void exitListNode(ConfigurationNode node, S state);
296
297        @Override
298        T endVisit(S state);
299    }
300}