    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 */
    017package org.apache.commons.configuration2.builder;
    018
    019import java.util.Collection;
    020import java.util.Collections;
    021import java.util.HashMap;
    022import java.util.Iterator;
    023import java.util.Map;
    024
    025import org.apache.commons.configuration2.ConfigurationUtils;
    026import org.apache.commons.configuration2.ImmutableConfiguration;
    027import org.apache.commons.configuration2.Initializable;
    028import org.apache.commons.configuration2.beanutils.BeanDeclaration;
    029import org.apache.commons.configuration2.beanutils.BeanHelper;
    030import org.apache.commons.configuration2.beanutils.ConstructorArg;
    031import org.apache.commons.configuration2.event.Event;
    032import org.apache.commons.configuration2.event.EventListener;
    033import org.apache.commons.configuration2.event.EventListenerList;
    034import org.apache.commons.configuration2.event.EventListenerRegistrationData;
    035import org.apache.commons.configuration2.event.EventSource;
    036import org.apache.commons.configuration2.event.EventType;
    037import org.apache.commons.configuration2.ex.ConfigurationException;
    038import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
    039import org.apache.commons.configuration2.reloading.ReloadingController;
    040
    041/**
    042 * <p>
    043 * An implementation of the {@code ConfigurationBuilder} interface which is able
    044 * to create different concrete {@code ImmutableConfiguration} implementations based on
    045 * reflection.
    046 * </p>
    047 * <p>
    048 * When constructing an instance of this class the concrete
    049 * {@code ImmutableConfiguration} implementation class has to be provided. Then
    050 * properties for the new {@code ImmutableConfiguration} instance can be set. The first
    051 * call to {@code getConfiguration()} creates and initializes the new
    052 * {@code ImmutableConfiguration} object. It is cached and returned by subsequent calls.
    053 * This cache - and also the initialization properties set so far - can be
    054 * flushed by calling one of the {@code reset()} methods. That way other
    055 * {@code ImmutableConfiguration} instances with different properties can be created.
    056 * </p>
    057 * <p>
    058 * If the newly created {@code ImmutableConfiguration} object implements the
    059 * {@code Initializable} interface, its {@code initialize()} method is called
    060 * after all initialization properties have been set. This way a concrete
    061 * implementation class can perform arbitrary initialization steps.
    062 * </p>
    063 * <p>
    064 * There are multiple options for setting up a {@code BasicConfigurationBuilder}
    065 * instance:
    066 * </p>
    067 * <ul>
    068 * <li>All initialization properties can be set in one or multiple calls of the
    069 * {@code configure()} method. In each call an arbitrary number of
    070 * {@link BuilderParameters} objects can be passed. The API allows method
    071 * chaining and is intended to be used from Java code.</li>
    072 * <li>If builder instances are created by other means - e.g. using a dependency
    073 * injection framework -, the fluent API approach may not be suitable. For those
    074 * use cases it is also possible to pass in all initialization parameters as a
    075 * map. The keys of the map have to match initialization properties of the
    076 * {@code ImmutableConfiguration} object to be created, the values are the corresponding
    077 * property values. For instance, the key <em>throwExceptionOnMissing</em> in
    078 * the map will cause the method {@code setThrowExceptionOnMissing()} on the
    079 * {@code ImmutableConfiguration} object to be called with the corresponding value as
    080 * parameter.</li>
    081 * </ul>
    082 * <p>
    083 * A builder instance can be constructed with an <em>allowFailOnInit</em>
    084 * flag. If set to <strong>true</strong>, exceptions during initialization
    085 * of the configuration are ignored; in such a case an empty configuration
    086 * object is returned. A use case for this flag is a scenario in which a
    087 * configuration is optional and created on demand the first time configuration
    088 * data is to be stored. Consider an application that stores user-specific
    089 * configuration data in the user's home directory: When started for the first
    090 * time by a new user there is no configuration file; so it makes sense to
    091 * start with an empty configuration object. On application exit, settings
    092 * can be stored in this object and written to the associated file. Then they
    093 * are available on next application start.
    094 * </p>
    095 * <p>
    096 * This class is thread-safe. Multiple threads can modify initialization
    097 * properties and call {@code getConfiguration()}. However, the intended use
    098 * case is that the builder is configured by a single thread first. Then
    099 * {@code getConfiguration()} can be called concurrently, and it is guaranteed
    100 * that always the same {@code ImmutableConfiguration} instance is returned until the
    101 * builder is reset.
    102 * </p>
    103 *
    104 * @version $Id: BasicConfigurationBuilder.java 1842194 2018-09-27 22:24:23Z ggregory $
    105 * @since 2.0
    106 * @param <T> the concrete type of {@code ImmutableConfiguration} objects created by this
    107 *        builder
    108 */
    109public class BasicConfigurationBuilder<T extends ImmutableConfiguration> implements
    110        ConfigurationBuilder<T>
    111{
    112    /** The class of the objects produced by this builder instance. */
    113    private final Class<? extends T> resultClass;
    114
    115    /** An object managing the event listeners registered at this builder. */
    116    private final EventListenerList eventListeners;
    117
    118    /** A flag whether exceptions on initializing configurations are allowed. */
    119    private final boolean allowFailOnInit;
    120
    121    /** The map with current initialization parameters. */
    122    private Map<String, Object> parameters;
    123
    124    /** The current bean declaration. */
    125    private BeanDeclaration resultDeclaration;
    126
    127    /** The result object of this builder. */
    128    private volatile T result;
    129
    130    /**
    131     * Creates a new instance of {@code BasicConfigurationBuilder} and
    132     * initializes it with the given result class. No initialization properties
    133     * are set.
    134     *
    135     * @param resCls the result class (must not be <b>null</b>)
    136     * @throws IllegalArgumentException if the result class is <b>null</b>
    137     */
    138    public BasicConfigurationBuilder(final Class<? extends T> resCls)
    139    {
    140        this(resCls, null);
    141    }
    142
    143    /**
    144     * Creates a new instance of {@code BasicConfigurationBuilder} and
    145     * initializes it with the given result class and an initial set of builder
    146     * parameters. The <em>allowFailOnInit</em> flag is set to
    147     * <strong>false</strong>.
    148     *
    149     * @param resCls the result class (must not be <b>null</b>)
    150     * @param params a map with initialization parameters
    151     * @throws IllegalArgumentException if the result class is <b>null</b>
    152     */
    153    public BasicConfigurationBuilder(final Class<? extends T> resCls, final Map<String, Object> params)
    154    {
    155        this(resCls, params, false);
    156    }
    157
    158    /**
    159     * Creates a new instance of {@code BasicConfigurationBuilder} and
    160     * initializes it with the given result class, an initial set of builder
    161     * parameters, and the <em>allowFailOnInit</em> flag. The map with
    162     * parameters may be <b>null</b>, in this case no initialization parameters
    163     * are set.
    164     *
    165     * @param resCls the result class (must not be <b>null</b>)
    166     * @param params a map with initialization parameters
    167     * @param allowFailOnInit a flag whether exceptions on initializing a newly
    168     *        created {@code ImmutableConfiguration} object are allowed
    169     * @throws IllegalArgumentException if the result class is <b>null</b>
    170     */
    171    public BasicConfigurationBuilder(final Class<? extends T> resCls,
    172            final Map<String, Object> params, final boolean allowFailOnInit)
    173    {
    174        if (resCls == null)
    175        {
    176            throw new IllegalArgumentException("Result class must not be null!");
    177        }
    178
    179        resultClass = resCls;
    180        this.allowFailOnInit = allowFailOnInit;
    181        eventListeners = new EventListenerList();
    182        updateParameters(params);
    183    }
    184
    185    /**
    186     * Returns the result class of this builder. The objects produced by this
    187     * builder have the class returned here.
    188     *
    189     * @return the result class of this builder
    190     */
    191    public Class<? extends T> getResultClass()
    192    {
    193        return resultClass;
    194    }
    195
    196    /**
    197     * Returns the <em>allowFailOnInit</em> flag. See the header comment for
    198     * information about this flag.
    199     *
    200     * @return the <em>allowFailOnInit</em> flag
    201     */
    202    public boolean isAllowFailOnInit()
    203    {
    204        return allowFailOnInit;
    205    }
    206
    207    /**
    208     * Sets the initialization parameters of this builder. Already existing
    209     * parameters are replaced by the content of the given map.
    210     *
    211     * @param params the new initialization parameters of this builder; can be
    212     *        <b>null</b>, then all initialization parameters are removed
    213     * @return a reference to this builder for method chaining
    214     */
    215    public synchronized BasicConfigurationBuilder<T> setParameters(
    216            final Map<String, Object> params)
    217    {
    218        updateParameters(params);
    219        return this;
    220    }
    221
    222    /**
    223     * Adds the content of the given map to the already existing initialization
    224     * parameters.
    225     *
    226     * @param params the map with additional initialization parameters; may be
    227     *        <b>null</b>, then this call has no effect
    228     * @return a reference to this builder for method chaining
    229     */
    230    public synchronized BasicConfigurationBuilder<T> addParameters(
    231            final Map<String, Object> params)
    232    {
    233        final Map<String, Object> newParams =
    234                new HashMap<>(getParameters());
    235        if (params != null)
    236        {
    237            newParams.putAll(params);
    238        }
    239        updateParameters(newParams);
    240        return this;
    241    }
    242
    243    /**
    244     * Appends the content of the specified {@code BuilderParameters} objects to
    245     * the current initialization parameters. Calling this method multiple times
    246     * will create a union of the parameters provided.
    247     *
    248     * @param params an arbitrary number of objects with builder parameters
    249     * @return a reference to this builder for method chaining
    250     * @throws NullPointerException if a <b>null</b> array is passed
    251     */
    252    public BasicConfigurationBuilder<T> configure(final BuilderParameters... params)
    253    {
    254        final Map<String, Object> newParams = new HashMap<>();
    255        for (final BuilderParameters p : params)
    256        {
    257            newParams.putAll(p.getParameters());
    258            handleEventListenerProviders(p);
    259        }
    260
    261        return setParameters(newParams);
    262    }
    263
    264    /**
    265     * {@inheritDoc} This implementation creates the result configuration on
    266     * first access. Later invocations return the same object until this builder
    267     * is reset. The double-check idiom for lazy initialization is used (Bloch,
    268     * Effective Java, item 71).
    269     */
    270    @Override
    271    public T getConfiguration() throws ConfigurationException
    272    {
    273        fireBuilderEvent(new ConfigurationBuilderEvent(this,
    274                ConfigurationBuilderEvent.CONFIGURATION_REQUEST));
    275
    276        T resObj = result;
    277        boolean created = false;
    278        if (resObj == null)
    279        {
    280            synchronized (this)
    281            {
    282                resObj = result;
    283                if (resObj == null)
    284                {
    285                    result = resObj = createResult();
    286                    created = true;
    287                }
    288            }
    289        }
    290
    291        if (created)
    292        {
    293            fireBuilderEvent(new ConfigurationBuilderResultCreatedEvent(this,
    294                    ConfigurationBuilderResultCreatedEvent.RESULT_CREATED,
    295                    resObj));
    296        }
    297        return resObj;
    298    }
    299
    300    /**
    301     * {@inheritDoc} This implementation also takes care that the event listener
    302     * is added to the managed configuration object.
    303     *
    304     * @throws IllegalArgumentException if the event type or the listener is
    305     *         <b>null</b>
    306     */
    307    @Override
    308    public <E extends Event> void addEventListener(
    309            final EventType<E> eventType, final EventListener<? super E> listener)
    310    {
    311        installEventListener(eventType, listener);
    312    }
    313
    314    /**
    315     * {@inheritDoc} This implementation also takes care that the event listener
    316     * is removed from the managed configuration object.
    317     */
    318    @Override
    319    public <E extends Event> boolean removeEventListener(
    320            final EventType<E> eventType, final EventListener<? super E> listener)
    321    {
    322        fetchEventSource().removeEventListener(eventType, listener);
    323        return eventListeners.removeEventListener(eventType, listener);
    324    }
    325
    326    /**
    327     * Clears an existing result object. An invocation of this method causes a
    328     * new {@code ImmutableConfiguration} object to be created the next time
    329     * {@link #getConfiguration()} is called.
    330     */
    331    public void resetResult()
    332    {
    333        T oldResult;
    334        synchronized (this)
    335        {
    336            oldResult = result;
    337            result = null;
    338            resultDeclaration = null;
    339        }
    340
    341        if (oldResult != null)
    342        {
    343            removeEventListeners(oldResult);
    344        }
    345        fireBuilderEvent(new ConfigurationBuilderEvent(this,
    346                ConfigurationBuilderEvent.RESET));
    347    }
    348
    349    /**
    350     * Removes all initialization parameters of this builder. This method can be
    351     * called if this builder is to be reused for creating result objects with a
    352     * different configuration.
    353     */
    354    public void resetParameters()
    355    {
    356        setParameters(null);
    357    }
    358
    359    /**
    360     * Resets this builder. This is a convenience method which combines calls to
    361     * {@link #resetResult()} and {@link #resetParameters()}.
    362     */
    363    public synchronized void reset()
    364    {
    365        resetParameters();
    366        resetResult();
    367    }
    368
    369    /**
    370     * Connects this builder with a {@code ReloadingController}. With this
    371     * method support for reloading can be added to an arbitrary builder object.
    372     * Event listeners are registered at the reloading controller and this
    373     * builder with connect both objects:
    374     * <ul>
    375     * <li>When the reloading controller detects that a reload is required, the
    376     * builder's {@link #resetResult()} method is called; so the managed result
    377     * object is invalidated.</li>
    378     * <li>When a new result object has been created the controller's reloading
    379     * state is reset, so that new changes can be detected again.</li>
    380     * </ul>
    381     *
    382     * @param controller the {@code ReloadingController} to connect to (must not
    383     *        be <b>null</b>)
    384     * @throws IllegalArgumentException if the controller is <b>null</b>
    385     */
    386    public final void connectToReloadingController(
    387            final ReloadingController controller)
    388    {
    389        if (controller == null)
    390        {
    391            throw new IllegalArgumentException(
    392                    "ReloadingController must not be null!");
    393        }
    394        ReloadingBuilderSupportListener.connect(this, controller);
    395    }
    396
    397    /**
    398     * Creates a new, initialized result object. This method is called by
    399     * {@code getConfiguration()} if no valid result object exists. This base
    400     * implementation performs two steps:
    401     * <ul>
    402     * <li>{@code createResultInstance()} is called to create a new,
    403     * uninitialized result object.</li>
    404     * <li>{@code initResultInstance()} is called to process all initialization
    405     * parameters.</li>
    406     * </ul>
    407     * It also evaluates the <em>allowFailOnInit</em> flag, i.e. if
    408     * initialization causes an exception and this flag is set, the exception is
    409     * ignored, and the newly created, uninitialized configuration is returned.
    410     * Note that this method is called in a synchronized block.
    411     *
    412     * @return the newly created result object
    413     * @throws ConfigurationException if an error occurs
    414     */
    415    protected T createResult() throws ConfigurationException
    416    {
    417        final T resObj = createResultInstance();
    418
    419        try
    420        {
    421            initResultInstance(resObj);
    422        }
    423        catch (final ConfigurationException cex)
    424        {
    425            if (!isAllowFailOnInit())
    426            {
    427                throw cex;
    428            }
    429        }
    430
    431        return resObj;
    432    }
    433
    434    /**
    435     * Creates the new, uninitialized result object. This is the first step of
    436     * the process of producing a result object for this builder. This
    437     * implementation uses the {@link BeanHelper} class to create a new object
    438     * based on the {@link BeanDeclaration} returned by
    439     * {@link #getResultDeclaration()}. Note: This method is invoked in a
    440     * synchronized block.
    441     *
    442     * @return the newly created, yet uninitialized result object
    443     * @throws ConfigurationException if an exception occurs
    444     */
    445    protected T createResultInstance() throws ConfigurationException
    446    {
    447        final Object bean = fetchBeanHelper().createBean(getResultDeclaration());
    448        checkResultInstance(bean);
    449        return getResultClass().cast(bean);
    450    }
    451
    452    /**
    453     * Initializes a newly created result object. This is the second step of the
    454     * process of producing a result object for this builder. This
    455     * implementation uses the {@link BeanHelper} class to initialize the
    456     * object's property based on the {@link BeanDeclaration} returned by
    457     * {@link #getResultDeclaration()}. Note: This method is invoked in a
    458     * synchronized block. This is required because internal state is accessed.
    459     * Sub classes must not call this method without proper synchronization.
    460     *
    461     * @param obj the object to be initialized
    462     * @throws ConfigurationException if an error occurs
    463     */
    464    protected void initResultInstance(final T obj) throws ConfigurationException
    465    {
    466        fetchBeanHelper().initBean(obj, getResultDeclaration());
    467        registerEventListeners(obj);
    468        handleInitializable(obj);
    469    }
    470
    471    /**
    472     * Returns the {@code BeanDeclaration} that is used to create and initialize
    473     * result objects. The declaration is created on first access (by invoking
    474     * {@link #createResultDeclaration(Map)}) based on the current
    475     * initialization parameters.
    476     *
    477     * @return the {@code BeanDeclaration} for dynamically creating a result
    478     *         object
    479     * @throws ConfigurationException if an error occurs
    480     */
    481    protected final synchronized BeanDeclaration getResultDeclaration()
    482            throws ConfigurationException
    483    {
    484        if (resultDeclaration == null)
    485        {
    486            resultDeclaration = createResultDeclaration(getFilteredParameters());
    487        }
    488        return resultDeclaration;
    489    }
    490
    491    /**
    492     * Returns a (unmodifiable) map with the current initialization parameters
    493     * set for this builder. The map is populated with the parameters set using
    494     * the various configuration options.
    495     *
    496     * @return a map with the current set of initialization parameters
    497     */
    498    protected final synchronized Map<String, Object> getParameters()
    499    {
    500        if (parameters != null)
    501        {
    502            return parameters;
    503        }
    504        return Collections.emptyMap();
    505    }
    506
    507    /**
    508     * Obtains the {@code BeanHelper} object to be used when dealing with bean
    509     * declarations. This method checks whether this builder was configured with
    510     * a specific {@code BeanHelper} instance. If so, this instance is used.
    511     * Otherwise, the default {@code BeanHelper} is returned.
    512     *
    513     * @return the {@code BeanHelper} to be used
    514     */
    515    protected final BeanHelper fetchBeanHelper()
    516    {
    517        final BeanHelper helper =
    518                BasicBuilderParameters.fetchBeanHelper(getParameters());
    519        return (helper != null) ? helper : BeanHelper.INSTANCE;
    520    }
    521
    522    /**
    523     * Creates a new {@code BeanDeclaration} which is used for creating new
    524     * result objects dynamically. This implementation creates a specialized
    525     * {@code BeanDeclaration} object that is initialized from the given map of
    526     * initialization parameters. The {@code BeanDeclaration} must be
    527     * initialized with the result class of this builder, otherwise exceptions
    528     * will be thrown when the result object is created. Note: This method is
    529     * invoked in a synchronized block.
    530     *
    531     * @param params a snapshot of the current initialization parameters
    532     * @return the {@code BeanDeclaration} for creating result objects
    533     * @throws ConfigurationException if an error occurs
    534     */
    535    protected BeanDeclaration createResultDeclaration(
    536            final Map<String, Object> params) throws ConfigurationException
    537    {
    538        return new BeanDeclaration()
    539        {
    540            @Override
    541            public Map<String, Object> getNestedBeanDeclarations()
    542            {
    543                // no nested beans
    544                return Collections.emptyMap();
    545            }
    546
    547            @Override
    548            public Collection<ConstructorArg> getConstructorArgs()
    549            {
    550                // no constructor arguments
    551                return Collections.emptySet();
    552            }
    553
    554            @Override
    555            public Map<String, Object> getBeanProperties()
    556            {
    557                // the properties are equivalent to the parameters
    558                return params;
    559            }
    560
    561            @Override
    562            public Object getBeanFactoryParameter()
    563            {
    564                return null;
    565            }
    566
    567            @Override
    568            public String getBeanFactoryName()
    569            {
    570                return null;
    571            }
    572
    573            @Override
    574            public String getBeanClassName()
    575            {
    576                return getResultClass().getName();
    577            }
    578        };
    579    }
    580
    581    /**
    582     * Copies all {@code EventListener} objects registered at this builder to
    583     * the specified target configuration builder. This method is intended to be
    584     * used by derived classes which support inheritance of their properties to
    585     * other builder objects.
    586     *
    587     * @param target the target configuration builder (must not be <b>null</b>)
    588     * @throws NullPointerException if the target builder is <b>null</b>
    589     */
    590    protected synchronized void copyEventListeners(
    591            final BasicConfigurationBuilder<?> target)
    592    {
    593        copyEventListeners(target, eventListeners);
    594    }
    595
    596    /**
    597     * Copies all event listeners in the specified list to the specified target
    598     * configuration builder. This method is intended to be used by derived
    599     * classes which have to deal with managed configuration builders that need
    600     * to be initialized with event listeners.
    601     *
    602     * @param target the target configuration builder (must not be <b>null</b>)
    603     * @param listeners the event listeners to be copied over
    604     * @throws NullPointerException if the target builder is <b>null</b>
    605     */
    606    protected void copyEventListeners(final BasicConfigurationBuilder<?> target,
    607            final EventListenerList listeners)
    608    {
    609        target.eventListeners.addAll(listeners);
    610    }
    611
    612    /**
    613     * Adds the specified event listener to this object. This method is called
    614     * by {@code addEventListener()}, it does the actual listener registration.
    615     * Because it is final it can be called by sub classes in the constructor if
    616     * there is already the need to register an event listener.
    617     *
    618     * @param eventType the event type object
    619     * @param listener the listener to be registered
    620     * @param <E> the event type
    621     */
    622    protected final <E extends Event> void installEventListener(
    623            final EventType<E> eventType, final EventListener<? super E> listener)
    624    {
    625        fetchEventSource().addEventListener(eventType, listener);
    626        eventListeners.addEventListener(eventType, listener);
    627    }
    628
    629    /**
    630     * Sends the specified builder event to all registered listeners.
    631     *
    632     * @param event the event to be fired
    633     */
    634    protected void fireBuilderEvent(final ConfigurationBuilderEvent event)
    635    {
    636        eventListeners.fire(event);
    637    }
    638
    639    /**
    640     * Replaces the current map with parameters by a new one.
    641     *
    642     * @param newParams the map with new parameters (may be <b>null</b>)
    643     */
    644    private void updateParameters(final Map<String, Object> newParams)
    645    {
    646        final Map<String, Object> map = new HashMap<>();
    647        if (newParams != null)
    648        {
    649            map.putAll(newParams);
    650        }
    651        parameters = Collections.unmodifiableMap(map);
    652    }
    653
    654    /**
    655     * Registers the available event listeners at the given object. This method
    656     * is called for each result object created by the builder.
    657     *
    658     * @param obj the object to initialize
    659     */
    660    private void registerEventListeners(final T obj)
    661    {
    662        final EventSource evSrc = ConfigurationUtils.asEventSource(obj, true);
    663        for (final EventListenerRegistrationData<?> regData : eventListeners
    664                .getRegistrations())
    665        {
    666            registerListener(evSrc, regData);
    667        }
    668    }
    669
    670    /**
    671     * Removes all available event listeners from the given result object. This
    672     * method is called when the result of this builder is reset. Then the old
    673     * managed configuration should no longer generate events.
    674     *
    675     * @param obj the affected result object
    676     */
    677    private void removeEventListeners(final T obj)
    678    {
    679        final EventSource evSrc = ConfigurationUtils.asEventSource(obj, true);
    680        for (final EventListenerRegistrationData<?> regData : eventListeners
    681                .getRegistrations())
    682        {
    683            removeListener(evSrc, regData);
    684        }
    685    }
    686
    687    /**
    688     * Returns an {@code EventSource} for the current result object. If there is
    689     * no current result or if it does not extend {@code EventSource}, a dummy
    690     * event source is returned.
    691     *
    692     * @return the {@code EventSource} for the current result object
    693     */
    694    private EventSource fetchEventSource()
    695    {
    696        return ConfigurationUtils.asEventSource(result, true);
    697    }
    698
    699    /**
    700     * Checks whether the specified parameters object implements the
    701     * {@code EventListenerProvider} interface. If so, the event listeners it
    702     * provides are added to this builder.
    703     *
    704     * @param params the parameters object
    705     */
    706    private void handleEventListenerProviders(final BuilderParameters params)
    707    {
    708        if (params instanceof EventListenerProvider)
    709        {
    710            eventListeners.addAll(((EventListenerProvider) params)
    711                    .getListeners());
    712        }
    713    }
    714
    715    /**
    716     * Checks whether the class of the result configuration is compatible with
    717     * this builder's result class. This is done to ensure that only objects of
    718     * the expected result class are created.
    719     *
    720     * @param inst the result instance to be checked
    721     * @throws ConfigurationRuntimeException if an invalid result class is
    722     *         detected
    723     */
    724    private void checkResultInstance(final Object inst)
    725    {
    726        if (!getResultClass().isInstance(inst))
    727        {
    728            throw new ConfigurationRuntimeException(
    729                    "Incompatible result object: " + inst);
    730        }
    731    }
    732
    733    /**
    734     * Returns a map with initialization parameters where all parameters
    735     * starting with the reserved prefix have been filtered out.
    736     *
    737     * @return the filtered parameters map
    738     */
    739    private Map<String, Object> getFilteredParameters()
    740    {
    741        final Map<String, Object> filteredMap =
    742                new HashMap<>(getParameters());
    743        for (final Iterator<String> it = filteredMap.keySet().iterator(); it
    744                .hasNext();)
    745        {
    746            final String key = it.next();
    747            if (key.startsWith(BuilderParameters.RESERVED_PARAMETER_PREFIX))
    748            {
    749                it.remove();
    750            }
    751        }
    752        return filteredMap;
    753    }
    754
    755    /**
    756     * Performs special initialization of the result object. This method is
    757     * called after parameters have been set on a newly created result instance.
    758     * If supported by the result class, the {@code initialize()} method is now
    759     * called.
    760     *
    761     * @param obj the newly created result object
    762     */
    763    private void handleInitializable(final T obj)
    764    {
    765        if (obj instanceof Initializable)
    766        {
    767            ((Initializable) obj).initialize();
    768        }
    769    }
    770
    771    /**
    772     * Registers an event listener at an event source object.
    773     *
    774     * @param evSrc the event source
    775     * @param regData the registration data object
    776     * @param <E> the type of the event listener
    777     */
    778    private static <E extends Event> void registerListener(final EventSource evSrc,
    779            final EventListenerRegistrationData<E> regData)
    780    {
    781        evSrc.addEventListener(regData.getEventType(), regData.getListener());
    782    }
    783
    784    /**
    785     * Removes an event listener from an event source object.
    786     *
    787     * @param evSrc the event source
    788     * @param regData the registration data object
    789     * @param <E> the type of the event listener
    790     */
    791    private static <E extends Event> void removeListener(final EventSource evSrc,
    792            final EventListenerRegistrationData<E> regData)
    793    {
    794        evSrc.removeEventListener(regData.getEventType(), regData.getListener());
    795    }
    796}