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