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.view.facelets.compiler;
20  
21  import java.util.Collection;
22  
23  import javax.faces.application.FacesMessage;
24  import javax.faces.component.NamingContainer;
25  import javax.faces.component.UIComponent;
26  import javax.faces.component.UIInput;
27  import javax.faces.component.UIViewRoot;
28  import javax.faces.component.UniqueIdVendor;
29  import javax.faces.context.FacesContext;
30  import javax.faces.el.EvaluationException;
31  import javax.faces.el.MethodBinding;
32  import javax.faces.el.ValueBinding;
33  import javax.faces.validator.Validator;
34  import javax.faces.validator.ValidatorException;
35  
36  /**
37   * A collection of static helper methods for locating UIComponents.
38   * 
39   * @author Manfred Geiler (latest modification by $Author$)
40   * @version $Revision$ $Date$
41   */
42  class _ComponentUtils
43  {
44      private _ComponentUtils()
45      {
46      }
47  
48      static UIComponent findParentNamingContainer(UIComponent component, boolean returnRootIfNotFound)
49      {
50          UIComponent parent = component.getParent();
51          if (returnRootIfNotFound && parent == null)
52          {
53              return component;
54          }
55          while (parent != null)
56          {
57              if (parent instanceof NamingContainer)
58              {
59                  return parent;
60              }
61              if (returnRootIfNotFound)
62              {
63                  UIComponent nextParent = parent.getParent();
64                  if (nextParent == null)
65                  {
66                      return parent; // Root
67                  }
68                  parent = nextParent;
69              }
70              else
71              {
72                  parent = parent.getParent();
73              }
74          }
75          return null;
76      }
77  
78      static UniqueIdVendor findParentUniqueIdVendor(UIComponent component)
79      {
80          UIComponent parent = component.getParent();
81  
82          while (parent != null)
83          {
84              if (parent instanceof UniqueIdVendor)
85              {
86                  return (UniqueIdVendor) parent;
87              }
88              parent = parent.getParent();
89          }
90          return null;
91      }
92      
93      static UIComponent getRootComponent(UIComponent component)
94      {
95          UIComponent parent;
96          for (;;)
97          {
98              parent = component.getParent();
99              if (parent == null)
100             {
101                 return component;
102             }
103             component = parent;
104         }
105     }
106 
107     /**
108      * Find the component with the specified id starting from the specified component.
109      * <p>
110      * Param id must not contain any NamingContainer.SEPARATOR_CHAR characters (ie ":"). This method explicitly does
111      * <i>not</i> search into any child naming container components; this is expected to be handled by the caller of
112      * this method.
113      * <p>
114      * For an implementation of findComponent which does descend into child naming components, see
115      * org.apache.myfaces.custom.util.ComponentUtils.
116      * 
117      * @return findBase, a descendant of findBase, or null.
118      */
119     static UIComponent findComponent(UIComponent findBase, String id, final char separatorChar)
120     {
121         if (!(findBase instanceof NamingContainer) && idsAreEqual(id, findBase))
122         {
123             return findBase;
124         }
125 
126         int facetCount = findBase.getFacetCount();
127         if (facetCount > 0)
128         {
129             for (UIComponent facet : findBase.getFacets().values())
130             {
131                 if (!(facet instanceof NamingContainer))
132                 {
133                     UIComponent find = findComponent(facet, id, separatorChar);
134                     if (find != null)
135                     {
136                         return find;
137                     }
138                 }
139                 else if (idsAreEqual(id, facet))
140                 {
141                     return facet;
142                 }
143             }
144         }
145         
146         for (int i = 0, childCount = findBase.getChildCount(); i < childCount; i++)
147         {
148             UIComponent child = findBase.getChildren().get(i);
149             if (!(child instanceof NamingContainer))
150             {
151                 UIComponent find = findComponent(child, id, separatorChar);
152                 if (find != null)
153                 {
154                     return find;
155                 }
156             }
157             else if (idsAreEqual(id, child))
158             {
159                 return child;
160             }
161         }
162 
163         if (findBase instanceof NamingContainer && idsAreEqual(id, findBase))
164         {
165             return findBase;
166         }
167 
168         return null;
169     }
170     
171     static UIComponent findComponentChildOrFacetFrom(UIComponent parent, String id, String innerExpr)
172     {
173         if (parent.getFacetCount() > 0)
174         {
175             for (UIComponent facet : parent.getFacets().values())
176             {
177                 if (id.equals(facet.getId()))
178                 {
179                     if (innerExpr == null)
180                     {
181                         return facet;
182                     }
183                     else if (facet instanceof NamingContainer)
184                     {
185                         UIComponent find = facet.findComponent(innerExpr);
186                         if (find != null)
187                         {
188                             return find;
189                         }
190                     }
191                 }
192                 else if (!(facet instanceof NamingContainer))
193                 {
194                     UIComponent find = findComponentChildOrFacetFrom(facet, id, innerExpr);
195                     if (find != null)
196                     {
197                         return find;
198                     }
199                 }
200             }
201         }
202         if (parent.getChildCount() > 0)
203         {
204             for (int i = 0, childCount = parent.getChildCount(); i < childCount; i++)
205             {
206                 UIComponent child = parent.getChildren().get(i);
207                 if (id.equals(child.getId()))
208                 {
209                     if (innerExpr == null)
210                     {
211                         return child;
212                     }
213                     else if (child instanceof NamingContainer)
214                     {
215                         UIComponent find = child.findComponent(innerExpr);
216                         if (find != null)
217                         {
218                             return find;
219                         }
220                     }
221                 }
222                 else if (!(child instanceof NamingContainer))
223                 {
224                     UIComponent find = findComponentChildOrFacetFrom(child, id, innerExpr);
225                     if (find != null)
226                     {
227                         return find;
228                     }
229                 }
230             }
231         }
232         return null;
233     }
234 
235     /*
236      * Return true if the specified component matches the provided id. This needs some quirks to handle components whose
237      * id value gets dynamically "tweaked", eg a UIData component whose id gets the current row index appended to it.
238      */
239     private static boolean idsAreEqual(String id, UIComponent cmp)
240     {
241         if (id.equals(cmp.getId()))
242         {
243             return true;
244         }
245 
246         /* By the spec, findComponent algorithm does not take into account UIData.rowIndex() property,
247          * because it just scan over nested plain ids. 
248         if (cmp instanceof UIData)
249         {
250             UIData uiData = ((UIData)cmp);
251 
252             if (uiData.getRowIndex() == -1)
253             {
254                 return dynamicIdIsEqual(id, cmp.getId());
255             }
256             return id.equals(cmp.getId() + separatorChar + uiData.getRowIndex());
257         }
258         */
259 
260         return false;
261     }
262 
263     static void callValidators(FacesContext context, UIInput input, Object convertedValue)
264     {
265         // first invoke the list of validator components
266         Validator[] validators = input.getValidators();
267         for (int i = 0; i < validators.length; i++)
268         {
269             Validator validator = validators[i];
270             try
271             {
272                 validator.validate(context, input, convertedValue);
273             }
274             catch (ValidatorException e)
275             {
276                 input.setValid(false);
277 
278                 String validatorMessage = input.getValidatorMessage();
279                 if (validatorMessage != null)
280                 {
281                     context.addMessage(input.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR,
282                         validatorMessage, validatorMessage));
283                 }
284                 else
285                 {
286                     FacesMessage facesMessage = e.getFacesMessage();
287                     if (facesMessage != null)
288                     {
289                         context.addMessage(input.getClientId(context), facesMessage);
290                     }
291                     Collection<FacesMessage> facesMessages = e.getFacesMessages();
292                     if (facesMessages != null)
293                     {
294                         for (FacesMessage message : facesMessages)
295                         {
296                             context.addMessage(input.getClientId(context), message);
297                         }
298                     }
299                 }
300             }
301         }
302 
303         // now invoke the validator method defined as a method-binding attribute
304         // on the component
305         MethodBinding validatorBinding = input.getValidator();
306         if (validatorBinding != null)
307         {
308             try
309             {
310                 validatorBinding.invoke(context, new Object[] { context, input, convertedValue });
311             }
312             catch (EvaluationException e)
313             {
314                 input.setValid(false);
315                 Throwable cause = e.getCause();
316                 if (cause instanceof ValidatorException)
317                 {
318                     String validatorMessage = input.getValidatorMessage();
319                     if (validatorMessage != null)
320                     {
321                         context.addMessage(input.getClientId(context), new FacesMessage(FacesMessage.SEVERITY_ERROR,
322                             validatorMessage, validatorMessage));
323                     }
324                     else
325                     {
326                         FacesMessage facesMessage = ((ValidatorException)cause).getFacesMessage();
327                         if (facesMessage != null)
328                         {
329                             context.addMessage(input.getClientId(context), facesMessage);
330                         }
331                         Collection<FacesMessage> facesMessages = ((ValidatorException)cause).getFacesMessages();
332                         if (facesMessages != null)
333                         {
334                             for (FacesMessage message : facesMessages)
335                             {
336                                 context.addMessage(input.getClientId(context), message);
337                             }
338                         }
339                     }
340                 }
341                 else
342                 {
343                     throw e;
344                 }
345             }
346         }
347     }
348 
349     static String getStringValue(FacesContext context, ValueBinding vb)
350     {
351         Object value = vb.getValue(context);
352         if (value == null)
353         {
354             return null;
355         }
356         return value.toString();
357     }
358 
359     /*
360     @SuppressWarnings("unchecked")
361     static <T> T getExpressionValue(UIComponent component, String attribute, T overrideValue, T defaultValue)
362     {
363         if (overrideValue != null)
364         {
365             return overrideValue;
366         }
367         ValueExpression ve = component.getValueExpression(attribute);
368         if (ve != null)
369         {
370             return (T)ve.getValue(component.getFacesContext().getELContext());
371         }
372         return defaultValue;
373     }*/
374 
375     static String getPathToComponent(UIComponent component)
376     {
377         StringBuffer buf = new StringBuffer();
378 
379         if (component == null)
380         {
381             buf.append("{Component-Path : ");
382             buf.append("[null]}");
383             return buf.toString();
384         }
385 
386         getPathToComponent(component, buf);
387 
388         buf.insert(0, "{Component-Path : ");
389         buf.append("}");
390 
391         return buf.toString();
392     }
393     
394     /**
395      * Call {@link UIComponent#pushComponentToEL(javax.faces.context.FacesContext,javax.faces.component.UIComponent)},
396      * reads the isRendered property, call {@link
397      * UIComponent#popComponentFromEL} and returns the value of isRendered.
398      */
399     static boolean isRendered(FacesContext facesContext, UIComponent uiComponent)
400     {
401         // We must call pushComponentToEL here because ValueExpression may have 
402         // implicit object "component" used. 
403         try
404         {
405             uiComponent.pushComponentToEL(facesContext, uiComponent);
406             return uiComponent.isRendered();
407         }
408         finally
409         {       
410             uiComponent.popComponentFromEL(facesContext);
411         }
412     }
413 
414     private static void getPathToComponent(UIComponent component, StringBuffer buf)
415     {
416         if (component == null)
417         {
418             return;
419         }
420 
421         StringBuffer intBuf = new StringBuffer();
422 
423         intBuf.append("[Class: ");
424         intBuf.append(component.getClass().getName());
425         if (component instanceof UIViewRoot)
426         {
427             intBuf.append(",ViewId: ");
428             intBuf.append(((UIViewRoot)component).getViewId());
429         }
430         else
431         {
432             intBuf.append(",Id: ");
433             intBuf.append(component.getId());
434         }
435         intBuf.append("]");
436 
437         buf.insert(0, intBuf.toString());
438 
439         getPathToComponent(component.getParent(), buf);
440     }
441 }