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}