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 javax.faces.component;
20  
21  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
22  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
23  
24  import javax.faces.FacesException;
25  import javax.faces.component.visit.VisitCallback;
26  import javax.faces.component.visit.VisitContext;
27  import javax.faces.component.visit.VisitResult;
28  import javax.faces.context.FacesContext;
29  import javax.faces.event.PostValidateEvent;
30  import javax.faces.event.PreValidateEvent;
31  import javax.faces.view.Location;
32  
33  import java.util.Collection;
34  
35  /**
36   * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
37   * 
38   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
39   * @version $Revision: 1425017 $ $Date: 2012-12-21 11:47:07 -0500 (Fri, 21 Dec 2012) $
40   */
41  @JSFComponent(type = "javax.faces.Form", family = "javax.faces.Form")
42  public class UIForm extends UIComponentBase implements NamingContainer, UniqueIdVendor
43  {
44      // private static final Log log = LogFactory.getLog(UIForm.class);
45  
46      //private boolean _submitted;
47  
48      /**
49       * 
50       * {@inheritDoc}
51       * 
52       * @since 2.0
53       */
54      public String createUniqueId(FacesContext context, String seed)
55      {
56          StringBuilder bld = null;
57          
58          // When prependId is set to false, it is necessary to append an unique
59          // prefix to ensure the generated ids are unique, but that's only necessary
60          // when no seed is provided. If a seed is provided, that one is already unique
61          // for all the view, so the following logic is not necessary.
62          if (!isPrependId() && seed==null )
63          {
64              bld = new StringBuilder();
65              UniqueIdVendor parentUniqueIdVendor = _ComponentUtils.findParentUniqueIdVendor(this);
66              if (parentUniqueIdVendor == null)
67              {
68                  UIViewRoot viewRoot = context.getViewRoot();
69                  if (viewRoot != null)
70                  {
71                      bld.append(viewRoot.createUniqueId());
72                      bld.append('_');
73                  }
74                  else
75                  {
76                      // The RI throws a NPE
77                      String location = getComponentLocation(this);
78                      throw new FacesException("Cannot create clientId. No id is assigned for component"
79                              + " to create an id and UIViewRoot is not defined: "
80                              + getPathToComponent(this)
81                              + (location != null ? " created from: " + location : ""));
82                  }
83              }
84              else
85              {
86                  bld.append(parentUniqueIdVendor.createUniqueId(context, null));
87                  bld.append('_');
88              }
89          }
90          else
91          {
92              bld = _getSharedStringBuilder(context);
93          }
94  
95          // Generate an identifier for a component. The identifier will be prefixed with
96          // UNIQUE_ID_PREFIX, and will be unique within this UIViewRoot.
97          if(seed==null)
98          {
99              Long uniqueIdCounter = (Long) getStateHelper().get(PropertyKeys.uniqueIdCounter);
100             uniqueIdCounter = (uniqueIdCounter == null) ? 0 : uniqueIdCounter;
101             getStateHelper().put(PropertyKeys.uniqueIdCounter, (uniqueIdCounter+1L));
102             return bld.append(UIViewRoot.UNIQUE_ID_PREFIX).append(uniqueIdCounter).toString();    
103         }
104         // Optionally, a unique seed value can be supplied by component creators
105         // which should be included in the generated unique id.
106         else
107         {
108             return bld.append(UIViewRoot.UNIQUE_ID_PREFIX).append(seed).toString();
109         }
110     }
111     
112     public boolean isSubmitted()
113     {
114         //return _submitted;
115         return (Boolean) getTransientStateHelper().getTransient(PropertyKeys.submitted, false);
116     }
117 
118     public void setSubmitted(boolean submitted)
119     {
120         getTransientStateHelper().putTransient(PropertyKeys.submitted, submitted);
121         //_submitted = submitted;
122     }
123 
124     @Override
125     public void processDecodes(FacesContext context)
126     {
127         if (context == null)
128         {
129             throw new NullPointerException("context");
130         }
131         try
132         {
133             setCachedFacesContext(context);
134             try
135             {
136                 pushComponentToEL(context, this);
137                 
138                 decode(context);
139                 
140                 if (!isSubmitted())
141                 {
142                     return;
143                 }
144 
145                 int facetCount = getFacetCount();
146                 if (facetCount > 0)
147                 {
148                     for (UIComponent facet : getFacets().values())
149                     {
150                         facet.processDecodes(context);
151                     }
152                 }
153                 
154                 for (int i = 0, childCount = getChildCount(); i < childCount; i++)
155                 {
156                     UIComponent child = getChildren().get(i);
157                     child.processDecodes(context);
158                 }
159                 
160             }
161             finally
162             {
163                 popComponentFromEL(context);
164             }
165         }
166         finally
167         {
168             setCachedFacesContext(null);
169         }
170     }
171 
172     @Override
173     public void processValidators(FacesContext context)
174     {
175         if (context == null)
176         {
177             throw new NullPointerException("context");
178         }
179         
180         try
181         {
182             setCachedFacesContext(context);
183             try
184             {
185                 pushComponentToEL(context, this);
186                 // SF issue #1050022: a form used within a datatable will loose it's submitted state
187                 // as UIForm is no EditableValueHolder and therefore it's state is not saved/restored by UIData
188                 // to restore the submitted state we call decode here again
189                 if (!isSubmitted())
190                 {
191                     decode(context);
192                 }
193                 if (!isSubmitted())
194                 {
195                     return;
196                 }
197 
198                 //Pre validation event dispatch for component
199                 context.getApplication().publishEvent(context,  PreValidateEvent.class, getClass(), this);
200                 
201                 int facetCount = getFacetCount();
202                 if (facetCount > 0)
203                 {
204                     for (UIComponent facet : getFacets().values())
205                     {
206                         facet.processValidators(context);
207                     }
208                 }
209                 
210                 for (int i = 0, childCount = getChildCount(); i < childCount; i++)
211                 {
212                     UIComponent child = getChildren().get(i);
213                     child.processValidators(context);
214                 }
215                 
216             }
217             finally
218             {
219                 context.getApplication().publishEvent(context,  PostValidateEvent.class, getClass(), this);
220                 popComponentFromEL(context);
221             }
222         }
223         finally
224         {
225             setCachedFacesContext(null);
226         }
227     }
228 
229     @Override
230     public void processUpdates(FacesContext context)
231     {
232         if (context == null)
233         {
234             throw new NullPointerException("context");
235         }
236         
237         try
238         {
239             setCachedFacesContext(context);
240             try
241             {
242                 pushComponentToEL(context, this);
243                 // SF issue #1050022: a form used within a datatable will loose it's submitted state
244                 // as UIForm is no EditableValueHolder and therefore it's state is not saved/restored by UIData
245                 // to restore the submitted state we call decode here again
246                 if (!isSubmitted())
247                 {
248                     decode(context);
249                 }
250                 if (!isSubmitted())
251                 {
252                     return;
253                 }
254                 
255                 int facetCount = getFacetCount();
256                 if (facetCount > 0)
257                 {
258                     for (UIComponent facet : getFacets().values())
259                     {
260                         facet.processUpdates(context);
261                     }
262                 }
263                 
264                 for (int i = 0, childCount = getChildCount(); i < childCount; i++)
265                 {
266                     UIComponent child = getChildren().get(i);
267                     child.processUpdates(context);
268                 }
269 
270             }
271             finally
272             {
273                 popComponentFromEL(context);
274             }
275         }
276         finally
277         {
278             setCachedFacesContext(null);
279         }
280     }
281 
282     enum PropertyKeys
283     {
284          prependId,
285          uniqueIdCounter,
286          submitted,
287     }
288     
289     @Override
290     public Object saveState(FacesContext context)
291     {
292         // The saveState() method of UIForm must call setSubmitted(false) before calling super.saveState() as an 
293         // extra precaution to ensure the submitted state is not persisted across requests.
294         //setSubmitted(false);
295 
296         return super.saveState(context);
297     }
298     
299     @Override
300     public boolean visitTree(VisitContext context, VisitCallback callback)
301     {
302         if (!isPrependId())
303         {
304             // Since the container client id will not be added to child clientId,
305             // It is not possible to take advantage of NamingContainer interface
306             // and prevent visit child nodes. Just do it as default.
307             return super.visitTree(context, callback);
308         }
309         else
310         {
311             boolean isCachedFacesContext = isCachedFacesContext();
312             try
313             {
314                 if (!isCachedFacesContext)
315                 {
316                     setCachedFacesContext(context.getFacesContext());
317                 }
318                 
319                 if (!isVisitable(context))
320                 {
321                     return false;
322                 }
323         
324                 pushComponentToEL(context.getFacesContext(), this);
325                 try
326                 {
327                     VisitResult res = context.invokeVisitCallback(this, callback);
328                     switch (res)
329                     {
330                     //we are done nothing has to be processed anymore
331                     case COMPLETE:
332                         return true;
333         
334                     case REJECT:
335                         return false;
336         
337                     //accept
338                     default:
339                         // Take advantage of the fact this is a NamingContainer
340                         // and we can know if there are ids to visit inside it 
341                         Collection<String> subtreeIdsToVisit = context.getSubtreeIdsToVisit(this);
342                         
343                         if (subtreeIdsToVisit != null && !subtreeIdsToVisit.isEmpty())
344                         {
345                             if (getFacetCount() > 0)
346                             {
347                                 for (UIComponent facet : getFacets().values())
348                                 {
349                                     if (facet.visitTree(context, callback))
350                                     {
351                                         return true;
352                                     }
353                                 }
354                             }
355                             for (int i = 0, childCount = getChildCount(); i < childCount; i++)
356                             {
357                                 UIComponent child = getChildren().get(i);
358                                 if (child.visitTree(context, callback))
359                                 {
360                                     return true;
361                                 }
362                             }
363                         }
364                         return false;
365                     }
366                 }
367                 finally
368                 {
369                     //all components must call popComponentFromEl after visiting is finished
370                     popComponentFromEL(context.getFacesContext());
371                 }
372             }
373             finally
374             {
375                 if (!isCachedFacesContext)
376                 {
377                     setCachedFacesContext(null);
378                 }
379             }
380         }
381     }
382 
383     // ------------------ GENERATED CODE BEGIN (do not modify!) --------------------
384 
385     public static final String COMPONENT_TYPE = "javax.faces.Form";
386     public static final String COMPONENT_FAMILY = "javax.faces.Form";
387     private static final String DEFAULT_RENDERER_TYPE = "javax.faces.Form";
388 
389     public UIForm()
390     {
391         setRendererType(DEFAULT_RENDERER_TYPE);
392     }
393 
394     @Override
395     public String getFamily()
396     {
397         return COMPONENT_FAMILY;
398     }
399 
400     private String getComponentLocation(UIComponent component)
401     {
402         Location location = (Location) component.getAttributes()
403                 .get(UIComponent.VIEW_LOCATION_KEY);
404         if (location != null)
405         {
406             return location.toString();
407         }
408         return null;
409     }
410     
411     private String getPathToComponent(UIComponent component)
412     {
413         StringBuffer buf = new StringBuffer();
414 
415         if (component == null)
416         {
417             buf.append("{Component-Path : ");
418             buf.append("[null]}");
419             return buf.toString();
420         }
421 
422         getPathToComponent(component, buf);
423 
424         buf.insert(0, "{Component-Path : ");
425         buf.append("}");
426 
427         return buf.toString();
428     }
429     
430     private void getPathToComponent(UIComponent component, StringBuffer buf)
431     {
432         if (component == null)
433         {
434             return;
435         }
436 
437         StringBuffer intBuf = new StringBuffer();
438 
439         intBuf.append("[Class: ");
440         intBuf.append(component.getClass().getName());
441         if (component instanceof UIViewRoot)
442         {
443             intBuf.append(",ViewId: ");
444             intBuf.append(((UIViewRoot) component).getViewId());
445         }
446         else
447         {
448             intBuf.append(",Id: ");
449             intBuf.append(component.getId());
450         }
451         intBuf.append("]");
452 
453         buf.insert(0, intBuf.toString());
454 
455         getPathToComponent(component.getParent(), buf);
456     }
457 
458     // ------------------ GENERATED CODE END ---------------------------------------
459 
460     @Override
461     public String getContainerClientId(FacesContext ctx)
462     {
463         if (isPrependId())
464         {
465             return super.getContainerClientId(ctx);
466         }
467         UIComponent parentNamingContainer = _ComponentUtils.findParentNamingContainer(this, false);
468         if (parentNamingContainer != null)
469         {
470             return parentNamingContainer.getContainerClientId(ctx);
471         }
472         return null;
473     }
474 
475     @JSFProperty(defaultValue = "true")
476     public boolean isPrependId()
477     {
478         return (Boolean) getStateHelper().eval(PropertyKeys.prependId, true);
479     }
480 
481     public void setPrependId(boolean prependId)
482     {
483         getStateHelper().put(PropertyKeys.prependId, prependId ); 
484     }
485 
486 }