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