001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  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.apache.commons.configuration2.interpol;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.LinkedList;
024import java.util.Map;
025
026/**
027 * <p>
028 * A simple value class defining a {@link ConfigurationInterpolator}.
029 * </p>
030 * <p>
031 * Objects of this class can be used for creating new
032 * {@code ConfigurationInterpolator} instances; they contain all required
033 * properties. It is either possible to set a fully initialized
034 * {@code ConfigurationInterpolator} directly which can be used as is.
035 * Alternatively, some or all properties of an instance to be newly created can
036 * be set. These properties include
037 * </p>
038 * <ul>
039 * <li>a map with {@code Lookup} objects associated with a specific prefix</li>
040 * <li>a collection with default {@code Lookup} objects (without a prefix)</li>
041 * <li>a parent {@code ConfigurationInterpolator}</li>
042 * </ul>
043 * <p>
044 * When setting up a configuration it is possible to define the
045 * {@code ConfigurationInterpolator} in terms of this class. The configuration
046 * will then either use the {@code ConfigurationInterpolator} instance
047 * explicitly defined in the {@code InterpolatorSpecification} instance or
048 * create a new one.
049 * </p>
050 * <p>
051 * Instances are not created directly, but using the nested {@code Builder}
052 * class. They are then immutable.
053 * </p>
054 *
055 * @version $Id: InterpolatorSpecification.java 1790899 2017-04-10 21:56:46Z ggregory $
056 * @since 2.0
057 */
058public final class InterpolatorSpecification
059{
060    /** The {@code ConfigurationInterpolator} instance to be used directly. */
061    private final ConfigurationInterpolator interpolator;
062
063    /** The parent {@code ConfigurationInterpolator}. */
064    private final ConfigurationInterpolator parentInterpolator;
065
066    /** The map with prefix lookups. */
067    private final Map<String, Lookup> prefixLookups;
068
069    /** The collection with default lookups. */
070    private final Collection<Lookup> defaultLookups;
071
072    /**
073     * Creates a new instance of {@code InterpolatorSpecification} with the
074     * properties defined by the given builder object.
075     *
076     * @param builder the builder
077     */
078    private InterpolatorSpecification(Builder builder)
079    {
080        interpolator = builder.interpolator;
081        parentInterpolator = builder.parentInterpolator;
082        prefixLookups =
083                Collections.unmodifiableMap(new HashMap<>(
084                        builder.prefixLookups));
085        defaultLookups =
086                Collections.unmodifiableCollection(new ArrayList<>(
087                        builder.defLookups));
088    }
089
090    /**
091     * Returns the {@code ConfigurationInterpolator} instance to be used
092     * directly.
093     *
094     * @return the {@code ConfigurationInterpolator} (can be <b>null</b>)
095     */
096    public ConfigurationInterpolator getInterpolator()
097    {
098        return interpolator;
099    }
100
101    /**
102     * Returns the parent {@code ConfigurationInterpolator} object.
103     *
104     * @return the parent {@code ConfigurationInterpolator} (can be <b>null</b>)
105     */
106    public ConfigurationInterpolator getParentInterpolator()
107    {
108        return parentInterpolator;
109    }
110
111    /**
112     * Returns a map with prefix lookups. The keys of the map are the prefix
113     * strings, its values are the corresponding {@code Lookup} objects.
114     *
115     * @return the prefix lookups for a new {@code ConfigurationInterpolator}
116     *         instance (never <b>null</b>)
117     */
118    public Map<String, Lookup> getPrefixLookups()
119    {
120        return prefixLookups;
121    }
122
123    /**
124     * Returns a collection with the default lookups.
125     *
126     * @return the default lookups for a new {@code ConfigurationInterpolator}
127     *         instance (never <b>null</b>)
128     */
129    public Collection<Lookup> getDefaultLookups()
130    {
131        return defaultLookups;
132    }
133
134    /**
135     * <p>A <em>builder</em> class for creating instances of
136     * {@code InterpolatorSpecification}.</p>
137     * <p>
138     * This class provides a fluent API for defining the various properties of
139     * an {@code InterpolatorSpecification} object. <em>Note:</em> This builder
140     * class is not thread-safe.
141     * </p>
142     */
143    public static class Builder
144    {
145        /** A map with prefix lookups. */
146        private final Map<String, Lookup> prefixLookups;
147
148        /** A collection with default lookups. */
149        private final Collection<Lookup> defLookups;
150
151        /** The {@code ConfigurationInterpolator}. */
152        private ConfigurationInterpolator interpolator;
153
154        /** The parent {@code ConfigurationInterpolator}. */
155        private ConfigurationInterpolator parentInterpolator;
156
157        public Builder()
158        {
159            prefixLookups = new HashMap<>();
160            defLookups = new LinkedList<>();
161        }
162
163        /**
164         * Adds a {@code Lookup} object for a given prefix.
165         *
166         * @param prefix the prefix (must not be <b>null</b>)
167         * @param lookup the {@code Lookup} (must not be <b>null</b>)
168         * @return a reference to this builder for method chaining
169         * @throws IllegalArgumentException if a required parameter is missing
170         */
171        public Builder withPrefixLookup(String prefix, Lookup lookup)
172        {
173            if (prefix == null)
174            {
175                throw new IllegalArgumentException("Prefix must not be null!");
176            }
177            checkLookup(lookup);
178            prefixLookups.put(prefix, lookup);
179            return this;
180        }
181
182        /**
183         * Adds the content of the given map to the prefix lookups managed by
184         * this builder. The map can be <b>null</b>, then this method has no
185         * effect.
186         *
187         * @param lookups the map with prefix lookups to be added
188         * @return a reference to this builder for method chaining
189         * @throws IllegalArgumentException if the map contains <b>null</b>
190         *         values
191         */
192        public Builder withPrefixLookups(Map<String, ? extends Lookup> lookups)
193        {
194            if (lookups != null)
195            {
196                for (Map.Entry<String, ? extends Lookup> e : lookups.entrySet())
197                {
198                    withPrefixLookup(e.getKey(), e.getValue());
199                }
200            }
201            return this;
202        }
203
204        /**
205         * Adds the given {@code Lookup} object to the list of default lookups.
206         *
207         * @param lookup the {@code Lookup} (must not be <b>null</b>)
208         * @return a reference to this builder for method chaining
209         * @throws IllegalArgumentException if the {@code Lookup} is <b>null</b>
210         */
211        public Builder withDefaultLookup(Lookup lookup)
212        {
213            checkLookup(lookup);
214            defLookups.add(lookup);
215            return this;
216        }
217
218        /**
219         * Adds the content of the given collection to the default lookups
220         * managed by this builder. The collection can be <b>null</b>, then this
221         * method has no effect.
222         *
223         * @param lookups the collection with lookups to be added
224         * @return a reference to this builder for method chaining
225         * @throws IllegalArgumentException if the collection contains
226         *         <b>null</b> entries
227         */
228        public Builder withDefaultLookups(Collection<? extends Lookup> lookups)
229        {
230            if (lookups != null)
231            {
232                for (Lookup l : lookups)
233                {
234                    withDefaultLookup(l);
235                }
236            }
237            return this;
238        }
239
240        /**
241         * Sets the {@code ConfigurationInterpolator} instance for the
242         * {@code InterpolatorSpecification}. This means that a
243         * {@code ConfigurationInterpolator} has been created and set up
244         * externally and can be used directly.
245         *
246         * @param ci the {@code ConfigurationInterpolator} (can be <b>null</b>)
247         * @return a reference to this builder for method chaining
248         */
249        public Builder withInterpolator(ConfigurationInterpolator ci)
250        {
251            interpolator = ci;
252            return this;
253        }
254
255        /**
256         * Sets an optional parent {@code ConfigurationInterpolator}. If
257         * defined, this object is set as parent of a newly created
258         * {@code ConfigurationInterpolator} instance.
259         *
260         * @param parent the parent {@code ConfigurationInterpolator} (can be
261         *        <b>null</b>)
262         * @return a reference to this builder for method chaining
263         */
264        public Builder withParentInterpolator(ConfigurationInterpolator parent)
265        {
266            parentInterpolator = parent;
267            return this;
268        }
269
270        /**
271         * Creates a new {@code InterpolatorSpecification} instance with the
272         * properties set so far. After that this builder instance is reset so
273         * that it can be reused for creating further specification objects.
274         *
275         * @return the newly created {@code InterpolatorSpecification}
276         */
277        public InterpolatorSpecification create()
278        {
279            InterpolatorSpecification spec =
280                    new InterpolatorSpecification(this);
281            reset();
282            return spec;
283        }
284
285        /**
286         * Removes all data from this builder. Afterwards it can be used to
287         * define a brand new {@code InterpolatorSpecification} object.
288         */
289        public void reset()
290        {
291            interpolator = null;
292            parentInterpolator = null;
293            prefixLookups.clear();
294            defLookups.clear();
295        }
296
297        /**
298         * Helper method for checking a lookup. Throws an exception if the
299         * lookup is <b>null</b>.
300         *
301         * @param lookup the lookup to be checked
302         * @throws IllegalArgumentException if the lookup is <b>null</b>
303         */
304        private static void checkLookup(Lookup lookup)
305        {
306            if (lookup == null)
307            {
308                throw new IllegalArgumentException("Lookup must not be null!");
309            }
310        }
311    }
312}