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