View Javadoc

1   /*
2    * Copyright 2006-2016 The JGUIraffe Team.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.jguiraffe.gui.builder.components;
17  
18  import java.util.Collection;
19  import java.util.HashMap;
20  import java.util.LinkedList;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Set;
24  import java.util.Stack;
25  import java.util.concurrent.CopyOnWriteArrayList;
26  
27  import net.sf.jguiraffe.di.BeanContext;
28  import net.sf.jguiraffe.di.impl.DefaultBeanContext;
29  import net.sf.jguiraffe.di.impl.SimpleBeanStoreImpl;
30  import net.sf.jguiraffe.gui.builder.event.FormEventManager;
31  import net.sf.jguiraffe.gui.builder.event.PlatformEventManager;
32  import net.sf.jguiraffe.gui.forms.BindingStrategy;
33  import net.sf.jguiraffe.gui.forms.ComponentHandler;
34  import net.sf.jguiraffe.gui.forms.ComponentStore;
35  import net.sf.jguiraffe.gui.forms.FieldHandler;
36  import net.sf.jguiraffe.gui.forms.Form;
37  import net.sf.jguiraffe.transform.TransformerContext;
38  
39  import org.apache.commons.jelly.JellyContext;
40  
41  /**
42   * <p>
43   * A class for storing temporary data and the results of a form builder
44   * operation.
45   * </p>
46   * <p>
47   * For every call of a form builder an instance of this class is created. During
48   * the build process a couple of information is created. Some of this belongs to
49   * the final result, other parts need to be accessed later to resolve references
50   * or for different purposes. This class is a home for all these kinds of data.
51   * </p>
52   * <p>
53   * Especially the components created during a builder operation must be stored
54   * somewhere so that they can be combined to a resulting {@code Form}
55   * object. For this purpose this class provides an implementation of the
56   * {@code ComponentStore} interface and registers it as the default
57   * component store. To access the component store interested parties do not
58   * directly invoke the methods provided by {@code ComponentStore}, but
59   * access them through the {@code storeXXXX()} and {@code getXXXX()}
60   * methods defined in this class. These methods obtain the current store and
61   * delegate the call to it. During the builder operation a different
62   * {@code ComponentStore} can be temporarily set using the
63   * {@code pushComponentStore()} and {@code popComponentStore()}
64   * methods. This makes it possible for complex components to catch all the
65   * components created in their context; this way sub forms can be created that
66   * for instance represent a row in a table. With the
67   * {@code pushFormContext()} and {@code popFormContext()} methods a
68   * complete new form context can be created, i.e. all components will be added
69   * to the passed in form, and event registration can be performed on the
70   * components that belong to that form.
71   * </p>
72   * <p>
73   * To make the current form and all of its components available to the
74   * <em>dependency injection</em> framework, this class implements the
75   * {@link SimpleBeanStoreImpl.BeanContributor} interface and can therefore
76   * collaborate with a {@link SimpleBeanStoreImpl}. There is also a
77   * {@code getBeanContext()} method that returns a context for querying all
78   * available beans. Through this context the global beans (as defined by the
79   * application) can be queried and the objects created during the builder
80   * operation as well. To access the components and their associated handler
81   * classes the following naming scheme is used:
82   * <ul>
83   * <li>Components (i.e. the platform specific objects created by the
84   * {@code ComponentManager}) can be accessed using the name that is
85   * specified in the Jelly builder script. For instance (if
86   * {@code builderData} is an instance of {@code ComponentBuilderData})
87   * {@code builderData.getBeanContext().getBean("txtFirstName");} would
88   * return the input component associated with the name <em>txtFirstName</em>
89   * (probably a {@code JTextField} if Swing is used).</li>
90   * <li>For accessing the {@code ComponentHandler}s for the managed
91   * components the prefix <em>comp:</em>:
92   * {@code builderData.getBeanContext().getBean("comp:txtFirstName");} would
93   * return the {@code ComponentHandler} for the <em>txtFirstName</em>
94   * component.</li>
95   * <li>{@code FieldHandler}s are also accessed using a special prefix:
96   * <em>field:</em>. So for obtaining the {@code FieldHandler} for the
97   * <em>txtFirstName</em> field you would write
98   * {@code builderData.getBeanContext().getBean("field:txtFirstName");}.</li>
99   * <li>Each component is associated with a {@code WidgetHandler}. For
100  * obtaining a component's {@code WidgetHandler} the prefix
101  * <em>widget:</em> is used, as in
102  * {@code builderData.getBeanContext().getBean("widget:txtFirstName");}.</li>
103  * <li>The {@code Form} object constructed during the current builder
104  * operation can also be accessed under a special reserved key:
105  * {@code Form form = (Form) builderData.getBeanContext().getBean("CURRENT_FORM");}
106  * .</li>
107  * <li>The {@code BeanContext} maintained by this instance can also be
108  * accessed (e.g. to be injected into a bean defined by the current builder
109  * script). This can be done using the key <em>CURRENT_CONTEXT</em>.</li>
110  * <li>The current {@link net.sf.jguiraffe.gui.builder.BuilderData BuilderData}
111  * object is available under the key <em>BUILDER_DATA</em>.</li>
112  * <li>Finally the current {@code ComponentBuilderData} instance itself is
113  * exposed through the {@code BeanStore} implementation. The corresponding
114  * reserved key is named <em>COMPONENT_BUILDER_DATA</em>.</li>
115  * </ul>
116  * To avoid naming conflicts the identifiers for components in the builder
117  * scripts should be chosen in a way that they do not interfere with this
118  * reserved keys. Note that this class also defines constants for these keys.
119  * </p>
120  * <p>
121  * The current instance of this class for the running builder process is stored
122  * in the Jelly Context, where it can be accessed from all tags. From here also
123  * references to the factories needed for creating components can be obtained.
124  * The root container object is maintained by this object, too.
125  * </p>
126  * <p>
127  * Implementation note: This class is not thread-safe. If it is accessed
128  * concurrently by multiple threads, proper synchronization must be ensured.
129  * </p>
130  *
131  * @author Oliver Heger
132  * @version $Id: ComponentBuilderData.java 208 2012-02-11 20:57:33Z oheger $
133  */
134 public class ComponentBuilderData implements Composite,
135         SimpleBeanStoreImpl.BeanContributor
136 {
137     /**
138      * Constant for the prefix for accessing component handlers. This prefix has
139      * to be used for obtaining the {@code ComponentHandler} of a
140      * component from the bean context managed by this class.
141      */
142     public static final String KEY_COMPHANDLER_PREFIX = "comp";
143 
144     /**
145      * Constant for the prefix for accessing field handlers. This prefix has to
146      * be used for obtaining the {@code FieldHandler} of a component from
147      * the bean context managed by this class.
148      */
149     public static final String KEY_FIELDHANDLER_PREFIX = "field";
150 
151     /**
152      * Constant for the prefix for accessing widget handlers. This prefix has to
153      * be used for obtaining the {@code WidgetHandler} of a component
154      * from the bean context managed by this class.
155      */
156     public static final String KEY_WIDGETHANDLER_PREFIX = "widget";
157 
158     /**
159      * Constant for the key for accessing the current form from the bean context
160      * managed by this class.
161      */
162     public static final String KEY_FORM = "CURRENT_FORM";
163 
164     /**
165      * Constant for the key for accessing the current instance of this class
166      * from the managed bean context.
167      */
168     public static final String KEY_COMPONENT_BUILDER_DATA = "COMPONENT_BUILDER_DATA";
169 
170     /**
171      * Constant for the key for accessing the current bean context. This name
172      * can be used to inject a context into beans defined in a Jelly builder
173      * script.
174      */
175     public static final String KEY_CURRENT_CONTEXT = "CURRENT_CONTEXT";
176 
177     /**
178      * Constant for the key for accessing the current {@code BuilderData}
179      * object from the bean context managed by this class. The
180      * {@code BuilderData} object allows access to some important,
181      * application-global objects.
182      */
183     public static final String KEY_BUILDER_DATA = "BUILDER_DATA";
184 
185     /** Constant for the key prefix separator. */
186     static final char PREFIX_SEPARATOR = ':';
187 
188     /**
189      * Constant for the name under which an object is stored in the jelly
190      * context.
191      */
192     private static final String CTX_NAME = ComponentBuilderData.class.getName();
193 
194     /** Stores a reference to the component manager. */
195     private ComponentManager componentManager;
196 
197     /** Stores a reference to the field handler factory. */
198     private FieldHandlerFactory fieldHandlerFactory;
199 
200     /** The container selector used during the builder operation. */
201     private ContainerSelector containerSelector;
202 
203     /** A stack for managing the form-relevant information. */
204     private final Stack<FormContextData> formContextStack;
205 
206     /** A stack for storing the forms of the open form contexts. */
207     private final Stack<Form> formStack;
208 
209     /** Stores a map with the so far requested widget handlers. */
210     private final Map<Object, WidgetHandler> widgetHandlers;
211 
212     /** A list for the context listeners registered at this object. */
213     private final List<FormContextListener> contextListeners;
214 
215     /**
216      * Stores a reference to the main form object that is set up during the
217      * builder operation.
218      */
219     private Form form;
220 
221     /** Stores the bean context. */
222     private BeanContext beanContext;
223 
224     /** Stores the default resource group. */
225     private Object defaultResourceGroup;
226 
227     /** Stores the root container for this form building operation. */
228     private Object rootContainer;
229 
230     /** A map with the known event managers. */
231     private final Map<Form, FormEventManager> formEventManagers;
232 
233     /** Stores the current event manager object. */
234     private FormEventManager eventManager;
235 
236     /** Stores a reference to the platform event manager. */
237     private PlatformEventManager platformEventManager;
238 
239     /** Stores the tool tip manager. */
240     private ToolTipManager toolTipManager;
241 
242     /** Stores the name of the current builder. */
243     private String builderName;
244 
245     /** Stores the name of the current default button. */
246     private String defaultButtonName;
247 
248     /** A counter that determines whether callbacks are disabled. */
249     private int callBacksEnabledState;
250 
251     /**
252      * Creates a new instance of {@code ComponentBuilderData}.
253      */
254     public ComponentBuilderData()
255     {
256         formContextStack = new Stack<FormContextData>();
257         formStack = new Stack<Form>();
258         formEventManagers = new HashMap<Form, FormEventManager>();
259         widgetHandlers = new HashMap<Object, WidgetHandler>();
260         contextListeners = new CopyOnWriteArrayList<FormContextListener>();
261         containerSelector = new DefaultContainerSelector();
262     }
263 
264     /**
265      * Initializes the main form to be maintained by the {@code ComponentBuilderData}
266      * object. This method must be called after the construction of this object
267      * to explicitly initialize the {@code Form} object.
268      * @param tctx the {@code TransformerContext}
269      * @param strategy the {@code BindingStrategy} for the form
270      * @throws IllegalArgumentException if a required parameter is <b>null</b>
271      */
272     public void initializeForm(TransformerContext tctx, BindingStrategy strategy)
273     {
274         form = new Form(tctx, strategy);
275         // Open main form context
276         pushFormContext(form);
277     }
278 
279     /**
280      * Returns the name of the current builder.
281      *
282      * @return the builder name
283      */
284     public String getBuilderName()
285     {
286         return builderName;
287     }
288 
289     /**
290      * Sets the name of the current builder.
291      *
292      * @param builderName the builder name
293      */
294     public void setBuilderName(String builderName)
295     {
296         this.builderName = builderName;
297     }
298 
299     /**
300      * Returns the root container.
301      *
302      * @return the root container
303      */
304     public Object getRootContainer()
305     {
306         return rootContainer;
307     }
308 
309     /**
310      * Sets the root container. All component tags that are not nested inside a
311      * container tag will add their created objects to this container object.
312      *
313      * @param rootContainer the root container to use
314      */
315     public void setRootContainer(Object rootContainer)
316     {
317         this.rootContainer = rootContainer;
318     }
319 
320     /**
321      * Returns the component manager.
322      *
323      * @return the component manager
324      */
325     public ComponentManager getComponentManager()
326     {
327         return componentManager;
328     }
329 
330     /**
331      * Sets the component manager. This object will be used to create and
332      * manipulate GUI components.
333      *
334      * @param componentManager the component manager to use
335      */
336     public void setComponentManager(ComponentManager componentManager)
337     {
338         this.componentManager = componentManager;
339     }
340 
341     /**
342      * Returns the field handler factory.
343      *
344      * @return the field handler factory
345      */
346     public FieldHandlerFactory getFieldHandlerFactory()
347     {
348         return fieldHandlerFactory;
349     }
350 
351     /**
352      * Sets the field handler factory. This object is used by input component
353      * tags for creating the field handlers that are then passed to the internal
354      * form object.
355      *
356      * @param fieldHandlerFactory the handler factory
357      */
358     public void setFieldHandlerFactory(FieldHandlerFactory fieldHandlerFactory)
359     {
360         this.fieldHandlerFactory = fieldHandlerFactory;
361     }
362 
363     /**
364      * Returns the {@code ContainerSelector} used by this object.
365      *
366      * @return the {@code ContainerSelector}
367      * @since 1.3
368      */
369     public ContainerSelector getContainerSelector()
370     {
371         return containerSelector;
372     }
373 
374     /**
375      * Sets the {@code ContainerSelector} to be used by this object.
376      *
377      * @param containerSelector the {@code ContainerSelector}
378      * @since 1.3
379      */
380     public void setContainerSelector(ContainerSelector containerSelector)
381     {
382         this.containerSelector = containerSelector;
383     }
384 
385     /**
386      * Returns the default resource group.
387      *
388      * @return the default resource group
389      */
390     public Object getDefaultResourceGroup()
391     {
392         return defaultResourceGroup;
393     }
394 
395     /**
396      * Sets the default resource group. This group will be used if no specific
397      * group is specified in a resource request.
398      *
399      * @param defaultResourceGroup the default resource group
400      */
401     public void setDefaultResourceGroup(Object defaultResourceGroup)
402     {
403         this.defaultResourceGroup = defaultResourceGroup;
404     }
405 
406     /**
407      * Returns the {@code Form} object. This object is created during the builder
408      * process. It contains all fields that have been created so far. <strong>Note:</strong>
409      * Before calling this method {@code initializeForm()} must have been invoked;
410      * otherwise an exception is thrown.
411      *
412      * @return the {@code Form} object
413      * @throws IllegalStateException if the form has not yet been initialized
414      */
415     public Form getForm()
416     {
417         if (form == null)
418         {
419             throw new IllegalStateException("Form has not been initialized!"
420                     + "Call initializeForm() first.");
421         }
422         return form;
423     }
424 
425     /**
426      * Returns the transformer context.
427      *
428      * @return the transformer context
429      */
430     public TransformerContext getTransformerContext()
431     {
432         return getForm().getTransformerContext();
433     }
434 
435     /**
436      * Returns the {@code FormEventManager} used by this builder
437      * operation. This object can be used to register event handlers at
438      * components created during the building process.
439      *
440      * @return the event manager object
441      */
442     public FormEventManager getEventManager()
443     {
444         if (eventManager == null)
445         {
446             assert getContextForm() != null : "No context form set!";
447             eventManager = getEventManagerForForm(getContextForm());
448         }
449         return eventManager;
450     }
451 
452     /**
453      * Allows to set an event manager. All event handling logic, e.g.
454      * registering event listeners, will be done by this object. Normally it is
455      * not necessary to set a specific event manager; there is a default
456      * instance. This method is intended for complex components that need to
457      * hook into the event logic.
458      *
459      * @param evMan the new event manager to be set (can be <b>null</b>, then
460      * the default event manager for the current form will be set)
461      */
462     public void setEventManager(FormEventManager evMan)
463     {
464         eventManager = evMan;
465     }
466 
467     /**
468      * Returns the {@code ToolTipManager} associated with this object. If no
469      * specific {@code ToolTipManager} has been set, a default instance is
470      * created and returned.
471      *
472      * @return the {@code ToolTipManager}
473      */
474     public ToolTipManager getToolTipManager()
475     {
476         if (toolTipManager == null)
477         {
478             toolTipManager = createToolTipManager();
479         }
480 
481         return toolTipManager;
482     }
483 
484     /**
485      * Sets the {@code ToolTipManager} for this object. This {@code
486      * ToolTipManager} is then used for manipulating tool tips for components.
487      * Normally it is not necessary to set a specific tool tip manager. If none
488      * is set, a default instance is created. This method can be used to inject
489      * a custom tool tip manager.
490      *
491      * @param toolTipManager the {@code ToolTipManager} to be used
492      */
493     public void setToolTipManager(ToolTipManager toolTipManager)
494     {
495         this.toolTipManager = toolTipManager;
496     }
497 
498     /**
499      * Returns a reference to the current component store. This store will be
500      * used for searching and storing components.
501      *
502      * @return the currently used {@code ComponentStore}
503      */
504     public ComponentStore getComponentStore()
505     {
506         return fetchFormContextData().componentStore;
507     }
508 
509     /**
510      * Adds a new component store to this object that will replace the current
511      * store. All newly created components will be added to this store. It will
512      * be active until {@code popComponentStore()} is called, then the
513      * replaced component store will become the current store again. The purpose
514      * of this method is to allow complex tags to install their own store so
515      * that all components created in their context are put into this store.
516      * Thus it is possible to create sub forms or things like that. Call backs
517      * that are registered using the {@code addCallBack()} method will
518      * also be affected: they are always created in the context of the current
519      * component store and executed when {@code popComponentStore()} is
520      * invoked.
521      *
522      * @param store the new store (must not be <b>null</b>)
523      * @return the old active store; this store is replaced by the new one
524      * @throws IllegalArgumentException if the passed in store is <b>null</b>
525      * @see #popComponentStore()
526      */
527     public ComponentStore pushComponentStore(ComponentStore store)
528     {
529         if (store == null)
530         {
531             throw new IllegalArgumentException(
532                     "Component store must not be null!");
533         }
534         ComponentStore result = formContextStack.isEmpty() ? null
535                 : getComponentStore();
536         FormContextData fcd = new FormContextData(store);
537         formContextStack.push(fcd);
538         return result;
539     }
540 
541     /**
542      * Removes a component store from this object. This method is the counter
543      * part of {@code pushComponentStore()}. It removes the last pushed
544      * component store, making the store before to the current store again. If
545      * any call backs have been registered for the popped component store, they
546      * will now be invoked.
547      *
548      * @return the store that was removed
549      * @throws FormBuilderException if an error occurs when invoking call backs
550      * @throws java.util.EmptyStackException if there are no more stores to pop
551      */
552     public ComponentStore popComponentStore() throws FormBuilderException
553     {
554         invokeCallBacks();
555         FormContextData fcd = formContextStack.pop();
556         return fcd.getComponentStore();
557     }
558 
559     /**
560      * Returns the event manager for the specified form. An event manager is
561      * always associated with a {@code Form} object; it uses the form's
562      * {@link ComponentStore} for retrieving the components, for
563      * which event listeners are to be registered. With this method an event
564      * manager for a given form can be requested. If no such event manager
565      * exists, it will be created now. Per default there will be a single event
566      * manager for the main form constructed during the build process. However
567      * if complex components are involved that construct sub forms (which need
568      * their own event handling logic), it may be necessary to have a different
569      * event manager.
570      *
571      * @param f the form the event manager is associated with
572      * @return the event manager for this form
573      */
574     public FormEventManager getEventManagerForForm(Form f)
575     {
576         FormEventManager evMan = formEventManagers.get(f);
577         if (evMan == null)
578         {
579             evMan = createEventManager();
580             evMan.setComponentStore(f.getComponentStore());
581             formEventManagers.put(f, evMan);
582         }
583         return evMan;
584     }
585 
586     /**
587      * Adds a {@code FormContextListener} object to this data object. The
588      * listener receives notifications when a new form context is created or the
589      * current context is closed. This method can be called from an arbitrary
590      * thread
591      *
592      * @param listener the listener to be registered (must not be <b>null</b>)
593      * @throws IllegalArgumentException if the listener is <b>null</b>
594      * @since 1.3
595      */
596     public void addFormContextListener(FormContextListener listener)
597     {
598         if (listener == null)
599         {
600             throw new IllegalArgumentException("Listener must not be null!");
601         }
602         contextListeners.add(listener);
603     }
604 
605     /**
606      * Removes the specified {@code FormContextListener} from this object.
607      *
608      * @param listener the listener to be removed
609      * @since 1.3
610      */
611     public void removeFormContextListener(FormContextListener listener)
612     {
613         contextListeners.remove(listener);
614     }
615 
616     /**
617      * Installs a new form context for the specified form. Works like the method
618      * with the same name, but passes <b>null</b> for the source.
619      *
620      * @param f the sub form of the new form context (must not be <b>null</b>)
621      * @throws IllegalArgumentException if the form instance is <b>null</b>
622      * @see #pushComponentStore(ComponentStore)
623      * @see #getEventManagerForForm(Form)
624      */
625     public void pushFormContext(Form f)
626     {
627         pushFormContext(f, null);
628     }
629 
630     /**
631      * Installs a new form context for the specified form and passes information
632      * about the responsible source. This method can be called by complex
633      * components that create their own (sub) form instances. It has the
634      * following effect:
635      * <ul>
636      * <li>{@code pushComponentStore()} is called with the component store of
637      * the specified form. So newly created components will be added to this
638      * store.</li>
639      * <li>The event manager for this form is obtained using
640      * {@code getEventManagerForForm()} and made to the active event manager.
641      * This ensures that event listener registration logic for the sub form is
642      * handled by the appropriate event manager.</li>
643      * <li>Registered {@code FormContextListener} objects are notified about the
644      * newly created context.</li>
645      * </ul>
646      *
647      * @param form the sub form of the new form context (must not be
648      *        <b>null</b>)
649      * @param source the source object responsible for the form context
650      * @throws IllegalArgumentException if the form instance is <b>null</b>
651      * @see #pushComponentStore(ComponentStore)
652      * @see #getEventManagerForForm(Form)
653      * @since 1.3
654      */
655     public void pushFormContext(Form form, Object source)
656     {
657         if (form == null)
658         {
659             throw new IllegalArgumentException(
660                     "Form for context must not be null!");
661         }
662         formStack.push(form);
663         pushComponentStore(form.getComponentStore());
664         setEventManager(null);
665         fireFormContextCreated(form, source);
666     }
667 
668     /**
669      * Removes the outer most form context. Works like the method with the same
670      * name, but no information about a source is provided.
671      *
672      * @return the {@code Form} instance of the removed form context
673      * @throws FormBuilderException if an error occurs when closing the current
674      *         form context
675      * @throws IllegalStateException if {@code pushFormContext()} has not been
676      *         called before (and the context to be removed is the root context)
677      */
678     public Form popFormContext() throws FormBuilderException
679     {
680         return popFormContext(null);
681     }
682 
683     /**
684      * Removes the outer most form context passing in information about the
685      * responsible source. This method is the counter part of
686      * {@code pushFormContext()}. It makes the previous form to the active form
687      * again (and ensures that the correct component store and event manager are
688      * selected. This method must be called after processing of a sub form has
689      * completed. <em>Note:</em> This method calls {@code popComponentStore()}
690      * to make the component store of the previous form to the current one.
691      * Clients must be aware that the calls to the push and pop methods must be
692      * symmetric and correctly nested, otherwise the association between the
693      * current forms and their component stores may get lost!
694      *
695      * @param source the source object responsible for the form context
696      * @return the {@code Form} instance of the removed form context
697      * @throws FormBuilderException if an error occurs when closing the current
698      *         form context
699      * @throws IllegalStateException if {@code pushFormContext()} has not been
700      *         called before (and the context to be removed is the root context)
701      * @see #pushFormContext(Form, Object)
702      * @since 1.3
703      */
704     public Form popFormContext(Object source) throws FormBuilderException
705     {
706         if (formStack.size() <= 1)
707         {
708             // only root context present?
709             throw new IllegalStateException("Root context must not be closed!");
710         }
711 
712         Form result = formStack.pop();
713         popComponentStore();
714         setEventManager(null);
715         fireFormContextClosed(result, source);
716         return result;
717     }
718 
719     /**
720      * Returns the form of the current form context. While the
721      * {@code getForm()} method always returns the main form of this
722      * builder operation, this method takes the current form context into
723      * account, i.e. if {@code pushFormContext()} has been called before,
724      * the form passed to this method will be returned.
725      *
726      * @return the form of the current form context
727      * @see #pushFormContext(Form)
728      */
729     public Form getContextForm()
730     {
731         assert !formStack.isEmpty() : "No form context open!";
732         return formStack.peek();
733     }
734 
735     /**
736      * Stores the specified component in the current {@code ComponentStore}.
737      * From there it can be accessed e.g. if another component defines a
738      * reference to it.
739      *
740      * @param name the name of this component
741      * @param component the component itself
742      */
743     public void storeComponent(String name, Object component)
744     {
745         getComponentStore().add(name, component);
746     }
747 
748     /**
749      * Returns the component with the given name from the currently active
750      * {@code ComponentStore}. If no such component can be found, the
751      * method tries to find a component handler with this name and extract the
752      * component object from this handler. If this fails, too, <b>null</b> is
753      * returned.
754      *
755      * @param name the name of the desired component
756      * @return the component
757      */
758     public Object getComponent(String name)
759     {
760         Object component = getComponentStore().findComponent(name);
761         if (component == null)
762         {
763             ComponentHandler<?> handler = getComponentHandler(name);
764             return (handler != null) ? handler.getComponent() : null;
765         }
766         else
767         {
768             return component;
769         }
770     }
771 
772     /**
773      * Stores the given component handler in the current
774      * {@code ComponentStore}. From there it can later be accessed,
775      * which is useful if it is referenced by other tags.
776      *
777      * @param name the name of this component handler
778      * @param handler the handler itself
779      */
780     public void storeComponentHandler(String name, ComponentHandler<?> handler)
781     {
782         getComponentStore().addComponentHandler(name, handler);
783     }
784 
785     /**
786      * Returns the component handler with the specified name from the current
787      * {@code ComponentStore}. If no such handler can be found, return
788      * value is <b>null </b>.
789      *
790      * @param name the name of the desired handler
791      * @return the handler
792      */
793     public ComponentHandler<?> getComponentHandler(String name)
794     {
795         return getComponentStore().findComponentHandler(name);
796     }
797 
798     /**
799      * Stores the specified field handler. This field will be added to the
800      * internally maintained form object. The component that is associated with
801      * the field handler will also be accessible by the
802      * {@link #getComponent(String)} and
803      * {@link #getComponentHandler(String)} methods.
804      *
805      * @param name the name of the field
806      * @param fld the field handler
807      */
808     public void storeFieldHandler(String name, FieldHandler fld)
809     {
810         getComponentStore().addFieldHandler(name, fld);
811         storeComponentHandler(name, fld.getComponentHandler());
812     }
813 
814     /**
815      * Returns the field handler with the specified name from the current
816      * {@code ComponentStore} object. If no handler exists with this
817      * name, <b>null</b> is returned.
818      *
819      * @param name the name of the desired field handler
820      * @return the field handler with this name
821      */
822     public FieldHandler getFieldHandler(String name)
823     {
824         return getComponentStore().findFieldHandler(name);
825     }
826 
827     /**
828      * Returns a {@code WidgetHandler} for accessing the component with
829      * the given name. A component with this name is searched in the current
830      * {@code ComponentStore} object. If it cannot be found, <b>null</b>
831      * will be returned. Otherwise the current {@code ComponentManager}
832      * is asked to create a {@code WidgetHandler} object for this
833      * component. A once created {@code WidgetHandler} object will be
834      * cached, so that it can be directly returned if it is queried for the
835      * second time.
836      *
837      * @param name the name of the component
838      * @return a {@code WidgetHandler} object wrapping this component
839      */
840     public WidgetHandler getWidgetHandler(String name)
841     {
842         return getWidgetHandlerForComponent(getComponent(name));
843     }
844 
845     /**
846      * Returns a {@code WidgetHandler} object for the specified
847      * component. This method checks whether already a
848      * {@code WidgetHandler} for the passed in component has been created
849      * (by looking it up in the internal cache). If this is the case, it can be
850      * directly returned. Otherwise the current {@code ComponentManager}
851      * is asked to create a new {@code WidgetHandler} instance now. If
852      * the passed in component is <b>null</b>, <b>null</b> will be returned.
853      *
854      * @param component the component, for which a {@code WidgetHandler}
855      * is to be obtained
856      * @return the {@code WidgetHandler} for this component
857      */
858     public WidgetHandler getWidgetHandlerForComponent(Object component)
859     {
860         if (component == null)
861         {
862             return null;
863         }
864 
865         WidgetHandler wh = widgetHandlers.get(component);
866         if (wh == null)
867         {
868             wh = getComponentManager().getWidgetHandlerFor(component);
869             widgetHandlers.put(component, wh);
870         }
871         return wh;
872     }
873 
874     /**
875      * Adds the specified component to the root container. This method is called
876      * by component tags that are not nested inside container tags.
877      *
878      * @param comp the component to add
879      * @param constraints the constraints for this component
880      * @throws FormBuilderRuntimeException if no root container was set
881      */
882     public void addComponent(Object comp, Object constraints)
883             throws FormBuilderRuntimeException
884     {
885         if (getRootContainer() == null)
886         {
887             throw new FormBuilderRuntimeException("No root container was set!");
888         }
889         fetchComponentHandler().addContainerComponent(getRootContainer(), comp,
890                 constraints);
891     }
892 
893     /**
894      * Sets the layout for the root container. This method is called by layout
895      * tags that are not nested inside container tags.
896      *
897      * @param layout the layout object to set
898      */
899     public void setLayout(Object layout)
900     {
901         if (getRootContainer() == null)
902         {
903             throw new FormBuilderRuntimeException("No root container was set!");
904         }
905         fetchComponentHandler().setContainerLayout(getRootContainer(), layout);
906     }
907 
908     /**
909      * Returns the concrete container component. In this case this is the root
910      * container.
911      *
912      * @return the container component
913      */
914     public Object getContainer()
915     {
916         return getRootContainer();
917     }
918 
919     /**
920      * Disables the call back mechanism. Newly added callbacks are ignored and
921      * will not be executed by {@link #invokeCallBacks()}. Calls to this method
922      * can be nested. A corresponding number of {@link #enableCallBacks()} is
923      * necessary in order to enable callbacks again.
924      *
925      * @since 1.3
926      */
927     public void disableCallBacks()
928     {
929         callBacksEnabledState--;
930     }
931 
932     /**
933      * Enables the call back mechanism. This is the counter part of
934      * {@link #disableCallBacks()}.
935      *
936      * @since 1.3
937      */
938     public void enableCallBacks()
939     {
940         callBacksEnabledState++;
941     }
942 
943     /**
944      * Returns a flag whether the call back mechanism is currently enabled. If
945      * this method returns <b>false</b>, all callbacks added to this object are
946      * ignored.
947      *
948      * @return <b>true</b> if callbacks are enabled, <b>false</b> otherwise
949      * @since 1.3
950      */
951     public boolean isCallBacksEnabled()
952     {
953         return callBacksEnabledState >= 0;
954     }
955 
956     /**
957      * Registers the specified call back at this builder data object. It will be
958      * invoked after the building operation is complete for the current form
959      * context.
960      *
961      * @param callBack the call back object
962      * @param param a parameter object; this object is passed to the call back
963      *        when it is invoked
964      */
965     public void addCallBack(ComponentBuilderCallBack callBack, Object param)
966     {
967         if (isCallBacksEnabled())
968         {
969             fetchFormContextData().getCallBacks().add(
970                     new CallBackData(callBack, param));
971         }
972     }
973 
974     /**
975      * Invokes all call backs that are registered at this object for the current
976      * form context.
977      *
978      * @throws FormBuilderException if an exception is thrown by one of the call
979      * backs
980      */
981     public void invokeCallBacks() throws FormBuilderException
982     {
983         for (CallBackData cbd : fetchFormContextData().getCallBacks())
984         {
985             cbd.invokeCallBack(this);
986         }
987     }
988 
989     /**
990      * Returns a set with the names of all contained bean. This implementation
991      * returns the names of all stored components. If these components are
992      * associated with handlers, the correspondingly prefixed names are also
993      * contained in the set.
994      *
995      * @param names the set in which to store the names of the managed beans
996      */
997     public void beanNames(Set<String> names)
998     {
999         ComponentStore cStore = getComponentStore();
1000         appendNames(names, cStore.getComponentNames(), null);
1001         appendNames(names, cStore.getComponentNames(), KEY_WIDGETHANDLER_PREFIX);
1002         appendNames(names, cStore.getComponentHandlerNames(),
1003                 KEY_COMPHANDLER_PREFIX);
1004         appendNames(names, cStore.getFieldHandlerNames(),
1005                 KEY_FIELDHANDLER_PREFIX);
1006         names.add(KEY_FORM);
1007         names.add(KEY_COMPONENT_BUILDER_DATA);
1008     }
1009 
1010     /**
1011      * Returns the bean with the given name. This implementation supports the
1012      * names of the stored components. If for a component a
1013      * {@code ComponentHandler}, a {@code FieldHandler}, or a
1014      * {@code WidgetHandler} is available, the correspondingly prefixed
1015      * name is also supported. In addition the other reserved keys as described
1016      * in the header comment can be used.
1017      *
1018      * @param name the name of the desired bean provider
1019      * @return the bean with this name or <b>null</b>
1020      */
1021     public Object getBean(String name)
1022     {
1023         Object bean = null;
1024 
1025         if (KEY_COMPONENT_BUILDER_DATA.equals(name))
1026         {
1027             bean = this;
1028         }
1029         else if (KEY_FORM.equals(name))
1030         {
1031             bean = getForm();
1032         }
1033         else if (KEY_CURRENT_CONTEXT.equals(name))
1034         {
1035             bean = getBeanContext();
1036         }
1037 
1038         else
1039         {
1040             if (name != null)
1041             {
1042                 int pos = name.indexOf(PREFIX_SEPARATOR);
1043                 if (pos > 0)
1044                 {
1045                     String prefix = name.substring(0, pos);
1046                     String cname = name.substring(pos + 1);
1047                     bean = getPrefixedComponent(prefix, cname);
1048                 }
1049             }
1050 
1051             if (bean == null)
1052             {
1053                 // always check for a component with this name
1054                 bean = getComponent(name);
1055             }
1056         }
1057 
1058         return bean;
1059     }
1060 
1061     /**
1062      * Initializes the specified bean store object. This method is called by the
1063      * builder when the {@code BeanContext} used during the builder
1064      * operation is constructed. This implementation will add the static beans
1065      * to the given store and register this object as
1066      * {@code BeanContributor}.
1067      *
1068      * @param store the store to be initialized
1069      */
1070     public void initBeanStore(SimpleBeanStoreImpl store)
1071     {
1072         store.addBean(KEY_COMPONENT_BUILDER_DATA, this);
1073         store.addBeanContributor(this);
1074     }
1075 
1076     /**
1077      * Returns the {@code BeanContext} managed by this instance. If not
1078      * context has been set, a default context will be returned, which allows
1079      * access only to the beans defined in this object (i.e. the components
1080      * created during the builder operation and their handlers). Typically the
1081      * builder will create a context in the initialization phase of a builder
1082      * operation.
1083      *
1084      * @return the context maintained by this instance
1085      */
1086     public BeanContext getBeanContext()
1087     {
1088         if (beanContext == null)
1089         {
1090             SimpleBeanStoreImpl store = new SimpleBeanStoreImpl();
1091             initBeanStore(store);
1092             // create a default context
1093             beanContext = new DefaultBeanContext(store);
1094         }
1095         return beanContext;
1096     }
1097 
1098     /**
1099      * Allows to set a specific {@code BeanContext}. This is not necessary
1100      * normally, because the bean context is correctly set up automatically
1101      * taking account the appropriate hierarchy of contexts and bean stores.
1102      *
1103      * @param ctx the new bean context to be used
1104      */
1105     public void setBeanContext(BeanContext ctx)
1106     {
1107         beanContext = ctx;
1108     }
1109 
1110     /**
1111      * Returns the name of the default button. This can be <b>null</b> if no
1112      * default button has been set.
1113      *
1114      * @return the name of the default button
1115      */
1116     public String getDefaultButtonName()
1117     {
1118         return defaultButtonName;
1119     }
1120 
1121     /**
1122      * Sets the name of the default button. This method is called by a button
1123      * tag if the button is marked as default button of the current window.
1124      * Window tags can evaluate this property to decide whether some action is
1125      * necessary to actually make this button the window's default button.
1126      *
1127      * @param defaultButtonName the name of the default button; can be
1128      *        <b>null</b> to clear the default button
1129      */
1130     public void setDefaultButtonName(String defaultButtonName)
1131     {
1132         this.defaultButtonName = defaultButtonName;
1133     }
1134 
1135     /**
1136      * Stores this instance in the specified context. From there it can be
1137      * retrieved using the {@code get()} method.
1138      *
1139      * @param ctx the Jelly context (must not be <b>null</b>)
1140      * @throws IllegalArgumentException if the context is <b>null</b>
1141      */
1142     public void put(JellyContext ctx)
1143     {
1144         if (ctx == null)
1145         {
1146             throw new IllegalArgumentException("Context must not be null!");
1147         }
1148         ctx.setVariable(CTX_NAME, this);
1149     }
1150 
1151     /**
1152      * Returns the instance of this class stored in the specified Jelly context.
1153      * If no such instance can be found, <b>null</b> is returned.
1154      *
1155      * @param ctx the Jelly context
1156      * @return the instance of this class stored in this context
1157      */
1158     public static ComponentBuilderData get(JellyContext ctx)
1159     {
1160         return (ctx != null) ? (ComponentBuilderData) ctx
1161                 .findVariable(CTX_NAME) : null;
1162     }
1163 
1164     /**
1165      * Returns the associated component manager. If this object is not set, a
1166      * runtime exception will be thrown.
1167      *
1168      * @return the component manager
1169      * @throws FormBuilderRuntimeException if no component manager was set
1170      */
1171     protected ComponentManager fetchComponentHandler()
1172             throws FormBuilderRuntimeException
1173     {
1174         if (getComponentManager() == null)
1175         {
1176             throw new FormBuilderRuntimeException("No component manager set!");
1177         }
1178         return getComponentManager();
1179     }
1180 
1181     /**
1182      * Creates the event manager object. This method is called when the event
1183      * manager is accessed for the first time. It creates a new instance of
1184      * {@code FormEventManager} and initializes it with the platform
1185      * specific event manager obtained from the component manager.
1186      *
1187      * @return the new event manager
1188      * @throws FormBuilderRuntimeException if no component manager was set
1189      */
1190     protected FormEventManager createEventManager()
1191     {
1192         if (platformEventManager == null)
1193         {
1194             platformEventManager = createPlatformEventManager();
1195         }
1196 
1197         FormEventManager evMan = new FormEventManager(platformEventManager);
1198         return evMan;
1199     }
1200 
1201     /**
1202      * Creates the platform specific event manager. This method is called once
1203      * on first access to the platform event manager. This implementation
1204      * obtains the event manager from the component handler.
1205      *
1206      * @return the platform specific event manager
1207      * @throws FormBuilderRuntimeException if no component manager was set
1208      */
1209     protected PlatformEventManager createPlatformEventManager()
1210     {
1211         return fetchComponentHandler().createEventManager();
1212     }
1213 
1214     /**
1215      * Creates the {@code ToolTipManager}. This method is called when the
1216      * {@code ToolTipManager} is accessed for the first time, but no specific
1217      * instance has been set. This implementation creates a default tool tip
1218      * manager object.
1219      *
1220      * @return the new {@code ToolTipManager} instance
1221      */
1222     protected ToolTipManager createToolTipManager()
1223     {
1224         return new DefaultToolTipManager(this);
1225     }
1226 
1227     /**
1228      * Notifies registered listeners about a newly created form context.
1229      *
1230      * @param form the sub form of the new form context
1231      * @param source the source object responsible for the form context
1232      */
1233     private void fireFormContextCreated(Form form, Object source)
1234     {
1235         for (FormContextListener listener : contextListeners)
1236         {
1237             listener.formContextCreated(form, source);
1238         }
1239     }
1240 
1241     /**
1242      * Notifies registered listeners about a form context that has been closed.
1243      *
1244      * @param form the sub form of the closed form context
1245      * @param source the source object responsible for the form context
1246      */
1247     private void fireFormContextClosed(Form form, Object source)
1248     {
1249         for (FormContextListener listener : contextListeners)
1250         {
1251             listener.formContextClosed(form, source);
1252         }
1253     }
1254 
1255     /**
1256      * Returns a reference to the current form component data object that holds
1257      * information about the currently constructed form.
1258      *
1259      * @return the current form component data object
1260      */
1261     private FormContextData fetchFormContextData()
1262     {
1263         return formContextStack.peek();
1264     }
1265 
1266     /**
1267      * Tries to resolve the specified prefixed name.
1268      *
1269      * @param prefix the prefix
1270      * @param name the name
1271      * @return the corresponding component
1272      */
1273     private Object getPrefixedComponent(String prefix, String name)
1274     {
1275         Object comp = null;
1276 
1277         if (KEY_COMPHANDLER_PREFIX.equals(prefix))
1278         {
1279             comp = getComponentHandler(name);
1280         }
1281         else if (KEY_FIELDHANDLER_PREFIX.equals(prefix))
1282         {
1283             comp = getFieldHandler(name);
1284         }
1285         else if (KEY_WIDGETHANDLER_PREFIX.equals(prefix))
1286         {
1287             comp = getWidgetHandler(name);
1288         }
1289 
1290         return comp;
1291     }
1292 
1293     /**
1294      * Processes the names for elements with a given prefix and adds them to the
1295      * target name set.
1296      *
1297      * @param target the target name set
1298      * @param source the source name set
1299      * @param prefix the prefix to be used
1300      */
1301     private static void appendNames(Set<String> target, Set<String> source,
1302             String prefix)
1303     {
1304         for (String s : source)
1305         {
1306             target.add(prefixedName(prefix, s));
1307         }
1308     }
1309 
1310     /**
1311      * Creates a prefixed name to be used for the bean store implementation.
1312      *
1313      * @param prefix the prefix (can be <b>null</b>)
1314      * @param name the name
1315      * @return the prefixed name
1316      */
1317     private static String prefixedName(String prefix, String name)
1318     {
1319         if (prefix == null)
1320         {
1321             return name;
1322         }
1323 
1324         StringBuilder buf = new StringBuilder(prefix.length() + name.length()
1325                 + 1);
1326         buf.append(prefix).append(PREFIX_SEPARATOR).append(name);
1327         return buf.toString();
1328     }
1329 
1330     /**
1331      * A simple data class for storing information about registered call backs.
1332      */
1333     private static class CallBackData
1334     {
1335         /** Stores the call back. */
1336         private ComponentBuilderCallBack callBack;
1337 
1338         /** Stores the parameter for the call back. */
1339         private Object param;
1340 
1341         /**
1342          * Creates a new instance of {@code CallBackData} and initializes
1343          * it.
1344          *
1345          * @param cb the call back
1346          * @param p the parameter for the call back
1347          */
1348         public CallBackData(ComponentBuilderCallBack cb, Object p)
1349         {
1350             callBack = cb;
1351             param = p;
1352         }
1353 
1354         /**
1355          * Invokes the stored call back.
1356          *
1357          * @param data the builder data object
1358          * @throws FormBuilderException if the call back throws an exception
1359          */
1360         public void invokeCallBack(ComponentBuilderData data)
1361                 throws FormBuilderException
1362         {
1363             callBack.callBack(data, param);
1364         }
1365     }
1366 
1367     /**
1368      * A simple data class that stores all information needed for the currently
1369      * defined form. Typically only a single form is constructed during a
1370      * builder process. But there may be complex components that create sub
1371      * forms. In this case the relevant information about the current form is
1372      * packed in an instance of this class and pushed on a stack. After the
1373      * complex component and its sub form are completely processed the old state
1374      * is restored by popping the information back from the stack.
1375      */
1376     private static class FormContextData
1377     {
1378         /** Stores a reference to the component store associated with the form. */
1379         private final ComponentStore componentStore;
1380 
1381         /** A list for the registered call backs. */
1382         private final Collection<CallBackData> callBacks;
1383 
1384         /**
1385          * Creates a new instance of {@code FormComponentData} and sets
1386          * the component store. An empty collection for the call backs will also
1387          * be created.
1388          *
1389          * @param store the associated component store
1390          */
1391         public FormContextData(ComponentStore store)
1392         {
1393             componentStore = store;
1394             callBacks = new LinkedList<CallBackData>();
1395         }
1396 
1397         /**
1398          * Returns the {@code ComponentStore}.
1399          *
1400          * @return the {@code ComponentStore}
1401          */
1402         public ComponentStore getComponentStore()
1403         {
1404             return componentStore;
1405         }
1406 
1407         /**
1408          * Returns the collection with callback objects.
1409          *
1410          * @return the callBacks the collection with callback objects
1411          */
1412         public Collection<CallBackData> getCallBacks()
1413         {
1414             return callBacks;
1415         }
1416     }
1417 }