    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}