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