View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2.interpol;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.LinkedList;
24  import java.util.Map;
25  import java.util.function.Function;
26  
27  /**
28   * <p>
29   * A simple value class defining a {@link ConfigurationInterpolator}.
30   * </p>
31   * <p>
32   * Objects of this class can be used for creating new {@code ConfigurationInterpolator} instances; they contain all
33   * required properties. It is either possible to set a fully initialized {@code ConfigurationInterpolator} directly
34   * which can be used as is. Alternatively, some or all properties of an instance to be newly created can be set. These
35   * properties include
36   * </p>
37   * <ul>
38   * <li>a map with {@code Lookup} objects associated with a specific prefix</li>
39   * <li>a collection with default {@code Lookup} objects (without a prefix)</li>
40   * <li>a parent {@code ConfigurationInterpolator}</li>
41   * <li>a function used to convert interpolated values into strings</li>
42   * </ul>
43   * <p>
44   * When setting up a configuration it is possible to define the {@code ConfigurationInterpolator} in terms of this
45   * class. The configuration will then either use the {@code ConfigurationInterpolator} instance explicitly defined in
46   * the {@code InterpolatorSpecification} instance or create a new one.
47   * </p>
48   * <p>
49   * Instances are not created directly, but using the nested {@code Builder} class. They are then immutable.
50   * </p>
51   *
52   * @since 2.0
53   */
54  public final class InterpolatorSpecification {
55      /** The {@code ConfigurationInterpolator} instance to be used directly. */
56      private final ConfigurationInterpolator interpolator;
57  
58      /** The parent {@code ConfigurationInterpolator}. */
59      private final ConfigurationInterpolator parentInterpolator;
60  
61      /** The map with prefix lookups. */
62      private final Map<String, Lookup> prefixLookups;
63  
64      /** The collection with default lookups. */
65      private final Collection<Lookup> defaultLookups;
66  
67      /** Function used to convert interpolated values to strings. */
68      private final Function<Object, String> stringConverter;
69  
70      /**
71       * Creates a new instance of {@code InterpolatorSpecification} with the properties defined by the given builder object.
72       *
73       * @param builder the builder
74       */
75      private InterpolatorSpecification(final Builder builder) {
76          interpolator = builder.interpolator;
77          parentInterpolator = builder.parentInterpolator;
78          prefixLookups = Collections.unmodifiableMap(new HashMap<>(builder.prefixLookups));
79          defaultLookups = Collections.unmodifiableCollection(new ArrayList<>(builder.defLookups));
80          stringConverter = builder.stringConverter;
81      }
82  
83      /**
84       * Gets the {@code ConfigurationInterpolator} instance to be used directly.
85       *
86       * @return the {@code ConfigurationInterpolator} (can be <b>null</b>)
87       */
88      public ConfigurationInterpolator getInterpolator() {
89          return interpolator;
90      }
91  
92      /**
93       * Gets the parent {@code ConfigurationInterpolator} object.
94       *
95       * @return the parent {@code ConfigurationInterpolator} (can be <b>null</b>)
96       */
97      public ConfigurationInterpolator getParentInterpolator() {
98          return parentInterpolator;
99      }
100 
101     /**
102      * Gets a map with prefix lookups. The keys of the map are the prefix strings, its values are the corresponding
103      * {@code Lookup} objects.
104      *
105      * @return the prefix lookups for a new {@code ConfigurationInterpolator} instance (never <b>null</b>)
106      */
107     public Map<String, Lookup> getPrefixLookups() {
108         return prefixLookups;
109     }
110 
111     /**
112      * Gets a collection with the default lookups.
113      *
114      * @return the default lookups for a new {@code ConfigurationInterpolator} instance (never <b>null</b>)
115      */
116     public Collection<Lookup> getDefaultLookups() {
117         return defaultLookups;
118     }
119 
120     /**
121      * Gets the function used to convert interpolated values to strings or {@code null}
122      * if the default conversion function is to be used.
123      *
124      * @return function used to convert interpolated values to strings or {@code null} if
125      *      the default conversion function is to be used
126      */
127     public Function<Object, String> getStringConverter() {
128         return stringConverter;
129     }
130 
131     /**
132      * <p>
133      * A <em>builder</em> class for creating instances of {@code InterpolatorSpecification}.
134      * </p>
135      * <p>
136      * This class provides a fluent API for defining the various properties of an {@code InterpolatorSpecification} object.
137      * <em>Note:</em> This builder class is not thread-safe.
138      * </p>
139      */
140     public static class Builder {
141         /** A map with prefix lookups. */
142         private final Map<String, Lookup> prefixLookups;
143 
144         /** A collection with default lookups. */
145         private final Collection<Lookup> defLookups;
146 
147         /** The {@code ConfigurationInterpolator}. */
148         private ConfigurationInterpolator interpolator;
149 
150         /** The parent {@code ConfigurationInterpolator}. */
151         private ConfigurationInterpolator parentInterpolator;
152 
153         /** Function used to convert interpolated values to strings. */
154         private Function<Object, String> stringConverter;
155 
156         public Builder() {
157             prefixLookups = new HashMap<>();
158             defLookups = new LinkedList<>();
159         }
160 
161         /**
162          * Adds a {@code Lookup} object for a given prefix.
163          *
164          * @param prefix the prefix (must not be <b>null</b>)
165          * @param lookup the {@code Lookup} (must not be <b>null</b>)
166          * @return a reference to this builder for method chaining
167          * @throws IllegalArgumentException if a required parameter is missing
168          */
169         public Builder withPrefixLookup(final String prefix, final Lookup lookup) {
170             if (prefix == null) {
171                 throw new IllegalArgumentException("Prefix must not be null!");
172             }
173             checkLookup(lookup);
174             prefixLookups.put(prefix, lookup);
175             return this;
176         }
177 
178         /**
179          * Adds the content of the given map to the prefix lookups managed by this builder. The map can be <b>null</b>, then
180          * this method has no effect.
181          *
182          * @param lookups the map with prefix lookups to be added
183          * @return a reference to this builder for method chaining
184          * @throws IllegalArgumentException if the map contains <b>null</b> values
185          */
186         public Builder withPrefixLookups(final Map<String, ? extends Lookup> lookups) {
187             if (lookups != null) {
188                 lookups.forEach(this::withPrefixLookup);
189             }
190             return this;
191         }
192 
193         /**
194          * Adds the given {@code Lookup} object to the list of default lookups.
195          *
196          * @param lookup the {@code Lookup} (must not be <b>null</b>)
197          * @return a reference to this builder for method chaining
198          * @throws IllegalArgumentException if the {@code Lookup} is <b>null</b>
199          */
200         public Builder withDefaultLookup(final Lookup lookup) {
201             checkLookup(lookup);
202             defLookups.add(lookup);
203             return this;
204         }
205 
206         /**
207          * Adds the content of the given collection to the default lookups managed by this builder. The collection can be
208          * <b>null</b>, then this method has no effect.
209          *
210          * @param lookups the collection with lookups to be added
211          * @return a reference to this builder for method chaining
212          * @throws IllegalArgumentException if the collection contains <b>null</b> entries
213          */
214         public Builder withDefaultLookups(final Collection<? extends Lookup> lookups) {
215             if (lookups != null) {
216                 lookups.forEach(this::withDefaultLookup);
217             }
218             return this;
219         }
220 
221         /**
222          * Sets the {@code ConfigurationInterpolator} instance for the {@code InterpolatorSpecification}. This means that a
223          * {@code ConfigurationInterpolator} has been created and set up externally and can be used directly.
224          *
225          * @param ci the {@code ConfigurationInterpolator} (can be <b>null</b>)
226          * @return a reference to this builder for method chaining
227          */
228         public Builder withInterpolator(final ConfigurationInterpolator ci) {
229             interpolator = ci;
230             return this;
231         }
232 
233         /**
234          * Sets an optional parent {@code ConfigurationInterpolator}. If defined, this object is set as parent of a newly
235          * created {@code ConfigurationInterpolator} instance.
236          *
237          * @param parent the parent {@code ConfigurationInterpolator} (can be <b>null</b>)
238          * @return a reference to this builder for method chaining
239          */
240         public Builder withParentInterpolator(final ConfigurationInterpolator parent) {
241             parentInterpolator = parent;
242             return this;
243         }
244 
245         /**
246          * Sets the function used to convert interpolated values to strings. Pass {@code null}
247          * if the default conversion function is to be used.
248          *
249          * @param fn function used to convert interpolated values to string or {@code null} if the
250          *      default conversion function is to be used
251          * @return a reference to this builder for method chaining
252          */
253         public Builder withStringConverter(final Function<Object, String> fn) {
254             this.stringConverter = fn;
255             return this;
256         }
257 
258         /**
259          * Creates a new {@code InterpolatorSpecification} instance with the properties set so far. After that this builder
260          * instance is reset so that it can be reused for creating further specification objects.
261          *
262          * @return the newly created {@code InterpolatorSpecification}
263          */
264         public InterpolatorSpecification create() {
265             final InterpolatorSpecification spec = new InterpolatorSpecification(this);
266             reset();
267             return spec;
268         }
269 
270         /**
271          * Removes all data from this builder. Afterwards it can be used to define a brand new {@code InterpolatorSpecification}
272          * object.
273          */
274         public void reset() {
275             interpolator = null;
276             parentInterpolator = null;
277             prefixLookups.clear();
278             defLookups.clear();
279             stringConverter = null;
280         }
281 
282         /**
283          * Helper method for checking a lookup. Throws an exception if the lookup is <b>null</b>.
284          *
285          * @param lookup the lookup to be checked
286          * @throws IllegalArgumentException if the lookup is <b>null</b>
287          */
288         private static void checkLookup(final Lookup lookup) {
289             if (lookup == null) {
290                 throw new IllegalArgumentException("Lookup must not be null!");
291             }
292         }
293     }
294 }