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