View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.orchestra.dynaForm.jsf.component;
20  
21  import org.apache.myfaces.orchestra.dynaForm.jsf.guiBuilder.Slipstream;
22  import org.apache.myfaces.orchestra.dynaForm.jsf.guiBuilder.impl.jsf.JsfGuiBuilder;
23  import org.apache.myfaces.orchestra.dynaForm.jsf.guiBuilder.impl.jsf.JsfGuiBuilderFactory;
24  import org.apache.myfaces.orchestra.dynaForm.jsf.guiBuilder.impl.jsf.NewComponentListener;
25  import org.apache.myfaces.orchestra.dynaForm.lib.ViewType;
26  import org.apache.myfaces.orchestra.dynaForm.lib._FacesUtils;
27  import org.apache.myfaces.orchestra.dynaForm.metadata.Extractor;
28  import org.apache.myfaces.orchestra.dynaForm.metadata.MetaData;
29  import org.apache.myfaces.orchestra.dynaForm.metadata.MetaField;
30  import org.apache.myfaces.orchestra.dynaForm.metadata.impl.MetaDataImpl;
31  import org.apache.myfaces.orchestra.dynaForm.metadata.impl.jsf.JsfExclusiveExtractor;
32  import org.apache.myfaces.orchestra.dynaForm.metadata.impl.jsf.JsfExtractor;
33  import org.apache.myfaces.orchestra.dynaForm.metadata.impl.jsf.JsfRequestFieldExtractor;
34  import org.apache.myfaces.orchestra.dynaForm.uri.FacesUriResolver;
35  import org.apache.myfaces.orchestra.dynaForm.uri.UriResolver;
36  
37  import javax.el.ELContext;
38  import javax.faces.component.UIColumn;
39  import javax.faces.component.UIComponent;
40  import javax.faces.component.UIComponentBase;
41  import javax.faces.component.UIData;
42  import javax.faces.context.FacesContext;
43  import javax.faces.el.ValueBinding;
44  import javax.persistence.Transient;
45  import java.util.Iterator;
46  import java.util.List;
47  import java.util.Map;
48  
49  /**
50   * A DynaForm component dynamically creates child jsf component objects to render
51   * all the persistent fields of an arbitrary java object.
52   * <p>
53   * This component can be nested within a UIData. In this case the data model of the
54   * UIData is assumed to be a list of persistent objects, each of which is of the 
55   * type specified by the "uri" attribute of this component. A UIColumn component
56   * is generated for each persistent property of the class specified by "uri".
57   * <p>
58   * This component can also be used as a child of something other than a UIData,
59   * in which case it simply outputs a (label, field) pair of components for each
60   * persistent property of the class specified by the "uri" parameter. It is up
61   * to the parent component to lay out these component pairs appropriately. 
62   * <p>
63   * The standard "value" property must be an EL expression that returns the object
64   * whose properties are to be displayed or edited. When used within a UIData,
65   * this will normally return the "var" property of the datatable.
66   * <p>
67   * For documentation on the configurable properties of this component, see:
68   * <ul>
69   * <li>uri: {@link #setUri(String)}
70   * <li>valueBindingPrefix: {@link #setValueBindingPrefix(String)}
71   * <li>displayOnly: {@link #setDisplayOnly(boolean)}
72   * <li>bundle: {@link #setBundle(String)}
73   * <li>exclusiveFields: {@link #setUri(String)}
74   * <li>idAsDisplayOnly: {@link #setIdAsDisplayOnly(boolean)}
75   * </ul>
76   * <p>
77   * Note that this class has no Renderer; this class dynamically modifies the component
78   * tree to add components, but has no actual representation itself and therefore needs
79   * no renderer. See method initView.
80   */
81  public class DynaForm extends UIComponentBase
82  {
83      public static final String COMPONENT_TYPE = "org.apache.myfaces.orchestra.dynaForm.DynaForm";
84      public static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.orchestra.dynaForm.DynaForm";
85      public static final String COMPONENT_FAMILY = "org.apache.myfaces.orchestra.dynaForm.DynaForm";
86  
87      public static final String DYNA_FORM_CREATED = "org.apache.myfaces.dynaForm.CREATED";
88      public static final String DYNA_FORM_URI = "org.apache.myfaces.dynaForm.URI";
89  
90      private String uri;
91      private String bundle;
92      private String valueBindingPrefix;
93      private Boolean displayOnly;
94      private Boolean exclusiveFields;
95      private Boolean idAsDisplayOnly;
96      private NewComponentListener newComponentListener;
97  
98      // TODO: why is this property marked with a JPA Transient annotation?
99      @Transient
100     private transient UriResolver.Configuration configuration = null;
101     @Transient
102     private transient DynaConfigs formConfigs = null;
103     @Transient
104     private transient ELContext elcontext;
105 
106     /**
107      * Create a UIColumn component to wrap a (label, value) pair of JSF components
108      * that represent a single persistent property. This class is used when the
109      * DynaForm is generating a "row per object" layout for a list of persistent
110      * objects.
111      * <p>
112      * TODO: before writing out the component, look for an existing component with that
113      * fieldname inside the parent. If it exists, then just skip the component creation.
114      * This allows the user to do custom layout for some of the fields. This might also
115      * make the @UIComponent annotation obsolete. 
116      */
117     protected static class AddComponentToTable implements NewComponentListener
118     {
119         // TODO: fieldName is an unused parameter
120         @SuppressWarnings("unchecked")
121         public void newComponent(UIComponent destCmp,
122                                  MetaField field, String fieldName,
123                                  UIComponent label, UIComponent component)
124         {
125             UIColumn column = new UIColumn();
126             if (component.getId() != null)
127             {
128                 column.setId("col" + component.getId());
129             }
130             column.getChildren().add(component);
131             column.setHeader(label);
132             column.getAttributes().put(DynaForm.DYNA_FORM_CREATED, Boolean.TRUE);
133 
134             _FacesUtils.copyRendered(component, column);
135 
136             destCmp.getChildren().add(column);
137         }
138 
139         public boolean containsComponent(UIComponent destCmp, String id)
140         {
141             return destCmp.findComponent(id) != null;
142         }
143     }
144 
145     /**
146      * Simply attach a (label, value) pair of JSF components that represent a
147      * single persistent property.
148      * <p>
149      * This class is used when the DynaForm is generating a plain "form style"
150      * layout for a single persistent object.
151      */
152     protected static class AddComponentSimple implements NewComponentListener
153     {
154         @SuppressWarnings("unchecked")
155         public void newComponent(UIComponent destCmp,
156                                  MetaField field, String fieldName,
157                                  UIComponent label, UIComponent component)
158         {
159             _FacesUtils.copyRendered(component, label);
160 
161             destCmp.getChildren().add(label);
162             destCmp.getChildren().add(component);
163         }
164 
165         public boolean containsComponent(UIComponent destCmp, String id)
166         {
167             return destCmp.findComponent(id) != null;
168         }
169     }
170 
171     public DynaForm()
172     {
173         super();
174     }
175 
176     @Override
177     public String getFamily()
178     {
179         return COMPONENT_FAMILY;
180     }
181 
182     /**
183      *  @see #setNewComponentListener(org.apache.myfaces.orchestra.dynaForm.jsf.guiBuilder.impl.jsf.NewComponentListener)
184      */
185     public NewComponentListener getNewComponentListener()
186     {
187         if (newComponentListener != null)
188         {
189             return newComponentListener;
190         }
191 
192         ValueBinding vb = getValueBinding("newComponentListener");
193         return vb != null ? (NewComponentListener) vb.getValue(getFacesContext()) : null;
194     }
195 
196     /**
197      * Normally DynaForm will try to figure out how to add components to the JSF tree.
198      * In case you would like to customize that, you cann provide your own {@link NewComponentListener}.
199      */
200     public void setNewComponentListener(NewComponentListener newComponentListener)
201     {
202         this.newComponentListener = newComponentListener;
203     }
204 
205     /**
206      * @see #setUri
207      */
208     public String getUri()
209     {
210         if (uri != null)
211         {
212             return uri;
213         }
214         ValueBinding vb = getValueBinding("uri");
215         return vb != null ? (String) vb.getValue(getFacesContext()) : null;
216     }
217 
218     /**
219      * Specifies how to obtain the metadata for the value object.
220      * <p>
221      * This is a string of form "inspector:classname". The inspector part ndicates
222      * which of the configured Extractor implementations should be used to obtain the
223      * metadata for this class. The classname is of course the concrete class of the
224      * specified object.
225      * <p>
226      * For example, "ejb:fqn.to.model.Entity" means use the EJB3-based extractor
227      * implementation to obtain metadata about the given entity
228      * <p>
229      * TODO: why can't value.getClass() be used for the classname part of the uri?
230      * Maybe because the value EL expression can map to a null property when creating
231      * a new instance?
232      * <p>
233      *
234      * @see org.apache.myfaces.orchestra.dynaForm.uri.UriResolver
235      */
236     public void setUri(String uri)
237     {
238         this.uri = uri;
239     }
240 
241     /**
242      * @see #setValueBindingPrefix
243      */
244     public String getValueBindingPrefix()
245     {
246         if (valueBindingPrefix != null)
247         {
248             return valueBindingPrefix;
249         }
250         ValueBinding vb = getValueBinding("valueBindingPrefix");
251         return vb != null ? (String) vb.getValue(getFacesContext()) : null;
252     }
253 
254     /**
255      * Specify how to get and set the model value for each persistent property
256      * of the value object.
257      * <p>
258      * When the JSF components that correspond to the individual persistent
259      * properties of the value object are created, they need EL expressions to
260      * tell them how to access the corresponding model value.
261      * <p>
262      * The EL expression used for each created component is of form
263      * <code>#{valueBindingPrefix.propname}</code>.
264      * <p>
265      * Of course valueBindingPrefix will normally refer to the same object
266      * that the "value" property refers to. In fact, there probably isn't any
267      * other sane value for this property.
268      * <p>
269      * TODO: Can we get rid of this attribute? Either call
270      * <code>getValueBinding().getExpressionString()</code> or create a special
271      * ValueBinding subclass that takes a "base object" parameter which we can
272      * point at the result of evaluating the main value expression.
273      */
274     public void setValueBindingPrefix(String valueBindingPrefix)
275     {
276         this.valueBindingPrefix = valueBindingPrefix;
277     }
278 
279     /**
280      * @see #setBundle
281      */
282     public String getBundle()
283     {
284         if (bundle != null)
285         {
286             return bundle;
287         }
288         ValueBinding vb = getValueBinding("bundle");
289         return vb != null ? (String) vb.getValue(getFacesContext()) : null;
290     }
291 
292     /**
293      * The bundle to use to convert property names to localised label strings.
294      * <p>
295      * For each persistent property on the value object, a pair of components
296      * (label and value) are created. The label text is computed by using the
297      * persistent property name as a key into the specified bundle.
298      * <p>
299      * This must be the name of a managed bean that implements Map.
300      */
301     public void setBundle(String bundle)
302     {
303         this.bundle = bundle;
304     }
305 
306     /**
307      * Display the whole form in read only mode, ie all the JSF components
308      * generated to display persistent properties are "read only".
309      */
310     public void setDisplayOnly(boolean displayOnly)
311     {
312         this.displayOnly = displayOnly;
313     }
314 
315     /**
316      * @see #setDisplayOnly(boolean)
317      */
318     public boolean isDisplayOnly()
319     {
320         if (displayOnly != null)
321         {
322             return displayOnly.booleanValue();
323         }
324         ValueBinding vb = getValueBinding("displayOnly");
325         if (vb != null)
326         {
327             Boolean ret = (Boolean) vb.getValue(getFacesContext());
328             if (ret != null)
329             {
330                 return ret.booleanValue();
331             }
332         }
333 
334         return false;
335     }
336 
337     /**
338      * Display id fields (ie the key fields of the persistent object) in the form
339      * in read only mode.
340      * <p>
341      * Defaults to false.
342      * <p>
343      * When editing existing objects this should be set to true, as it is not possible
344      * to modify the key of an existing entity. When creating a new instance this should
345      * be set to true if the key is an auto-generated surrogate key, but false if the
346      * key to this entity is a "business key".
347      */
348     public void setIdAsDisplayOnly(boolean idAsDisplayOnly)
349     {
350         this.idAsDisplayOnly = idAsDisplayOnly;
351     }
352 
353     /**
354      * @see #setIdAsDisplayOnly(boolean)
355      */
356     public boolean isIdAsDisplayOnly()
357     {
358         if (idAsDisplayOnly != null)
359         {
360             return idAsDisplayOnly.booleanValue();
361         }
362         ValueBinding vb = getValueBinding("idAsDisplayOnly");
363         if (vb != null)
364         {
365             Boolean ret = (Boolean) vb.getValue(getFacesContext());
366             if (ret != null)
367             {
368                 return ret.booleanValue();
369             }
370         }
371 
372         return false;
373     }
374 
375     /**
376      * Process only fields listed by their facets
377      * <p>
378      * TODO: document this properly. What does this do exactly??
379      */
380     public void setExclusiveFields(boolean exclusiveFields)
381     {
382         this.exclusiveFields = exclusiveFields;
383     }
384 
385     /**
386      * @see #setExclusiveFields(boolean)
387      */
388     public boolean isExclusiveFields()
389     {
390         if (exclusiveFields != null)
391         {
392             return exclusiveFields.booleanValue();
393         }
394         ValueBinding vb = getValueBinding("exclusiveFields");
395         if (vb != null)
396         {
397             Boolean ret = (Boolean) vb.getValue(getFacesContext());
398             if (ret != null)
399             {
400                 return ret.booleanValue();
401             }
402         }
403 
404         return false;
405     }
406 
407     @Override
408     public void restoreState(FacesContext context, Object stateArray)
409     {
410         Object[] states = (Object[]) stateArray;
411         super.restoreState(context, states[0]);
412         uri = (String) states[1];
413         // var = (String) states[2];
414         displayOnly = (Boolean) states[2];
415         bundle = (String) states[3];
416         valueBindingPrefix = (String) states[4];
417         idAsDisplayOnly = (Boolean) states[5];
418         exclusiveFields = (Boolean) states[6];
419         newComponentListener = (NewComponentListener) restoreAttachedState(context, states[7]);
420     }
421 
422     @Override
423     public Object saveState(FacesContext context)
424     {
425         return new Object[]
426             {
427                 super.saveState(context),
428                 uri,
429                 // var,
430                 displayOnly,
431                 bundle,
432                 valueBindingPrefix,
433                 idAsDisplayOnly,
434                 exclusiveFields,
435                 saveAttachedState(context, newComponentListener)
436             };
437     }
438 
439     /**
440      * Get the overall configuration based on the current value of the uri property.
441      * <p>
442      * This returns an object that simply pairs an Extractor object with the remaining
443      * part of the URI. The Extractor instance selected is specified by the "protocol"
444      * part of the URI.
445      *
446      * @see org.apache.myfaces.orchestra.dynaForm.uri.UriResolver
447      */
448     protected UriResolver.Configuration getConfiguration()
449     {
450         if (configuration == null)
451         {
452             configuration = new FacesUriResolver().resolveUri(getUri());
453         }
454         return configuration;
455     }
456 
457     public Extractor getExtractor()
458     {
459         return getConfiguration().getExtractor();
460     }
461 
462     /**
463      * try to find a parent dyna form+
464      */
465     protected DynaForm findParentDynaForm(DynaForm start)
466     {
467         UIComponent component = start.getParent();
468         while (component != null)
469         {
470             if (component instanceof DynaForm)
471             {
472                 return (DynaForm) component;
473             }
474             component = component.getParent();
475         }
476 
477         return null;
478     }
479 
480     /**
481      * Find the dynaForm component
482      */
483     public static DynaForm getDynaForm(UIComponent component)
484     {
485         if (component == null)
486         {
487             throw new IllegalArgumentException("component==null not allowed");
488         }
489 
490         UIComponent dynaFormComponent = component;
491         while (dynaFormComponent != null && (!(dynaFormComponent instanceof DynaForm)))
492         {
493             dynaFormComponent = dynaFormComponent.getParent();
494         }
495         if (dynaFormComponent == null)
496         {
497             throw new IllegalArgumentException(
498                 "component with id '" + component.getId() + "' not contained in an dynaForm");
499         }
500 
501         return (DynaForm) dynaFormComponent;
502     }
503 
504     /**
505      * get access to the special form configurations
506      */
507     public DynaConfigs getFormConfigs()
508     {
509         if (formConfigs != null)
510         {
511             return formConfigs;
512         }
513 
514         for (Object obj : getChildren())
515         {
516             if (obj instanceof DynaConfigs)
517             {
518                 formConfigs = (DynaConfigs) obj;
519                 return formConfigs;
520             }
521         }
522 
523         return null;
524     }
525 
526     /**
527      * Note that this method should only be called when _FacesUtils.useValueExpression is true.
528      * <p>
529      * This is needed when using either JSF1.2 or (JSF1.1 + facelets). 
530      * <p>
531      * It is still not clear why we need this anyway; instead of storing a context for later
532      * use, why not just use the context available at the time an expression must be evaluated?
533      */
534     public ELContext getELContext()
535     {
536         return elcontext;
537     }
538 
539     public void setELContext(ELContext elcontext)
540     {
541         this.elcontext = elcontext;
542     }
543 
544     @Override
545     public boolean isRendered()
546     {
547         // ensure we are not treated as a rendering component
548         return false;
549     }
550 
551     /**
552      * Dynamically compute and add child components to the current tree.
553      * <p>
554      * This method is required to be invoked from the TagHandler when an instance of
555      * this component is created. Components are created based upon the class specified
556      * by the "uri" property, and added as children of the parent component of this component.
557      * <p>
558      * This component itself never has any components, and never generates any output
559      * into the response stream.
560      * <p>
561      * It would be much nicer if this component could be the parent of the components it
562      * dynamically creates, but that doesn't work well with an h:panelGrid or h:dataTable
563      * component as the parent; a panelGrid counts its children while a dataTable counts
564      * its UIColumn children. To make things work, the created components must therefore
565      * be direct children of the parent. Note that this component is also a child of the
566      * parent, but because it marks itself as rendered=false it does not affect the
567      * behaviour of parent components that count their (rendered) children.
568      * <p>
569      * Note also that although this method is capable of deleting previously-created
570      * components and creating a new set, at the moment this method is only invoked
571      * by the taghandler when this component is created. That means that the "uri"
572      * parameter is only used on first access to the view, and is ignored thereafter.
573      */
574     @SuppressWarnings("unchecked")
575     public void initView(FacesContext context)
576     {
577         UIComponent layoutComponent = getParent();
578         ViewType viewType = getViewType(layoutComponent);
579 
580         // check if the uri has changed since the last rendering. This can happen
581         // if the uri points to a value-binding
582         String previousUri = (String) getAttributes().get(DynaForm.DYNA_FORM_URI);
583         if (previousUri != null && !previousUri.equals(getUri()))
584         {
585             removeDynaFormCreatedComponents(layoutComponent);
586         }
587 
588         // create & add components
589         // boolean needAdd = processPreviouslyAdded(context, layoutComponent);
590         // if (needAdd)
591         {
592             getAttributes().put(DynaForm.DYNA_FORM_URI, getUri());
593             addComponents(context, this, layoutComponent, viewType);
594         }
595     }
596 
597     public void removeDynaFormCreatedComponents(UIComponent base)
598     {
599         @SuppressWarnings("unchecked")
600         List<UIComponent> children = base.getChildren();
601         if (children == null || children.size() < 1 )
602         {
603             return;
604         }
605 
606         Iterator<UIComponent> iterChildren = children.iterator();
607         while (iterChildren.hasNext())
608         {
609             UIComponent component = (UIComponent) iterChildren.next();
610 
611             if (Boolean.TRUE.equals(component.getAttributes().get(DynaForm.DYNA_FORM_CREATED)))
612             {
613                 iterChildren.remove();
614             }
615             else
616             {
617                 removeDynaFormCreatedComponents(component);
618             }
619         }
620     }
621 
622     /**
623      * determine the current view type
624      * <ul>
625      * <li>"list" means: the layout component "is a" or "is embedded in an" list component (UIData)</li>
626      * <li>"form" means: anything else</li>
627      * </ul>
628      */
629     protected ViewType getViewType(UIComponent startComponent)
630     {
631         UIComponent component = startComponent;
632         while (component != null && !(component instanceof DynaForm))
633         {
634             if (isTable(component))
635             {
636                 return ViewType.LIST;
637             }
638 
639             component = component.getParent();
640         }
641 
642         return ViewType.FORM;
643     }
644 
645     /**
646      * create and add the components to the layout component.
647      *
648      * TODO: clean this method up. It still had artefacts from when this code was in a separate
649      * Renderer class.
650      */
651     protected void addComponents(final FacesContext context,
652                                  final DynaForm dynaForm, final UIComponent layoutComponent,
653                                  final ViewType viewType)
654     {
655         MetaData metaData = extractMetaData(dynaForm);
656 
657         Slipstream slipstream = new Slipstream();
658         slipstream.setDynaForm(this);
659         slipstream.setModelMetaData(metaData);
660 
661         if (dynaForm.getBundle() != null)
662         {
663             // get the bundle and attach it
664             ValueBinding vb = context.getApplication().createValueBinding("#{" + dynaForm.getBundle() + "}");
665             @SuppressWarnings("unchecked")
666             Map<String, String> bundleMap = (Map<String, String>) vb.getValue(context);
667             slipstream.setLabelBundle(bundleMap);
668         }
669 
670         JsfGuiBuilder guiBuilder = createGuiBuilder(context);
671 
672         guiBuilder.setDestCmp(layoutComponent);
673         guiBuilder.setELContext(dynaForm.getELContext());
674         guiBuilder.setContext(context);
675 
676         if (dynaForm.getNewComponentListener() != null)
677         {
678             guiBuilder.setNewComponentListener(dynaForm.getNewComponentListener());
679         }
680         else if (isTable(layoutComponent))
681         {
682             guiBuilder.setNewComponentListener(new AddComponentToTable());
683         }
684         else
685         {
686             guiBuilder.setNewComponentListener(new AddComponentSimple());
687         }
688 
689         String valueBindingPrefix = getValueBindingPrefix(dynaForm, layoutComponent);
690         if (valueBindingPrefix == null)
691         {
692             throw new UnsupportedOperationException("can't determine the value binding prefix");
693         }
694         guiBuilder.setBackingEntityPrefix(valueBindingPrefix);
695 
696         slipstream.setGuiBuilder(guiBuilder);
697         slipstream.process();
698     }
699 
700     private boolean isTable(UIComponent layoutComponent)
701     {
702         return layoutComponent instanceof UIData || UIData.COMPONENT_FAMILY.equals(layoutComponent.getFamily());
703     }
704 
705     protected String getValueBindingPrefix(DynaForm dynaForm, UIComponent layoutComponent)
706     {
707         String valueBindingPrefix = dynaForm.getValueBindingPrefix();
708         if (valueBindingPrefix == null)
709         {
710             valueBindingPrefix = (String) layoutComponent.getAttributes().get("var");
711         }
712 
713         return valueBindingPrefix;
714     }
715 
716     private MetaData extractMetaData(final DynaForm dynaForm)
717     {
718         MetaDataImpl metaData = new MetaDataImpl();
719 
720         try
721         {
722             // lookup which fields to process
723             new JsfRequestFieldExtractor().getMetaData(metaData, dynaForm);
724 
725             if (dynaForm.isExclusiveFields())
726             {
727                 // the same as above, but keep their ordering and some additional metadata
728                 new JsfExclusiveExtractor().getMetaData(metaData, dynaForm);
729                 metaData.setLockFields(true);
730             }
731 
732             // use the users extractor
733             dynaForm.getExtractor().getMetaData(metaData, dynaForm.getConfiguration().getEntity());
734 
735             // processs the jsf stuff
736             new JsfExtractor().getMetaData(metaData, dynaForm);
737 
738         }
739         finally
740         {
741             metaData.setLockFields(false);
742         }
743 
744         return metaData;
745     }
746 
747     protected JsfGuiBuilder createGuiBuilder(final FacesContext facesContext)
748     {
749         return JsfGuiBuilderFactory.create(facesContext);
750     }
751 
752     /**
753      * check if we already added components to the layout component.<br />
754      * <p/>
755      * if this is the case then:
756      * <ul>
757      * <li>keep them cached and avoid readd</li>
758      * <li><strike>in development mode: remove the components</strike></li>
759      * <li><strike>in production mode: keep them cached</strike></li>
760      * </ul>
761      * <p/>
762      * TODO: need to figure out whats the best way to recreate the components
763      *
764      * @return true if we have to add our components again
765      */
766     protected boolean processPreviouslyAdded(FacesContext context,
767                                              UIComponent root)
768     {
769         @SuppressWarnings("unchecked")
770         List<UIComponent> rootComponentChildren = root.getChildren();
771         if (rootComponentChildren != null)
772         {
773             for (Object component : rootComponentChildren)
774             {
775                 UIComponent child = (UIComponent) component;
776                 if (Boolean.TRUE.equals(child.getAttributes().get(
777                     DynaForm.DYNA_FORM_CREATED)))
778                 {
779                     return false;
780                 }
781 
782                 if (!processPreviouslyAdded(context, child))
783                 {
784                     return false;
785                 }
786             }
787         }
788 
789         return true;
790     }
791 }