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 */
017
018package org.apache.commons.configuration2;
019
020import java.io.PrintStream;
021import java.io.PrintWriter;
022import java.io.StringWriter;
023import java.lang.reflect.InvocationTargetException;
024import java.lang.reflect.Method;
025import java.lang.reflect.Proxy;
026import java.util.Iterator;
027
028import org.apache.commons.configuration2.event.ConfigurationErrorEvent;
029import org.apache.commons.configuration2.event.Event;
030import org.apache.commons.configuration2.event.EventListener;
031import org.apache.commons.configuration2.event.EventSource;
032import org.apache.commons.configuration2.event.EventType;
033import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
034import org.apache.commons.configuration2.sync.NoOpSynchronizer;
035import org.apache.commons.configuration2.sync.Synchronizer;
036import org.apache.commons.configuration2.tree.ExpressionEngine;
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039
040/**
041 * Miscellaneous utility methods for configurations.
042 *
043 * @see ConfigurationConverter Utility methods to convert configurations.
044 *
045 */
046public final class ConfigurationUtils {
047    /** Constant for the name of the clone() method. */
048    private static final String METHOD_CLONE = "clone";
049
050    /**
051     * An array with interfaces to be implemented by a proxy for an immutable configuration.
052     */
053    private static final Class<?>[] IMMUTABLE_CONFIG_IFCS = {ImmutableConfiguration.class};
054
055    /**
056     * An array with interfaces to be implemented by a proxy for an immutable hierarchical configuration.
057     */
058    private static final Class<?>[] IMMUTABLE_HIERARCHICAL_CONFIG_IFCS = {ImmutableHierarchicalConfiguration.class};
059    /**
060     * A dummy event source that is returned by {@code asEventSource()} if a mock object has to be returned. It provides
061     * empty dummy implementations for all interface methods.
062     */
063    private static final EventSource DUMMY_EVENT_SOURCE = new EventSource() {
064
065        @Override
066        public <T extends Event> void addEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
067        }
068
069        @Override
070        public <T extends Event> boolean removeEventListener(final EventType<T> eventType, final EventListener<? super T> listener) {
071            return false;
072        }
073    };
074
075    /** The logger. */
076    private static final Log LOG = LogFactory.getLog(ConfigurationUtils.class);
077
078    /**
079     * Private constructor. Prevents instances from being created.
080     */
081    private ConfigurationUtils() {
082        // to prevent instantiation...
083    }
084
085    /**
086     * Dump the configuration key/value mappings to some ouput stream.
087     *
088     * @param configuration the configuration
089     * @param out the output stream to dump the configuration to
090     * @since 2.2
091     */
092    public static void dump(final ImmutableConfiguration configuration, final PrintStream out) {
093        dump(configuration, new PrintWriter(out));
094    }
095
096    /**
097     * Dump the configuration key/value mappings to some ouput stream. This version of the method exists only for backwards
098     * compatibility reason.
099     *
100     * @param configuration the configuration
101     * @param out the output stream to dump the configuration to
102     */
103    public static void dump(final Configuration configuration, final PrintStream out) {
104        dump((ImmutableConfiguration) configuration, out);
105    }
106
107    /**
108     * Dump the configuration key/value mappings to some writer.
109     *
110     * @param configuration the configuration
111     * @param out the writer to dump the configuration to
112     * @since 2.2
113     */
114    public static void dump(final ImmutableConfiguration configuration, final PrintWriter out) {
115        for (final Iterator<String> keys = configuration.getKeys(); keys.hasNext();) {
116            final String key = keys.next();
117            final Object value = configuration.getProperty(key);
118            out.print(key);
119            out.print("=");
120            out.print(value);
121
122            if (keys.hasNext()) {
123                out.println();
124            }
125        }
126
127        out.flush();
128    }
129
130    /**
131     * Dump the configuration key/value mappings to some writer. This version of the method exists only for backwards
132     * compatibility reason.
133     *
134     * @param configuration the configuration
135     * @param out the writer to dump the configuration to
136     */
137    public static void dump(final Configuration configuration, final PrintWriter out) {
138        dump((ImmutableConfiguration) configuration, out);
139    }
140
141    /**
142     * Get a string representation of the key/value mappings of a configuration.
143     *
144     * @param configuration the configuration
145     * @return a string representation of the configuration
146     * @since 2.2
147     */
148    public static String toString(final ImmutableConfiguration configuration) {
149        final StringWriter writer = new StringWriter();
150        dump(configuration, new PrintWriter(writer));
151        return writer.toString();
152    }
153
154    /**
155     * Get a string representation of the key/value mappings of a configuration. This version of the method exists only for
156     * backwards compatibility reason.
157     *
158     * @param configuration the configuration
159     * @return a string representation of the configuration
160     */
161    public static String toString(final Configuration configuration) {
162        return toString((ImmutableConfiguration) configuration);
163    }
164
165    /**
166     * <p>
167     * Copy all properties from the source configuration to the target configuration. Properties in the target configuration
168     * are replaced with the properties with the same key in the source configuration.
169     * </p>
170     * <p>
171     * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
172     * {@code AbstractConfiguration} (e.g. list delimiters). For a full support of all of these features the {@code copy()}
173     * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
174     * </p>
175     *
176     * @param source the source configuration
177     * @param target the target configuration
178     * @since 2.2
179     */
180    public static void copy(final ImmutableConfiguration source, final Configuration target) {
181        for (final Iterator<String> keys = source.getKeys(); keys.hasNext();) {
182            final String key = keys.next();
183            target.setProperty(key, source.getProperty(key));
184        }
185    }
186
187    /**
188     * <p>
189     * Copy all properties from the source configuration to the target configuration. Properties in the target configuration
190     * are replaced with the properties with the same key in the source configuration.
191     * </p>
192     * <p>
193     * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
194     * {@code AbstractConfiguration} (e.g. list delimiters). For a full support of all of these features the {@code copy()}
195     * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
196     * </p>
197     *
198     * @param source the source configuration
199     * @param target the target configuration
200     * @since 1.1
201     */
202    public static void copy(final Configuration source, final Configuration target) {
203        copy((ImmutableConfiguration) source, target);
204    }
205
206    /**
207     * <p>
208     * Append all properties from the source configuration to the target configuration. Properties in the source
209     * configuration are appended to the properties with the same key in the target configuration.
210     * </p>
211     * <p>
212     * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
213     * {@code AbstractConfiguration} (e.g. list delimiters). For a full support of all of these features the {@code copy()}
214     * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
215     * </p>
216     *
217     * @param source the source configuration
218     * @param target the target configuration
219     * @since 2.2
220     */
221    public static void append(final ImmutableConfiguration source, final Configuration target) {
222        for (final Iterator<String> keys = source.getKeys(); keys.hasNext();) {
223            final String key = keys.next();
224            target.addProperty(key, source.getProperty(key));
225        }
226    }
227
228    /**
229     * <p>
230     * Append all properties from the source configuration to the target configuration. Properties in the source
231     * configuration are appended to the properties with the same key in the target configuration.
232     * </p>
233     * <p>
234     * <em>Note:</em> This method is not able to handle some specifics of configurations derived from
235     * {@code AbstractConfiguration} (e.g. list delimiters). For a full support of all of these features the {@code copy()}
236     * method of {@code AbstractConfiguration} should be used. In a future release this method might become deprecated.
237     * </p>
238     *
239     * @param source the source configuration
240     * @param target the target configuration
241     * @since 1.1
242     */
243    public static void append(final Configuration source, final Configuration target) {
244        append((ImmutableConfiguration) source, target);
245    }
246
247    /**
248     * Converts the passed in configuration to a hierarchical one. If the configuration is already hierarchical, it is
249     * directly returned. Otherwise all properties are copied into a new hierarchical configuration.
250     *
251     * @param conf the configuration to convert
252     * @return the new hierarchical configuration (the result is <b>null</b> if and only if the passed in configuration is
253     *         <b>null</b>)
254     * @since 1.3
255     */
256    public static HierarchicalConfiguration<?> convertToHierarchical(final Configuration conf) {
257        return convertToHierarchical(conf, null);
258    }
259
260    /**
261     * Converts the passed in {@code Configuration} object to a hierarchical one using the specified
262     * {@code ExpressionEngine}. This conversion works by adding the keys found in the configuration to a newly created
263     * hierarchical configuration. When adding new keys to a hierarchical configuration the keys are interpreted by its
264     * {@code ExpressionEngine}. If they contain special characters (e.g. brackets) that are treated in a special way by the
265     * default expression engine, it may be necessary using a specific engine that can deal with such characters. Otherwise
266     * <b>null</b> can be passed in for the {@code ExpressionEngine}; then the default expression engine is used. If the
267     * passed in configuration is already hierarchical, it is directly returned. (However, the {@code ExpressionEngine} is
268     * set if it is not <b>null</b>.) Otherwise all properties are copied into a new hierarchical configuration.
269     *
270     * @param conf the configuration to convert
271     * @param engine the {@code ExpressionEngine} for the hierarchical configuration or <b>null</b> for the default
272     * @return the new hierarchical configuration (the result is <b>null</b> if and only if the passed in configuration is
273     *         <b>null</b>)
274     * @since 1.6
275     */
276    public static HierarchicalConfiguration<?> convertToHierarchical(final Configuration conf, final ExpressionEngine engine) {
277        if (conf == null) {
278            return null;
279        }
280
281        if (conf instanceof HierarchicalConfiguration) {
282            final HierarchicalConfiguration<?> hc = (HierarchicalConfiguration<?>) conf;
283            if (engine != null) {
284                hc.setExpressionEngine(engine);
285            }
286
287            return hc;
288        }
289        final BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration();
290        if (engine != null) {
291            hc.setExpressionEngine(engine);
292        }
293
294        // Per default, a DisabledListDelimiterHandler is set.
295        // So list delimiters in property values are not an issue.
296        hc.copy(conf);
297        return hc;
298    }
299
300    /**
301     * Clones the given configuration object if this is possible. If the passed in configuration object implements the
302     * {@code Cloneable} interface, its {@code clone()} method will be invoked. Otherwise an exception will be thrown.
303     *
304     * @param config the configuration object to be cloned (can be <b>null</b>)
305     * @return the cloned configuration (<b>null</b> if the argument was <b>null</b>, too)
306     * @throws ConfigurationRuntimeException if cloning is not supported for this object
307     * @since 1.3
308     */
309    public static Configuration cloneConfiguration(final Configuration config) throws ConfigurationRuntimeException {
310        if (config == null) {
311            return null;
312        }
313        try {
314            return (Configuration) clone(config);
315        } catch (final CloneNotSupportedException cnex) {
316            throw new ConfigurationRuntimeException(cnex);
317        }
318    }
319
320    /**
321     * Returns a clone of the passed in object if cloning is supported or the object itself if not. This method checks
322     * whether the passed in object implements the {@code Cloneable} interface. If this is the case, the {@code clone()}
323     * method is invoked. Otherwise, the object is directly returned. Errors that might occur during reflection calls are
324     * caught and also cause this method to return the original object.
325     *
326     * @param obj the object to be cloned
327     * @return the result of the cloning attempt
328     * @since 2.0
329     */
330    public static Object cloneIfPossible(final Object obj) {
331        try {
332            return clone(obj);
333        } catch (final Exception ex) {
334            return obj;
335        }
336    }
337
338    /**
339     * An internally used helper method for cloning objects. This implementation is not very sophisticated nor efficient.
340     * Maybe it can be replaced by an implementation from Commons Lang later. The method checks whether the passed in object
341     * implements the {@code Cloneable} interface. If this is the case, the {@code clone()} method is invoked by reflection.
342     * Errors that occur during the cloning process are re-thrown as runtime exceptions.
343     *
344     * @param obj the object to be cloned
345     * @return the cloned object
346     * @throws CloneNotSupportedException if the object cannot be cloned
347     */
348    static Object clone(final Object obj) throws CloneNotSupportedException {
349        if (obj instanceof Cloneable) {
350            try {
351                final Method m = obj.getClass().getMethod(METHOD_CLONE);
352                return m.invoke(obj);
353            } catch (final NoSuchMethodException nmex) {
354                throw new CloneNotSupportedException("No clone() method found for class" + obj.getClass().getName());
355            } catch (final IllegalAccessException | InvocationTargetException itex) {
356                throw new ConfigurationRuntimeException(itex);
357            }
358        }
359        throw new CloneNotSupportedException(obj.getClass().getName() + " does not implement Cloneable");
360    }
361
362    /**
363     * Creates a clone of the specified {@code Synchronizer}. This method can be called by {@code clone()} implementations
364     * in configuration classes that also need to copy the {@code Synchronizer} object. This method can handle some
365     * well-known {@code Synchronizer} implementations directly. For other classes, it uses the following algorithm:
366     * <ul>
367     * <li>If the class of the {@code Synchronizer} has a standard constructor, a new instance is created using
368     * reflection.</li>
369     * <li>If this is not possible, it is tried whether the object can be cloned.</li>
370     * </ul>
371     * If all attempts fail, a {@code ConfigurationRuntimeException} is thrown.
372     *
373     * @param sync the {@code Synchronizer} object to be cloned
374     * @return the clone of this {@code Synchronizer}
375     * @throws ConfigurationRuntimeException if no clone can be created
376     * @throws IllegalArgumentException if <b>null</b> is passed in
377     */
378    public static Synchronizer cloneSynchronizer(final Synchronizer sync) {
379        if (sync == null) {
380            throw new IllegalArgumentException("Synchronizer must not be null!");
381        }
382        if (NoOpSynchronizer.INSTANCE == sync) {
383            return sync;
384        }
385
386        try {
387            return sync.getClass().newInstance();
388        } catch (final Exception ex) {
389            LOG.info("Cannot create new instance of " + sync.getClass());
390        }
391
392        try {
393            return (Synchronizer) clone(sync);
394        } catch (final CloneNotSupportedException cnex) {
395            throw new ConfigurationRuntimeException("Cannot clone Synchronizer " + sync);
396        }
397    }
398
399    /**
400     * Enables runtime exceptions for the specified configuration object. This method can be used for configuration
401     * implementations that may face errors on normal property access, e.g. {@code DatabaseConfiguration} or
402     * {@code JNDIConfiguration}. Per default such errors are simply logged and then ignored. This implementation will
403     * register a special {@link EventListener} that throws a runtime exception (namely a
404     * {@code ConfigurationRuntimeException}) on each received error event.
405     *
406     * @param src the configuration, for which runtime exceptions are to be enabled; this configuration must implement
407     *        {@link EventSource}
408     */
409    public static void enableRuntimeExceptions(final Configuration src) {
410        if (!(src instanceof EventSource)) {
411            throw new IllegalArgumentException("Configuration must implement EventSource!");
412        }
413        ((EventSource) src).addEventListener(ConfigurationErrorEvent.ANY, event -> {
414            // Throw a runtime exception
415            throw new ConfigurationRuntimeException(event.getCause());
416        });
417    }
418
419    /**
420     * Loads the class with the given name. This method is used whenever a class has to be loaded dynamically. It first
421     * tries the current thread's context class loader. If this fails, the class loader of this class is tried.
422     *
423     * @param clsName the name of the class to be loaded
424     * @return the loaded class
425     * @throws ClassNotFoundException if the class cannot be resolved
426     * @since 2.0
427     */
428    public static Class<?> loadClass(final String clsName) throws ClassNotFoundException {
429        if (LOG.isDebugEnabled()) {
430            LOG.debug("Loading class " + clsName);
431        }
432
433        final ClassLoader cl = Thread.currentThread().getContextClassLoader();
434        try {
435            if (cl != null) {
436                return cl.loadClass(clsName);
437            }
438        } catch (final ClassNotFoundException cnfex) {
439            LOG.info("Could not load class " + clsName + " using CCL. Falling back to default CL.", cnfex);
440        }
441
442        return ConfigurationUtils.class.getClassLoader().loadClass(clsName);
443    }
444
445    /**
446     * Loads the class with the specified name re-throwing {@code ClassNotFoundException} exceptions as runtime exceptions.
447     * This method works like {@link #loadClass(String)}. However, checked exceptions are caught and re-thrown as
448     * {@code ConfigurationRuntimeException}.
449     *
450     * @param clsName the name of the class to be loaded
451     * @return the loaded class
452     * @throws ConfigurationRuntimeException if the class cannot be resolved
453     * @since 2.0
454     */
455    public static Class<?> loadClassNoEx(final String clsName) {
456        try {
457            return loadClass(clsName);
458        } catch (final ClassNotFoundException cnfex) {
459            throw new ConfigurationRuntimeException("Cannot load class " + clsName, cnfex);
460        }
461    }
462
463    /**
464     * Creates an {@code ImmutableConfiguration} from the given {@code Configuration} object. This method creates a proxy
465     * object wrapping the original configuration and making it available under the {@code ImmutableConfiguration}
466     * interface. Through this interface the configuration cannot be manipulated. It is also not possible to cast the
467     * returned object back to a {@code Configuration} instance to circumvent this protection.
468     *
469     * @param c the {@code Configuration} to be wrapped (must not be <b>null</b>)
470     * @return an {@code ImmutableConfiguration} view on the specified {@code Configuration} object
471     * @throws NullPointerException if the passed in {@code Configuration} is <b>null</b>
472     * @since 2.0
473     */
474    public static ImmutableConfiguration unmodifiableConfiguration(final Configuration c) {
475        return createUnmodifiableConfiguration(IMMUTABLE_CONFIG_IFCS, c);
476    }
477
478    /**
479     * Creates an {@code ImmutableHierarchicalConfiguration} from the given {@code HierarchicalConfiguration} object. This
480     * method works exactly like the method with the same name, but it operates on hierarchical configurations.
481     *
482     * @param c the {@code HierarchicalConfiguration} to be wrapped (must not be <b>null</b>)
483     * @return an {@code ImmutableHierarchicalConfiguration} view on the specified {@code HierarchicalConfiguration} object
484     * @throws NullPointerException if the passed in {@code HierarchicalConfiguration} is <b>null</b>
485     * @since 2.0
486     */
487    public static ImmutableHierarchicalConfiguration unmodifiableConfiguration(final HierarchicalConfiguration<?> c) {
488        return (ImmutableHierarchicalConfiguration) createUnmodifiableConfiguration(IMMUTABLE_HIERARCHICAL_CONFIG_IFCS, c);
489    }
490
491    /**
492     * Helper method for creating a proxy for an unmodifiable configuration. The interfaces the proxy should implement are
493     * passed as argument.
494     *
495     * @param ifcs an array with the interface classes the proxy must implement
496     * @param c the configuration object to be wrapped
497     * @return a proxy object for an immutable configuration
498     * @throws NullPointerException if the configuration is <b>null</b>
499     */
500    private static ImmutableConfiguration createUnmodifiableConfiguration(final Class<?>[] ifcs, final Configuration c) {
501        return (ImmutableConfiguration) Proxy.newProxyInstance(ConfigurationUtils.class.getClassLoader(), ifcs, new ImmutableConfigurationInvocationHandler(c));
502    }
503
504    /**
505     * Casts the specified object to an {@code EventSource} if possible. The boolean argument determines the method's
506     * behavior if the object does not implement the {@code EventSource} event: if set to <b>false</b>, a
507     * {@code ConfigurationRuntimeException} is thrown; if set to <b>true</b>, a dummy {@code EventSource} is returned; on
508     * this object all methods can be called, but they do not have any effect.
509     *
510     * @param obj the object to be cast as {@code EventSource}
511     * @param mockIfUnsupported a flag whether a mock object should be returned if necessary
512     * @return an {@code EventSource}
513     * @throws ConfigurationRuntimeException if the object cannot be cast to {@code EventSource} and the mock flag is
514     *         <b>false</b>
515     * @since 2.0
516     */
517    public static EventSource asEventSource(final Object obj, final boolean mockIfUnsupported) {
518        if (obj instanceof EventSource) {
519            return (EventSource) obj;
520        }
521
522        if (!mockIfUnsupported) {
523            throw new ConfigurationRuntimeException("Cannot cast to EventSource: " + obj);
524        }
525        return DUMMY_EVENT_SOURCE;
526    }
527}