    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}