    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}