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.transformation;
018
019import com.google.common.collect.Iterators;
020import ninja.leaping.configurate.ConfigurationNode;
021import org.checkerframework.checker.nullness.qual.NonNull;
022
023import java.util.Arrays;
024import java.util.Iterator;
025import java.util.SortedMap;
026import java.util.TreeMap;
027
028/**
029 * Represents a set of transformations on a configuration.
030 */
031public abstract class ConfigurationTransformation {
032
033    /**
034     * A special object that represents a wildcard in a path provided to a configuration transformer
035     */
036    public static final Object WILDCARD_OBJECT = new Object();
037
038    /**
039     * Create a new builder to create a basic configuration transformation.
040     *
041     * @return a new transformation builder.
042     */
043    @NonNull
044    public static Builder builder() {
045        return new Builder();
046    }
047
048    /**
049     * This creates a builder for versioned transformations.
050     *
051     * @return A new builder for versioned transformations
052     */
053    @NonNull
054    public static VersionedBuilder versionedBuilder() {
055        return new VersionedBuilder();
056    }
057
058    /**
059     * Creates a chain of {@link ConfigurationTransformation}s.
060     *
061     * @param transformations The transformations
062     * @return The resultant transformation chain
063     */
064    @NonNull
065    public static ConfigurationTransformation chain(ConfigurationTransformation... transformations) {
066        return new ChainedConfigurationTransformation(transformations);
067    }
068
069    /**
070     * Apply this transformation to a given node
071     *
072     * @param node The target node
073     */
074    public abstract void apply(@NonNull ConfigurationNode node);
075
076    /**
077     * Builds a basic {@link ConfigurationTransformation}.
078     */
079    public static final class Builder {
080        private MoveStrategy strategy = MoveStrategy.OVERWRITE;
081        private final SortedMap<Object[], TransformAction> actions;
082
083        protected Builder() {
084            this.actions = new TreeMap<>(new NodePathComparator());
085        }
086
087        /**
088         * Adds an action to the transformation.
089         *
090         * @param path The path to apply the action at
091         * @param action The action
092         * @return This builder (for chaining)
093         */
094        @NonNull
095        public Builder addAction(Object[] path, TransformAction action) {
096            actions.put(path, action);
097            return this;
098        }
099
100        /**
101         * Gets the move strategy to be used by the resultant transformation.
102         *
103         * @return The move strategy
104         */
105        @NonNull
106        public MoveStrategy getMoveStrategy() {
107            return strategy;
108        }
109
110        /**
111         * Sets the mode strategy to be used by the resultant transformation.
112         *
113         * @param strategy The strategy
114         * @return This builder (for chaining)
115         */
116        @NonNull
117        public Builder setMoveStrategy(@NonNull MoveStrategy strategy) {
118            this.strategy = strategy;
119            return this;
120        }
121
122        /**
123         * Builds the transformation.
124         *
125         * @return The transformation
126         */
127        @NonNull
128        public ConfigurationTransformation build() {
129            return new SingleConfigurationTransformation(actions, strategy);
130        }
131    }
132
133    /**
134     * Builds a versioned {@link ConfigurationTransformation}.
135     */
136    public static final class VersionedBuilder {
137        private Object[] versionKey = new Object[] {"version"};
138        private final SortedMap<Integer, ConfigurationTransformation> versions = new TreeMap<>();
139
140        protected VersionedBuilder() {}
141
142        /**
143         * Sets the path of the version key within the configuration.
144         *
145         * @param versionKey The path to the version key
146         * @return This builder (for chaining)
147         */
148        @NonNull
149        public VersionedBuilder setVersionKey(@NonNull Object... versionKey) {
150            this.versionKey = Arrays.copyOf(versionKey, versionKey.length, Object[].class);
151            return this;
152        }
153
154        /**
155         * Adds a transformation to this builder for the given version.
156         *
157         * @param version The version
158         * @param transformation The transformation
159         * @return This builder (for chaining)
160         */
161        @NonNull
162        public VersionedBuilder addVersion(int version, @NonNull ConfigurationTransformation transformation) {
163            versions.put(version, transformation);
164            return this;
165        }
166
167        /**
168         * Builds the transformation.
169         *
170         * @return The transformation
171         */
172        @NonNull
173        public ConfigurationTransformation build() {
174            return new VersionedTransformation(versionKey, versions);
175        }
176    }
177
178    /**
179     * Implementation of {@link ninja.leaping.configurate.transformation.NodePath} used by this class.
180     */
181    // TODO Remove usages of this class in favour of the NodePath interface (breaking change for 4.0)
182    public static final class NodePath implements ninja.leaping.configurate.transformation.NodePath {
183        Object[] arr;
184
185        NodePath() {
186        }
187
188        @Override
189        public Object get(int i) {
190            return arr[i];
191        }
192
193        @Override
194        public int size() {
195            return arr.length;
196        }
197
198        @Override
199        public Object[] getArray() {
200            return Arrays.copyOf(arr, arr.length);
201        }
202
203        @NonNull
204        @Override
205        public Iterator<Object> iterator() {
206            return Iterators.forArray(arr);
207        }
208    }
209}