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