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