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