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  
20  package org.apache.myfaces.tobago.util;
21  
22  import org.apache.commons.lang.ArrayUtils;
23  import org.apache.commons.lang.StringUtils;
24  import org.apache.myfaces.tobago.component.Attributes;
25  import org.apache.myfaces.tobago.component.Facets;
26  import org.apache.myfaces.tobago.component.RendererTypes;
27  import org.apache.myfaces.tobago.component.SupportsMarkup;
28  import org.apache.myfaces.tobago.context.Markup;
29  import org.apache.myfaces.tobago.context.TransientStateHolder;
30  import org.apache.myfaces.tobago.el.ConstantMethodBinding;
31  import org.apache.myfaces.tobago.event.AbstractPopupActionListener;
32  import org.apache.myfaces.tobago.internal.component.AbstractUIForm;
33  import org.apache.myfaces.tobago.internal.component.AbstractUIInput;
34  import org.apache.myfaces.tobago.internal.component.AbstractUIPage;
35  import org.apache.myfaces.tobago.internal.component.AbstractUIPopup;
36  import org.apache.myfaces.tobago.internal.util.FindComponentUtils;
37  import org.apache.myfaces.tobago.renderkit.RendererBase;
38  import org.apache.myfaces.tobago.renderkit.html.StyleClasses;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  import javax.faces.FactoryFinder;
43  import javax.faces.application.Application;
44  import javax.faces.application.FacesMessage;
45  import javax.faces.component.ActionSource;
46  import javax.faces.component.EditableValueHolder;
47  import javax.faces.component.NamingContainer;
48  import javax.faces.component.UICommand;
49  import javax.faces.component.UIComponent;
50  import javax.faces.component.UIGraphic;
51  import javax.faces.component.UIInput;
52  import javax.faces.component.UIOutput;
53  import javax.faces.component.UIParameter;
54  import javax.faces.component.UISelectMany;
55  import javax.faces.component.UIViewRoot;
56  import javax.faces.component.ValueHolder;
57  import javax.faces.context.FacesContext;
58  import javax.faces.convert.Converter;
59  import javax.faces.el.MethodBinding;
60  import javax.faces.el.ValueBinding;
61  import javax.faces.event.ActionEvent;
62  import javax.faces.event.ActionListener;
63  import javax.faces.event.ValueChangeEvent;
64  import javax.faces.model.SelectItem;
65  import javax.faces.render.RenderKit;
66  import javax.faces.render.RenderKitFactory;
67  import javax.faces.render.Renderer;
68  import javax.faces.view.facelets.FaceletContext;
69  import javax.faces.webapp.UIComponentTag;
70  import javax.servlet.jsp.JspException;
71  import java.util.ArrayList;
72  import java.util.HashMap;
73  import java.util.Iterator;
74  import java.util.List;
75  import java.util.Map;
76  
77  public class ComponentUtils {
78  
79    private static final Logger LOG = LoggerFactory.getLogger(ComponentUtils.class);
80  
81    public static final String SUB_SEPARATOR = "::";
82    
83    private static final String RENDER_KEY_PREFIX
84        = "org.apache.myfaces.tobago.util.ComponentUtils.RendererKeyPrefix_";
85  
86    private static final String PAGE_KEY = "org.apache.myfaces.tobago.Page.Key";
87  
88    public static final Class[] ACTION_ARGS = {};
89    public static final Class[] ACTION_LISTENER_ARGS = {ActionEvent.class};
90    public static final Class[] VALUE_CHANGE_LISTENER_ARGS = {ValueChangeEvent.class};
91    public static final Class[] VALIDATOR_ARGS = {FacesContext.class, UIComponent.class, Object.class};
92    public static final String LIST_SEPARATOR_CHARS = ", ";
93  
94    /**
95     * Name of the map for data attributes in components. New in JSF 2.2.
96     * @since 1.6.0
97     */
98    public static final String DATA_ATTRIBUTES_KEY = "javax.faces.component.DATA_ATTRIBUTES_KEY";
99  
100   private ComponentUtils() {
101   }
102 
103   public static boolean hasErrorMessages(FacesContext context) {
104     for (Iterator iter = context.getMessages(); iter.hasNext();) {
105       FacesMessage message = (FacesMessage) iter.next();
106       if (FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0) {
107         return true;
108       }
109     }
110     return false;
111   }
112 
113   public static boolean containsPopupActionListener(UICommand command) {
114     ActionListener[] actionListeners = command.getActionListeners();
115     for (ActionListener actionListener : actionListeners) {
116       if (actionListener instanceof AbstractPopupActionListener) {
117         return true;
118       }
119     }
120     return false;
121   }
122 
123   public static String getFacesMessageAsString(FacesContext facesContext, UIComponent component) {
124     Iterator messages = facesContext.getMessages(
125         component.getClientId(facesContext));
126     StringBuilder stringBuffer = new StringBuilder();
127     while (messages.hasNext()) {
128       FacesMessage message = (FacesMessage) messages.next();
129       stringBuffer.append(message.getDetail());
130     }
131     if (stringBuffer.length() > 0) {
132       return stringBuffer.toString();
133     } else {
134       return null;
135     }
136   }
137 
138   public static boolean isInPopup(UIComponent component) {
139     while (component != null) {
140       if (component instanceof AbstractUIPopup) {
141         return true;
142       }
143       component = component.getParent();
144     }
145     return false;
146   }
147 
148   public static void resetPage(FacesContext context) {
149     UIViewRoot view = context.getViewRoot();
150     if (view != null) {
151       view.getAttributes().remove(PAGE_KEY);
152     }
153   }
154 
155   /**
156    * Tries to walk up the parents to find the UIViewRoot, if not found, then go to FaceletContext's FacesContext for
157    * the view root.
158    */
159   public static UIViewRoot findViewRoot(FaceletContext faceletContext, UIComponent component) {
160     UIViewRoot viewRoot = findAncestor(component, UIViewRoot.class);
161     if (viewRoot != null) {
162       return viewRoot;
163     } else {
164       return faceletContext.getFacesContext().getViewRoot();
165     }
166   }
167 
168   public static AbstractUIPage findPage(FacesContext context, UIComponent component) {
169     UIViewRoot view = context.getViewRoot();
170     if (view != null) {
171       TransientStateHolder stateHolder = (TransientStateHolder) view.getAttributes().get(PAGE_KEY);
172       if (stateHolder == null || stateHolder.isEmpty()) {
173         AbstractUIPage page = findPage(component);
174         stateHolder = new TransientStateHolder(page);
175         context.getViewRoot().getAttributes().put(PAGE_KEY, stateHolder);
176       }
177       return (AbstractUIPage) stateHolder.get();
178     } else {
179       return findPage(component);
180     }
181   }
182 
183   public static AbstractUIPage findPage(UIComponent component) {
184     while (component != null) {
185       if (component instanceof AbstractUIPage) {
186         return (AbstractUIPage) component;
187       }
188       component = component.getParent();
189     }
190     return null;
191   }
192 
193   public static AbstractUIPage findPage(FacesContext facesContext) {
194     return findPageBreadthFirst(facesContext.getViewRoot());
195   }
196 
197   private static AbstractUIPage findPageBreadthFirst(UIComponent component) {
198     for (UIComponent child : component.getChildren()) {
199       if (child instanceof AbstractUIPage) {
200         return (AbstractUIPage) child;
201       }
202     }
203     for (UIComponent child : component.getChildren()) {
204       AbstractUIPage result = findPageBreadthFirst(child);
205       if (result != null) {
206         return result;
207       }
208     }
209     return null;
210   }
211 
212 
213   public static AbstractUIForm findForm(UIComponent component) {
214     while (component != null) {
215       if (component instanceof AbstractUIForm) {
216         return (AbstractUIForm) component;
217       }
218       component = component.getParent();
219     }
220     return null;
221   }
222 
223   public static <T> T findAncestor(UIComponent component, Class<T> type) {
224 
225     while (component != null) {
226       if (type.isAssignableFrom(component.getClass())) {
227         return (T) component;
228       }
229       component = component.getParent();
230     }
231     return null;
232   }
233 
234   /**
235    * Find all sub forms of a component, and collects it.
236    * It does not find sub forms of sub forms.
237    */
238   public static List<AbstractUIForm> findSubForms(UIComponent component) {
239     List<AbstractUIForm> collect = new ArrayList<AbstractUIForm>();
240     findSubForms(collect, component);
241     return collect;
242   }
243 
244   @SuppressWarnings("unchecked")
245   private static void findSubForms(List<AbstractUIForm> collect, UIComponent component) {
246     Iterator<UIComponent> kids = component.getFacetsAndChildren();
247     while (kids.hasNext()) {
248       UIComponent child = kids.next();
249       if (child instanceof AbstractUIForm) {
250         collect.add((AbstractUIForm) child);
251       } else {
252         findSubForms(collect, child);
253       }
254     }
255   }
256 
257   /**
258    * Searches the component tree beneath the component and return the first component matching the type.
259    */
260   public static <T extends UIComponent> T findDescendant(UIComponent component, Class<T> type) {
261 
262     for (UIComponent child : component.getChildren()) {
263       if (type.isAssignableFrom(child.getClass())) {
264         return (T) child;
265       }
266       final T descendant = findDescendant(child, type);
267       if (descendant != null) {
268         return descendant;
269       }
270     }
271     return null;
272   }
273 
274   /**
275    * Searches the component tree beneath the component and return all component matching the type.
276    */
277   public static <T extends UIComponent> List<T> findDescendantList(UIComponent component, Class<T> type) {
278 
279     List<T> result = new ArrayList<T>();
280     
281     for (UIComponent child : component.getChildren()) {
282       if (type.isAssignableFrom(child.getClass())) {
283         result.add((T) child);
284       }
285       result.addAll(findDescendantList(child, type));
286     }
287     return result;
288   }
289 
290   /**
291    * Looks for the attribute "for" in the component. If there is any
292    * search for the component which is referenced by the "for" attribute,
293    * and return their clientId.
294    * If there is no "for" attribute, return the "clientId" of the parent
295    * (if it has a parent). This is useful for labels.
296    */
297   public static String findClientIdFor(UIComponent component, FacesContext facesContext) {
298     UIComponent forComponent = findFor(component);
299     if (forComponent != null) {
300       String clientId = forComponent.getClientId(facesContext);
301       if (LOG.isDebugEnabled()) {
302         LOG.debug("found clientId: '" + clientId + "'");
303       }
304       return clientId;
305     }
306     if (LOG.isDebugEnabled()) {
307       LOG.debug("found no clientId");
308     }
309     return null;
310   }
311 
312   public static UIComponent findFor(UIComponent component) {
313     String forValue = (String) component.getAttributes().get(Attributes.FOR);
314     if (forValue == null) {
315       return component.getParent();
316     }
317     return ComponentUtils.findComponent(component, forValue);
318   }
319 
320   /**
321    * Looks for the attribute "for" of the component.
322    * In case that the value is equals to "@auto" the children of the parent will be
323    * checked if they are a UIInput. The "id" of the first one will be used to reset the "for"
324    * attribute of the component.
325    * @deprecated
326    */
327   @Deprecated
328   public static void evaluateAutoFor(UIComponent component) {
329     String forComponent = (String) component.getAttributes().get(Attributes.FOR);
330     if (LOG.isDebugEnabled()) {
331       LOG.debug("for = '" + forComponent + "'");
332     }
333     if ("@auto".equals(forComponent)) {
334       for (UIComponent child : component.getParent().getChildren()) {
335         if (setForToInput(component, child, AbstractUIInput.class, false)) {
336           break;
337         }
338       }
339     }
340   }
341 
342   /**
343    * Looks for the attribute "for" of the component.
344    * In case that the value is equals to "@auto" the children of the parent will be
345    * checked if they are of the type of the parameter clazz. The "id" of the first one will be used to reset the "for"
346    * attribute of the component.
347    */
348   public static void evaluateAutoFor(UIComponent component, Class<? extends UIComponent> clazz) {
349     String forComponent = (String) component.getAttributes().get(Attributes.FOR);
350     if (LOG.isDebugEnabled()) {
351       LOG.debug("for = '" + forComponent + "'");
352     }
353     if ("@auto".equals(forComponent)) {
354       // parent
355       for (UIComponent child : component.getParent().getChildren()) {
356         if (setForToInput(component, child, clazz, component instanceof NamingContainer)) {
357           return;
358         }
359       }
360       // grand parent
361       for (UIComponent child : component.getParent().getParent().getChildren()) {
362         if (setForToInput(component, child, clazz, component.getParent() instanceof NamingContainer)) {
363           return;
364         }
365       }
366     }
367   }
368 
369   private static boolean setForToInput(
370       UIComponent component, UIComponent child, Class<? extends UIComponent> clazz, boolean namingContainer) {
371     if (clazz.isAssignableFrom(child.getClass())) { // find the matching component
372       final String forComponent;
373       if (namingContainer) {
374         forComponent = ":::" + child.getId();
375       } else {
376         forComponent = child.getId();
377       }
378       component.getAttributes().put(Attributes.FOR, forComponent);
379       return true;
380     }
381     return false;
382   }
383 
384   public static boolean isInActiveForm(UIComponent component) {
385     while (component != null) {
386       if (component instanceof AbstractUIForm) {
387         AbstractUIForm form = (AbstractUIForm) component;
388         if (form.isSubmitted()) {
389           return true;
390         }
391       }
392       component = component.getParent();
393     }
394     return false;
395   }
396 
397   public static FacesMessage.Severity getMaximumSeverity(UIComponent component) {
398     final boolean invalid = component instanceof UIInput && !((UIInput) component).isValid();
399     FacesMessage.Severity max = invalid ? FacesMessage.SEVERITY_ERROR : null;
400     FacesContext facesContext = FacesContext.getCurrentInstance();
401     final Iterator messages = facesContext.getMessages(component.getClientId(facesContext));
402     while (messages.hasNext()) {
403       FacesMessage message = (FacesMessage) messages.next();
404       if (max == null || message.getSeverity().getOrdinal() > max.getOrdinal()) {
405         max = message.getSeverity();
406       }
407     }
408     return max;
409   }
410 
411   public static boolean isError(UIInput uiInput) {
412     FacesContext facesContext = FacesContext.getCurrentInstance();
413     return !uiInput.isValid()
414         || facesContext.getMessages(uiInput.getClientId(facesContext)).hasNext();
415   }
416 
417   public static boolean isError(UIComponent component) {
418     if (component instanceof AbstractUIInput) {
419       return isError((AbstractUIInput) component);
420     }
421     return false;
422   }
423 
424   public static boolean isOutputOnly(UIComponent component) {
425     return getBooleanAttribute(component, Attributes.DISABLED)
426         || getBooleanAttribute(component, Attributes.READONLY);
427   }
428 
429   public static boolean mayValidate(UIComponent component) {
430     return !isOutputOnly(component)
431         && ComponentUtils.isInActiveForm(component);
432   }
433 
434   public static boolean mayUpdateModel(UIComponent component) {
435     return mayValidate(component);
436   }
437 
438   public static boolean getBooleanAttribute(UIComponent component, String name) {
439 
440     Object bool = component.getAttributes().get(name);
441     if (bool == null) {
442       return false;
443     }
444     if (bool instanceof ValueBinding) {
445       bool = ((ValueBinding) bool).getValue(FacesContext.getCurrentInstance());
446     }
447     if (bool instanceof Boolean) {
448       return (Boolean) bool;
449     } else if (bool instanceof String) {
450       LOG.warn("Searching for a boolean, but find a String. Should not happen. "
451           + "attribute: '" + name + "' id: '" + component.getClientId(FacesContext.getCurrentInstance())
452           + "' comp: '" + component + "'");
453       return Boolean.valueOf((String) bool);
454     } else {
455       LOG.warn("Unknown type '" + bool.getClass().getName()
456           + "' for boolean attribute: " + name + " id: " + component.getClientId(FacesContext.getCurrentInstance())
457           + " comp: " + component);
458       return false;
459     }
460   }
461 
462 
463 
464   public static ValueBinding createValueBinding(String value) {
465     return FacesContext.getCurrentInstance().getApplication().createValueBinding(value);
466   }
467 
468   /**
469    * @deprecated Please define a {@link Markup} and set it to the component with
470    * {@link SupportsMarkup#setMarkup(Markup markup)} before the rendering phase.
471    */
472   @Deprecated
473   public static void setStyleClasses(UIComponent component, String styleClasses) {
474     if (styleClasses != null) {
475       if (UIComponentTag.isValueReference(styleClasses)) {
476         component.setValueBinding(Attributes.STYLE_CLASS, createValueBinding(styleClasses));
477       } else {
478         String[] classes = splitList(styleClasses);
479         if (classes.length > 0) {
480           StyleClasses styles = StyleClasses.ensureStyleClasses(component);
481           for (String clazz : classes) {
482             styles.addFullQualifiedClass(clazz);
483           }
484         }
485       }
486     }
487   }
488 
489   /**
490    * @deprecated since 1.5.0
491    */
492   @Deprecated
493   public static void setMarkup(UIComponent markupComponent, String markup) {
494     if (markup != null) {
495       if (markupComponent instanceof SupportsMarkup) {
496         if (UIComponentTag.isValueReference(markup)) {
497           markupComponent.setValueBinding(Attributes.MARKUP, createValueBinding(markup));
498         } else {
499           ((SupportsMarkup) markupComponent).setMarkup(Markup.valueOf(markup));
500         }
501       } else {
502         LOG.error("Component did not support Markup " + markupComponent.getClass().getName());
503       }
504     }
505   }
506 
507   public static Object getAttribute(UIComponent component, String name) {
508     Object value = component.getAttributes().get(name);
509     if (value instanceof ValueBinding) {
510       value = ((ValueBinding) value).getValue(FacesContext.getCurrentInstance());
511     }
512     return value;
513   }
514 
515   public static Object getObjectAttribute(UIComponent component, String name) {
516     return getAttribute(component, name);
517   }
518 
519   public static String getStringAttribute(UIComponent component, String name) {
520     return (String) getAttribute(component, name);
521   }
522 
523   public static int getIntAttribute(UIComponent component, String name) {
524     return getIntAttribute(component, name, 0);
525   }
526 
527   public static int getIntAttribute(UIComponent component, String name,
528       int defaultValue) {
529     Object integer = component.getAttributes().get(name);
530     if (integer instanceof Number) {
531       return ((Number) integer).intValue();
532     } else if (integer instanceof String) {
533       try {
534         return Integer.parseInt((String) integer);
535       } catch (NumberFormatException e) {
536         LOG.warn("Can't parse number from string : \"" + integer + "\"!");
537         return defaultValue;
538       }
539     } else if (integer == null) {
540       return defaultValue;
541     } else {
542       LOG.warn("Unknown type '" + integer.getClass().getName()
543           + "' for integer attribute: " + name + " comp: " + component);
544       return defaultValue;
545     }
546   }
547 
548   public static Character getCharacterAttribute(UIComponent component, String name) {
549     Object character = component.getAttributes().get(name);
550     if (character == null) {
551       return null;
552     } else if (character instanceof Character) {
553       return ((Character) character);
554     } else if (character instanceof String) {
555       String asString = ((String) character);
556       return asString.length() > 0 ? asString.charAt(0) : null;
557     } else {
558       LOG.warn("Unknown type '" + character.getClass().getName()
559           + "' for integer attribute: " + name + " comp: " + component);
560       return null;
561     }
562   }
563 
564   public static boolean isFacetOf(UIComponent component, UIComponent parent) {
565     for (Object o : parent.getFacets().keySet()) {
566       UIComponent facet = parent.getFacet((String) o);
567       if (component.equals(facet)) {
568         return true;
569       }
570     }
571     return false;
572   }
573 
574   public static RendererBase getRenderer(FacesContext facesContext, UIComponent component) {
575     return getRenderer(facesContext, component.getFamily(), component.getRendererType());
576   }
577 
578   public static RendererBase getRenderer(FacesContext facesContext, String family, String rendererType) {
579     if (rendererType == null) {
580       return null;
581     }
582 
583     Map<String, Object> requestMap = (Map<String, Object>) facesContext.getExternalContext().getRequestMap();
584     StringBuilder key = new StringBuilder(RENDER_KEY_PREFIX);
585     key.append(rendererType);
586     RendererBase renderer = (RendererBase) requestMap.get(key.toString());
587 
588     if (renderer == null) {
589       Renderer myRenderer = getRendererInternal(facesContext, family, rendererType);
590       if (myRenderer instanceof RendererBase) {
591         requestMap.put(key.toString(), myRenderer);
592         renderer = (RendererBase) myRenderer;
593       } else {
594         return null;
595       }
596     }
597     return renderer;
598   }
599 
600 
601   private static Renderer getRendererInternal(FacesContext facesContext, String family, String rendererType) {
602     RenderKitFactory rkFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
603     RenderKit renderKit = rkFactory.getRenderKit(facesContext, facesContext.getViewRoot().getRenderKitId());
604     Renderer myRenderer = renderKit.getRenderer(family, rendererType);
605     return myRenderer;
606   }
607 
608   public static Object findParameter(UIComponent component, String name) {
609     for (UIComponent child : component.getChildren()) {
610       if (child instanceof UIParameter) {
611         UIParameter parameter = (UIParameter) child;
612         if (LOG.isDebugEnabled()) {
613           LOG.debug("Select name='" + parameter.getName() + "'");
614           LOG.debug("Select value='" + parameter.getValue() + "'");
615         }
616         if (name.equals(parameter.getName())) {
617           return parameter.getValue();
618         }
619       }
620     }
621     return null;
622   }
623 
624   public static ActionListener createActionListener(String type)
625       throws JspException {
626     try {
627       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
628       if (classLoader == null) {
629         classLoader = type.getClass().getClassLoader();
630       }
631       Class clazz = classLoader.loadClass(type);
632       return (ActionListener) clazz.newInstance();
633     } catch (Exception e) {
634       if (LOG.isDebugEnabled()) {
635         LOG.debug("type=" + type, e);
636       }
637       throw new JspException(e);
638     }
639   }
640 
641   public static UIGraphic getFirstGraphicChild(UIComponent component) {
642     UIGraphic graphic = null;
643     for (UIComponent child : component.getChildren()) {
644       if (child instanceof UIGraphic) {
645         graphic = (UIGraphic) child;
646         break;
647       }
648     }
649     return graphic;
650   }
651 
652   public static boolean isHoverEnabled(UIComponent component) {
653     return ComponentUtils.getBooleanAttribute(component, Attributes.HOVER);
654   }
655 
656   public static UIOutput getFirstNonGraphicChild(UIComponent component) {
657     for (UIComponent child : component.getChildren()) {
658       if (child instanceof UIOutput) {
659         return (UIOutput) child;
660       }
661     }
662     return null;
663   }
664 
665   /**
666    * @deprecated since 1.5.0
667    */
668   @Deprecated
669   public static void setIntegerSizeProperty(UIComponent component, String name, String value) {
670     if (value != null) {
671       if (UIComponentTag.isValueReference(value)) {
672         component.setValueBinding(name, createValueBinding(value));
673       } else {
674         value = removePx(value);
675         component.getAttributes().put(name, new Integer(value));
676       }
677     }
678   }
679 
680   public static String removePx(String value) {
681     if (value != null && value.endsWith("px")) {
682       value = value.substring(0, value.length() - 2);
683     }
684     return value;
685   }
686 
687   public static void setValueForValueBinding(String name, Object value) {
688     FacesContext context = FacesContext.getCurrentInstance();
689     ValueBinding valueBinding = context.getApplication().createValueBinding(name);
690     valueBinding.setValue(context, value);
691   }
692 
693 
694   public static boolean hasSelectedValue(List<SelectItem> items, Object value) {
695     for (SelectItem item : items) {
696       if (item.getValue().equals(value)) {
697         return true;
698       }
699     }
700     return false;
701   }
702 
703   public static int getIntValue(ValueBinding valueBinding) {
704     return getAsInt(valueBinding.getValue(FacesContext.getCurrentInstance()));
705   }
706 
707   private static int getAsInt(Object value) {
708     int result;
709     if (value instanceof Number) {
710       result = ((Number) value).intValue();
711     } else if (value instanceof String) {
712       result = Integer.parseInt((String) value);
713     } else {
714       throw new IllegalArgumentException("Can't convert " + value + " to int!");
715     }
716     return result;
717   }
718 
719 
720   public static String createPickerId(FacesContext facesContext, UIComponent component, String postfix) {
721     //String id = component.getId();
722     String id = getComponentId(facesContext, component);
723     return id + "_picker" + postfix;
724   }
725 
726   public static String getComponentId(FacesContext facesContext, UIComponent component) {
727     String id = component.getId();
728     //if (id == null) {
729     // XXX What is this?
730     //  id = component.getClientId(facesContext).substring(id.lastIndexOf('_'));
731     //}
732     return id;
733   }
734 
735   /**
736    * Checks if the Component has a label facet and if not creates one with the label attribute.
737    *
738    * Todo: check if this method should be set to deprecated. 
739    */
740   public static UIComponent provideLabel(FacesContext facesContext, UIComponent component) {
741     UIComponent label = component.getFacet(Facets.LABEL);
742 
743 
744     if (label == null) {
745       final Map attributes = component.getAttributes();
746       Object labelText = component.getValueBinding(Attributes.LABEL);
747       if (labelText == null) {
748         labelText = attributes.get(Attributes.LABEL);
749       }
750 
751       if (labelText != null) {
752         Application application = FacesContext.getCurrentInstance().getApplication();
753         label = application.createComponent(UIOutput.COMPONENT_TYPE);
754         label.setRendererType(RendererTypes.LABEL);
755         String idprefix = ComponentUtils.getComponentId(facesContext, component);
756         label.setId(idprefix + "_" + Facets.LABEL);
757         label.setRendered(true);
758 
759         if (labelText instanceof ValueBinding) {
760           label.setValueBinding(Attributes.VALUE, (ValueBinding) labelText);
761         } else {
762           label.getAttributes().put(Attributes.VALUE, labelText);
763         }
764 
765         component.getFacets().put(Facets.LABEL, label);
766       }
767     }
768     return label;
769   }
770 
771   /**
772    * @deprecated since 1.5.0
773    */
774   @Deprecated
775   public static void setValidator(EditableValueHolder editableValueHolder, String validator) {
776     if (validator != null && editableValueHolder.getValidator() == null) {
777       if (UIComponentTag.isValueReference(validator)) {
778         MethodBinding methodBinding =
779             FacesContext.getCurrentInstance().getApplication().createMethodBinding(validator, VALIDATOR_ARGS);
780         editableValueHolder.setValidator(methodBinding);
781       }
782     }
783   }
784 
785   /**
786    * @deprecated since 1.5.0
787    */
788   @Deprecated
789   public static void setConverter(ValueHolder valueHolder, String converterId) {
790     if (converterId != null && valueHolder.getConverter() == null) {
791       final FacesContext facesContext = FacesContext.getCurrentInstance();
792       final Application application = facesContext.getApplication();
793       if (UIComponentTag.isValueReference(converterId)) {
794         ValueBinding valueBinding = application.createValueBinding(converterId);
795         if (valueHolder instanceof UIComponent) {
796           ((UIComponent) valueHolder).setValueBinding(Attributes.CONVERTER, valueBinding);
797         }
798       } else {
799         Converter converter = application.createConverter(converterId);
800         valueHolder.setConverter(converter);
801       }
802     }
803   }
804 
805   /**
806    * @deprecated since 1.5.0
807    */
808   @Deprecated
809   public static void setAction(ActionSource component, String action) {
810     if (action != null) {
811       if (UIComponentTag.isValueReference(action)) {
812         final FacesContext facesContext = FacesContext.getCurrentInstance();
813         final Application application = facesContext.getApplication();
814         MethodBinding binding = application.createMethodBinding(action, null);
815         component.setAction(binding);
816       } else {
817         component.setAction(new ConstantMethodBinding(action));
818       }
819     }
820   }
821 
822   /**
823    * @deprecated since 1.5.0
824    */
825   @Deprecated
826   public static void setActionListener(ActionSource command, String actionListener) {
827     final FacesContext facesContext = FacesContext.getCurrentInstance();
828     final Application application = facesContext.getApplication();
829     if (actionListener != null) {
830       if (UIComponentTag.isValueReference(actionListener)) {
831         MethodBinding binding
832             = application.createMethodBinding(actionListener, ACTION_LISTENER_ARGS);
833         command.setActionListener(binding);
834       } else {
835         throw new IllegalArgumentException(
836             "Must be a valueReference (actionListener): " + actionListener);
837       }
838     }
839   }
840 
841   /**
842    * @deprecated since 1.5.0
843    */
844   @Deprecated
845   public static void setValueChangeListener(EditableValueHolder valueHolder, String valueChangeListener) {
846     final FacesContext facesContext = FacesContext.getCurrentInstance();
847     final Application application = facesContext.getApplication();
848     if (valueChangeListener != null) {
849       if (UIComponentTag.isValueReference(valueChangeListener)) {
850         MethodBinding binding
851             = application.createMethodBinding(valueChangeListener, VALUE_CHANGE_LISTENER_ARGS);
852         valueHolder.setValueChangeListener(binding);
853       } else {
854         throw new IllegalArgumentException(
855             "Must be a valueReference (valueChangeListener): " + valueChangeListener);
856       }
857     }
858   }
859 
860   /**
861    * @deprecated since 1.5.0
862    */
863   @Deprecated
864   public static void setValueBinding(UIComponent component, String name, String state) {
865     // TODO: check, if it is an writeable object
866     if (state != null && UIComponentTag.isValueReference(state)) {
867       ValueBinding valueBinding = createValueBinding(state);
868       component.setValueBinding(name, valueBinding);
869     }
870   }
871 
872   /**
873    * @deprecated since 1.5
874    */
875   @Deprecated
876   public static String[] getMarkupBinding(FacesContext facesContext, SupportsMarkup component) {
877     ValueBinding vb = ((UIComponent) component).getValueBinding(Attributes.MARKUP);
878     if (vb != null) {
879       Object markups = vb.getValue(facesContext);
880       if (markups instanceof String[]) {
881         return (String[]) markups;
882       } else if (markups instanceof String) {
883         String[] strings = StringUtils.split((String) markups, ", ");
884         List<String> result = new ArrayList<String>(strings.length);
885         for (String string : strings) {
886           if (string.trim().length() != 0) {
887             result.add(string.trim());
888           }
889         }
890         return result.toArray(new String[result.size()]);
891       } else if (markups == null) {
892         return ArrayUtils.EMPTY_STRING_ARRAY;
893       } else {
894         return new String[]{markups.toString()};
895       }
896     }
897 
898     return ArrayUtils.EMPTY_STRING_ARRAY;
899   }
900 
901   /**
902    * The search depends on the number of colons in the relativeId:
903    * <dl>
904    *   <dd>colonCount == 0</dd>
905    *   <dt>fully relative</dt>
906    *   <dd>colonCount == 1</dd>
907    *   <dt>absolute (still normal findComponent syntax)</dt>
908    *   <dd>colonCount > 1</dd>
909    *   <dt>for each extra colon after 1, go up a naming container</dt>
910    * </dl>
911    * (to the view root, if naming containers run out)
912    */
913   public static UIComponent findComponent(UIComponent from, String relativeId) {
914     return FindComponentUtils.findComponent(from, relativeId);
915   }
916 
917   public static String[] splitList(String renderers) {
918     return StringUtils.split(renderers, LIST_SEPARATOR_CHARS);
919   }
920 
921   public static Object getConvertedValue(
922       FacesContext facesContext, UIComponent component, String stringValue) {
923     try {
924       Renderer renderer = getRenderer(facesContext, component);
925       if (renderer != null) {
926         if (component instanceof UISelectMany) {
927           final Object converted = renderer.getConvertedValue(facesContext, component, new String[]{stringValue});
928           return converted instanceof List ? ((List) converted).get(0) : ((Object[]) converted)[0];
929         } else {
930           return renderer.getConvertedValue(facesContext, component, stringValue);
931         }
932       } else if (component instanceof ValueHolder) {
933         Converter converter = ((ValueHolder) component).getConverter();
934         if (converter == null) {
935           //Try to find out by value binding
936           ValueBinding vb = component.getValueBinding("value");
937           if (vb != null) {
938             Class valueType = vb.getType(facesContext);
939             if (valueType != null) {
940               converter = facesContext.getApplication().createConverter(valueType);
941             }
942           }
943         }
944         if (converter != null) {
945           converter.getAsObject(facesContext, component, stringValue);
946         }
947       }
948     } catch (Exception e) {
949       LOG.warn("Can't convert string value '" + stringValue + "'", e);
950     }
951     return stringValue;
952   }
953 
954   public static Markup updateMarkup(UIComponent component, Markup markup) {
955     if (markup == null) {
956       markup = Markup.NULL;
957     }
958     if (ComponentUtils.getBooleanAttribute(component, Attributes.DISABLED)) {
959       markup = markup.add(Markup.DISABLED);
960     }
961     if (ComponentUtils.getBooleanAttribute(component, Attributes.READONLY)) {
962       markup = markup.add(Markup.READONLY);
963     }
964     if (component instanceof UIInput) {
965       UIInput input = (UIInput) component;
966 
967       final FacesMessage.Severity maximumSeverity = ComponentUtils.getMaximumSeverity(input);
968       markup = markup.add(markupOfSeverity(maximumSeverity));
969 
970       if (input.isRequired()) {
971         markup = markup.add(Markup.REQUIRED);
972       }
973     }
974     return markup;
975   }
976 
977   public static Markup markupOfSeverity(FacesMessage.Severity maximumSeverity) {
978     if (FacesMessage.SEVERITY_FATAL.equals(maximumSeverity)) {
979       return Markup.FATAL;
980     } else if (FacesMessage.SEVERITY_ERROR.equals(maximumSeverity)) {
981       return Markup.ERROR;
982     } else if (FacesMessage.SEVERITY_WARN.equals(maximumSeverity)) {
983       return Markup.WARN;
984     } else if (FacesMessage.SEVERITY_INFO.equals(maximumSeverity)) {
985       return Markup.INFO;
986     }
987     return null;
988   }
989 
990   public static void addCurrentMarkup(SupportsMarkup component, Markup markup) {
991     component.setCurrentMarkup(markup.add(component.getCurrentMarkup()));
992   }
993 
994   public static boolean hasChildrenWithMessages(FacesContext facesContext, NamingContainer  container) {
995     if (container instanceof UIComponent) {
996       String clientId = ((UIComponent) container).getClientId(facesContext);
997       for (Iterator ids = facesContext.getClientIdsWithMessages(); ids.hasNext();) {
998         String id = (String) ids.next();
999         if (id.startsWith(clientId)) {
1000           return true;
1001         }
1002       }
1003     }
1004     return false;
1005   }
1006 
1007   public static FacesMessage.Severity getMaximumSeverityOfChildrenMessages(
1008       FacesContext facesContext, NamingContainer container) {
1009     if (container instanceof UIComponent) {
1010       String clientId = ((UIComponent) container).getClientId(facesContext);
1011       FacesMessage.Severity max = null;
1012       for (Iterator ids = facesContext.getClientIdsWithMessages(); ids.hasNext();) {
1013         String id = (String) ids.next();
1014         if (id != null && id.startsWith(clientId)) {
1015           final Iterator messages = facesContext.getMessages(id);
1016           while (messages.hasNext()) {
1017             FacesMessage message = (FacesMessage) messages.next();
1018             if (max == null || message.getSeverity().getOrdinal() > max.getOrdinal()) {
1019               max = message.getSeverity();
1020             }
1021           }
1022         }
1023       }
1024       return max;
1025     }
1026     return null;
1027   }
1028 
1029   public static String[] getChildrenWithMessages(FacesContext facesContext, NamingContainer container) {
1030     if (container instanceof UIComponent) {
1031       List<String> clientIds = new ArrayList<String>();
1032       String clientId = ((UIComponent) container).getClientId(facesContext);
1033       for (Iterator ids = facesContext.getClientIdsWithMessages(); ids.hasNext();) {
1034         String id = (String) ids.next();
1035         if (id.startsWith(clientId)) {
1036           clientIds.add(id);
1037         }
1038       }
1039       return clientIds.toArray(new String[clientIds.size()]);
1040     }
1041     return ArrayUtils.EMPTY_STRING_ARRAY;
1042   }
1043 
1044   /**
1045    * Adding a data attribute to the component. 
1046    * The name must start with "data-", e. g. "data-tobago-foo" or "data-bar"
1047    */
1048   public static void putDataAttributeWithPrefix(UIComponent component, String name, Object value) {
1049     if (name.startsWith("data-")) {
1050       putDataAttribute(component, name.substring(5), value);
1051     } else {
1052       LOG.error("The name must start with 'data-' but it doesn't: '" + name + "'");
1053     }
1054   }
1055 
1056   /**
1057    * Adding a data attribute to the component.
1058    * The name should not start with "data-", e. g. "tobago-foo" or "bar"
1059    */
1060   public static void putDataAttribute(UIComponent component, Object name, Object value) {
1061     Map<Object, Object> map = (Map<Object, Object>) component.getAttributes().get(DATA_ATTRIBUTES_KEY);
1062     if (map == null) {
1063       map = new HashMap<Object, Object>();
1064       component.getAttributes().put(DATA_ATTRIBUTES_KEY, map);
1065     }
1066     map.put(name, value);
1067   }
1068 
1069   public static Map<Object, Object> getDataAttributes(UIComponent component) {
1070     return (Map<Object, Object>) component.getAttributes().get(DATA_ATTRIBUTES_KEY);
1071   }
1072 
1073   public static boolean invokeOnComponent(
1074       FacesContext context, UIComponent component, String clientId, javax.faces.component.ContextCallback callback) {
1075     String thisClientId = component.getClientId(context);
1076 
1077     if (clientId.equals(thisClientId)) {
1078       callback.invokeContextCallback(context, component);
1079       return true;
1080     } else if (component instanceof NamingContainer) {
1081       // This component is a naming container. If the client id shows it's inside this naming container,
1082       // then process further.
1083       // Otherwise we know the client id we're looking for is not in this naming container,
1084       // so for improved performance short circuit and return false.
1085       if (clientId.startsWith(thisClientId)
1086           && (clientId.charAt(thisClientId.length()) == NamingContainer.SEPARATOR_CHAR)) {
1087         if (invokeOnComponentFacetsAndChildren(context, component, clientId, callback)) {
1088           return true;
1089         }
1090       }
1091     } else {
1092       if (invokeOnComponentFacetsAndChildren(context, component, clientId, callback)) {
1093         return true;
1094       }
1095     }
1096 
1097     return false;
1098   }
1099 
1100   private static boolean invokeOnComponentFacetsAndChildren(
1101       FacesContext context, UIComponent component, String clientId, javax.faces.component.ContextCallback callback) {
1102     for (java.util.Iterator<UIComponent> it = component.getFacetsAndChildren(); it.hasNext();) {
1103       UIComponent child = it.next();
1104       if (child.invokeOnComponent(context, clientId, callback)) {
1105         return true;
1106       }
1107     }
1108     return false;
1109   }
1110 
1111 }