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 1785888 2017-03-07 21:05:35Z oheger $
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(EventType<T> eventType,
079                EventListener<? super T> listener)
080        {
081        }
082
083        @Override
084        public <T extends Event> boolean removeEventListener(
085                EventType<T> eventType, 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(ImmutableConfiguration configuration, 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(Configuration configuration, 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(ImmutableConfiguration configuration, PrintWriter out)
134    {
135        for (Iterator<String> keys = configuration.getKeys(); keys.hasNext();)
136        {
137            String key = keys.next();
138            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(Configuration configuration, 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(ImmutableConfiguration configuration)
173    {
174        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(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(ImmutableConfiguration source, Configuration target)
207    {
208        for (Iterator<String> keys = source.getKeys(); keys.hasNext();)
209        {
210            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(Configuration source, 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(ImmutableConfiguration source, Configuration target)
249    {
250        for (Iterator<String> keys = source.getKeys(); keys.hasNext();)
251        {
252            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(Configuration source, 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            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            Configuration conf, ExpressionEngine engine)
317    {
318        if (conf == null)
319        {
320            return null;
321        }
322
323        if (conf instanceof HierarchicalConfiguration)
324        {
325            HierarchicalConfiguration<?> hc = (HierarchicalConfiguration<?>) conf;
326            if (engine != null)
327            {
328                hc.setExpressionEngine(engine);
329            }
330
331            return hc;
332        }
333        else
334        {
335            BaseHierarchicalConfiguration hc = new BaseHierarchicalConfiguration();
336            if (engine != null)
337            {
338                hc.setExpressionEngine(engine);
339            }
340
341            // Per default, a DisabledListDelimiterHandler is set.
342            // So list delimiters in property values are not an issue.
343            hc.copy(conf);
344            return hc;
345        }
346    }
347
348    /**
349     * Clones the given configuration object if this is possible. If the passed
350     * in configuration object implements the {@code Cloneable}
351     * interface, its {@code clone()} method will be invoked. Otherwise
352     * an exception will be thrown.
353     *
354     * @param config the configuration object to be cloned (can be <b>null</b>)
355     * @return the cloned configuration (<b>null</b> if the argument was
356     * <b>null</b>, too)
357     * @throws ConfigurationRuntimeException if cloning is not supported for
358     * this object
359     * @since 1.3
360     */
361    public static Configuration cloneConfiguration(Configuration config)
362            throws ConfigurationRuntimeException
363    {
364        if (config == null)
365        {
366            return null;
367        }
368        else
369        {
370            try
371            {
372                return (Configuration) clone(config);
373            }
374            catch (CloneNotSupportedException cnex)
375            {
376                throw new ConfigurationRuntimeException(cnex);
377            }
378        }
379    }
380
381    /**
382     * Returns a clone of the passed in object if cloning is supported or the
383     * object itself if not. This method checks whether the passed in object
384     * implements the {@code Cloneable} interface. If this is the case, the
385     * {@code clone()} method is invoked. Otherwise, the object is directly
386     * returned. Errors that might occur during reflection calls are caught and
387     * also cause this method to return the original object.
388     *
389     * @param obj the object to be cloned
390     * @return the result of the cloning attempt
391     * @since 2.0
392     */
393    public static Object cloneIfPossible(Object obj)
394    {
395        try
396        {
397            return clone(obj);
398        }
399        catch (Exception ex)
400        {
401            return obj;
402        }
403    }
404
405    /**
406     * An internally used helper method for cloning objects. This implementation
407     * is not very sophisticated nor efficient. Maybe it can be replaced by an
408     * implementation from Commons Lang later. The method checks whether the
409     * passed in object implements the {@code Cloneable} interface. If
410     * this is the case, the {@code clone()} method is invoked by
411     * reflection. Errors that occur during the cloning process are re-thrown as
412     * runtime exceptions.
413     *
414     * @param obj the object to be cloned
415     * @return the cloned object
416     * @throws CloneNotSupportedException if the object cannot be cloned
417     */
418    static Object clone(Object obj) throws CloneNotSupportedException
419    {
420        if (obj instanceof Cloneable)
421        {
422            try
423            {
424                Method m = obj.getClass().getMethod(METHOD_CLONE);
425                return m.invoke(obj);
426            }
427            catch (NoSuchMethodException nmex)
428            {
429                throw new CloneNotSupportedException(
430                        "No clone() method found for class"
431                                + obj.getClass().getName());
432            }
433            catch (IllegalAccessException iaex)
434            {
435                throw new ConfigurationRuntimeException(iaex);
436            }
437            catch (InvocationTargetException itex)
438            {
439                throw new ConfigurationRuntimeException(itex);
440            }
441        }
442        else
443        {
444            throw new CloneNotSupportedException(obj.getClass().getName()
445                    + " does not implement Cloneable");
446        }
447    }
448
449    /**
450     * Creates a clone of the specified {@code Synchronizer}. This method can be
451     * called by {@code clone()} implementations in configuration classes that
452     * also need to copy the {@code Synchronizer} object. This method can handle
453     * some well-known {@code Synchronizer} implementations directly. For other
454     * classes, it uses the following algorithm:
455     * <ul>
456     * <li>If the class of the {@code Synchronizer} has a standard constructor,
457     * a new instance is created using reflection.</li>
458     * <li>If this is not possible, it is tried whether the object can be
459     * cloned.</li>
460     * </ul>
461     * If all attempts fail, a {@code ConfigurationRuntimeException} is thrown.
462     *
463     * @param sync the {@code Synchronizer} object to be cloned
464     * @return the clone of this {@code Synchronizer}
465     * @throws ConfigurationRuntimeException if no clone can be created
466     * @throws IllegalArgumentException if <b>null</b> is passed in
467     */
468    public static Synchronizer cloneSynchronizer(Synchronizer sync)
469    {
470        if (sync == null)
471        {
472            throw new IllegalArgumentException("Synchronizer must not be null!");
473        }
474        if (NoOpSynchronizer.INSTANCE == sync)
475        {
476            return sync;
477        }
478
479        try
480        {
481            return sync.getClass().newInstance();
482        }
483        catch (Exception ex)
484        {
485            LOG.info("Cannot create new instance of " + sync.getClass());
486        }
487
488        try
489        {
490            return (Synchronizer) clone(sync);
491        }
492        catch (CloneNotSupportedException cnex)
493        {
494            throw new ConfigurationRuntimeException(
495                    "Cannot clone Synchronizer " + sync);
496        }
497    }
498
499    /**
500     * Enables runtime exceptions for the specified configuration object. This
501     * method can be used for configuration implementations that may face errors
502     * on normal property access, e.g. {@code DatabaseConfiguration} or
503     * {@code JNDIConfiguration}. Per default such errors are simply
504     * logged and then ignored. This implementation will register a special
505     * {@link EventListener} that throws a runtime
506     * exception (namely a {@code ConfigurationRuntimeException}) on
507     * each received error event.
508     *
509     * @param src the configuration, for which runtime exceptions are to be
510     * enabled; this configuration must implement {@link EventSource}
511     */
512    public static void enableRuntimeExceptions(Configuration src)
513    {
514        if (!(src instanceof EventSource))
515        {
516            throw new IllegalArgumentException(
517                    "Configuration must implement EventSource!");
518        }
519        ((EventSource) src).addEventListener(ConfigurationErrorEvent.ANY,
520                new EventListener<ConfigurationErrorEvent>()
521                {
522                    @Override
523                    public void onEvent(ConfigurationErrorEvent event)
524                    {
525                        // Throw a runtime exception
526                        throw new ConfigurationRuntimeException(event
527                                .getCause());
528                    }
529                });
530    }
531
532    /**
533     * Loads the class with the given name. This method is used whenever a class
534     * has to be loaded dynamically. It first tries the current thread's context
535     * class loader. If this fails, the class loader of this class is tried.
536     *
537     * @param clsName the name of the class to be loaded
538     * @return the loaded class
539     * @throws ClassNotFoundException if the class cannot be resolved
540     * @since 2.0
541     */
542    public static Class<?> loadClass(String clsName)
543            throws ClassNotFoundException
544    {
545        if (LOG.isDebugEnabled())
546        {
547            LOG.debug("Loading class " + clsName);
548        }
549
550        ClassLoader cl = Thread.currentThread().getContextClassLoader();
551        try
552        {
553            if (cl != null)
554            {
555                return cl.loadClass(clsName);
556            }
557        }
558        catch (ClassNotFoundException cnfex)
559        {
560            LOG.info("Could not load class " + clsName
561                    + " using CCL. Falling back to default CL.", cnfex);
562        }
563
564        return ConfigurationUtils.class.getClassLoader().loadClass(clsName);
565    }
566
567    /**
568     * Loads the class with the specified name re-throwing
569     * {@code ClassNotFoundException} exceptions as runtime exceptions. This
570     * method works like {@link #loadClass(String)}. However, checked exceptions
571     * are caught and re-thrown as {@code ConfigurationRuntimeException}.
572     *
573     * @param clsName the name of the class to be loaded
574     * @return the loaded class
575     * @throws ConfigurationRuntimeException if the class cannot be resolved
576     * @since 2.0
577     */
578    public static Class<?> loadClassNoEx(String clsName)
579    {
580        try
581        {
582            return loadClass(clsName);
583        }
584        catch (ClassNotFoundException cnfex)
585        {
586            throw new ConfigurationRuntimeException("Cannot load class "
587                    + clsName, cnfex);
588        }
589    }
590
591    /**
592     * Creates an {@code ImmutableConfiguration} from the given
593     * {@code Configuration} object. This method creates a proxy object wrapping
594     * the original configuration and making it available under the
595     * {@code ImmutableConfiguration} interface. Through this interface the
596     * configuration cannot be manipulated. It is also not possible to cast the
597     * returned object back to a {@code Configuration} instance to circumvent
598     * this protection.
599     *
600     * @param c the {@code Configuration} to be wrapped (must not be
601     *        <b>null</b>)
602     * @return an {@code ImmutableConfiguration} view on the specified
603     *         {@code Configuration} object
604     * @throws NullPointerException if the passed in {@code Configuration} is
605     *         <b>null</b>
606     * @since 2.0
607     */
608    public static ImmutableConfiguration unmodifiableConfiguration(
609            Configuration c)
610    {
611        return createUnmodifiableConfiguration(IMMUTABLE_CONFIG_IFCS, c);
612    }
613
614    /**
615     * Creates an {@code ImmutableHierarchicalConfiguration} from the given
616     * {@code HierarchicalConfiguration} object. This method works exactly like
617     * the method with the same name, but it operates on hierarchical
618     * configurations.
619     *
620     * @param c the {@code HierarchicalConfiguration} to be wrapped (must not be
621     *        <b>null</b>)
622     * @return an {@code ImmutableHierarchicalConfiguration} view on the
623     *         specified {@code HierarchicalConfiguration} object
624     * @throws NullPointerException if the passed in
625     *         {@code HierarchicalConfiguration} is <b>null</b>
626     * @since 2.0
627     */
628    public static ImmutableHierarchicalConfiguration unmodifiableConfiguration(
629            HierarchicalConfiguration<?> c)
630    {
631        return (ImmutableHierarchicalConfiguration) createUnmodifiableConfiguration(
632                IMMUTABLE_HIERARCHICAL_CONFIG_IFCS, c);
633    }
634
635    /**
636     * Helper method for creating a proxy for an unmodifiable configuration. The
637     * interfaces the proxy should implement are passed as argument.
638     *
639     * @param ifcs an array with the interface classes the proxy must implement
640     * @param c the configuration object to be wrapped
641     * @return a proxy object for an immutable configuration
642     * @throws NullPointerException if the configuration is <b>null</b>
643     */
644    private static ImmutableConfiguration createUnmodifiableConfiguration(
645            Class<?>[] ifcs, Configuration c)
646    {
647        return (ImmutableConfiguration) Proxy.newProxyInstance(
648                ConfigurationUtils.class.getClassLoader(), ifcs,
649                new ImmutableConfigurationInvocationHandler(c));
650    }
651
652    /**
653     * Casts the specified object to an {@code EventSource} if possible. The
654     * boolean argument determines the method's behavior if the object does not
655     * implement the {@code EventSource} event: if set to <b>false</b>, a
656     * {@code ConfigurationRuntimeException} is thrown; if set to <b>true</b>, a
657     * dummy {@code EventSource} is returned; on this object all methods can be
658     * called, but they do not have any effect.
659     *
660     * @param obj the object to be cast as {@code EventSource}
661     * @param mockIfUnsupported a flag whether a mock object should be returned
662     *        if necessary
663     * @return an {@code EventSource}
664     * @throws ConfigurationRuntimeException if the object cannot be cast to
665     *         {@code EventSource} and the mock flag is <b>false</b>
666     * @since 2.0
667     */
668    public static EventSource asEventSource(Object obj,
669            boolean mockIfUnsupported)
670    {
671        if (obj instanceof EventSource)
672        {
673            return (EventSource) obj;
674        }
675
676        if (!mockIfUnsupported)
677        {
678            throw new ConfigurationRuntimeException(
679                    "Cannot cast to EventSource: " + obj);
680        }
681        return DUMMY_EVENT_SOURCE;
682    }
683}