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