001    package org.apache.myfaces.tobago.util;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one or more
005     * contributor license agreements.  See the NOTICE file distributed with
006     * this work for additional information regarding copyright ownership.
007     * The ASF licenses this file to You under the Apache License, Version 2.0
008     * (the "License"); you may not use this file except in compliance with
009     * the License.  You may obtain a copy of the License at
010     *
011     *      http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    import org.apache.commons.lang.ArrayUtils;
021    import org.apache.commons.lang.StringUtils;
022    import org.apache.myfaces.tobago.component.Attributes;
023    import org.apache.myfaces.tobago.component.Facets;
024    import org.apache.myfaces.tobago.component.RendererTypes;
025    import org.apache.myfaces.tobago.component.SupportsMarkup;
026    import org.apache.myfaces.tobago.context.Markup;
027    import org.apache.myfaces.tobago.context.TransientStateHolder;
028    import org.apache.myfaces.tobago.el.ConstantMethodBinding;
029    import org.apache.myfaces.tobago.event.AbstractPopupActionListener;
030    import org.apache.myfaces.tobago.internal.component.AbstractUIForm;
031    import org.apache.myfaces.tobago.internal.component.AbstractUIInput;
032    import org.apache.myfaces.tobago.internal.component.AbstractUIPage;
033    import org.apache.myfaces.tobago.internal.component.AbstractUIPopup;
034    import org.apache.myfaces.tobago.internal.util.FindComponentUtils;
035    import org.apache.myfaces.tobago.renderkit.RendererBase;
036    import org.apache.myfaces.tobago.renderkit.html.StyleClasses;
037    import org.slf4j.Logger;
038    import org.slf4j.LoggerFactory;
039    
040    import javax.faces.FactoryFinder;
041    import javax.faces.application.Application;
042    import javax.faces.application.FacesMessage;
043    import javax.faces.component.ActionSource;
044    import javax.faces.component.EditableValueHolder;
045    import javax.faces.component.NamingContainer;
046    import javax.faces.component.UICommand;
047    import javax.faces.component.UIComponent;
048    import javax.faces.component.UIGraphic;
049    import javax.faces.component.UIInput;
050    import javax.faces.component.UIOutput;
051    import javax.faces.component.UIParameter;
052    import javax.faces.component.UISelectMany;
053    import javax.faces.component.ValueHolder;
054    import javax.faces.context.FacesContext;
055    import javax.faces.convert.Converter;
056    import javax.faces.el.MethodBinding;
057    import javax.faces.el.ValueBinding;
058    import javax.faces.event.ActionEvent;
059    import javax.faces.event.ActionListener;
060    import javax.faces.event.ValueChangeEvent;
061    import javax.faces.model.SelectItem;
062    import javax.faces.render.RenderKit;
063    import javax.faces.render.RenderKitFactory;
064    import javax.faces.render.Renderer;
065    import javax.faces.webapp.UIComponentTag;
066    import javax.servlet.jsp.JspException;
067    import java.util.ArrayList;
068    import java.util.Iterator;
069    import java.util.List;
070    import java.util.Map;
071    
072    public class ComponentUtils {
073    
074      private static final Logger LOG = LoggerFactory.getLogger(ComponentUtils.class);
075    
076      public static final String SUB_SEPARATOR = "::";
077      
078      private static final String RENDER_KEY_PREFIX
079          = "org.apache.myfaces.tobago.util.ComponentUtils.RendererKeyPrefix_";
080    
081      private static final String PAGE_KEY = "org.apache.myfaces.tobago.Page.Key";
082    
083      public static final Class[] ACTION_ARGS = {};
084      public static final Class[] ACTION_LISTENER_ARGS = {ActionEvent.class};
085      public static final Class[] VALUE_CHANGE_LISTENER_ARGS = {ValueChangeEvent.class};
086      public static final Class[] VALIDATOR_ARGS = {FacesContext.class, UIComponent.class, Object.class};
087      public static final String LIST_SEPARATOR_CHARS = ", ";
088    
089      private ComponentUtils() {
090      }
091    
092      public static boolean hasErrorMessages(FacesContext context) {
093        for (Iterator iter = context.getMessages(); iter.hasNext();) {
094          FacesMessage message = (FacesMessage) iter.next();
095          if (FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0) {
096            return true;
097          }
098        }
099        return false;
100      }
101    
102      public static boolean containsPopupActionListener(UICommand command) {
103        ActionListener[] actionListeners = command.getActionListeners();
104        for (ActionListener actionListener : actionListeners) {
105          if (actionListener instanceof AbstractPopupActionListener) {
106            return true;
107          }
108        }
109        return false;
110      }
111    
112      public static String getFacesMessageAsString(FacesContext facesContext, UIComponent component) {
113        Iterator messages = facesContext.getMessages(
114            component.getClientId(facesContext));
115        StringBuilder stringBuffer = new StringBuilder();
116        while (messages.hasNext()) {
117          FacesMessage message = (FacesMessage) messages.next();
118          stringBuffer.append(message.getDetail());
119        }
120        if (stringBuffer.length() > 0) {
121          return stringBuffer.toString();
122        } else {
123          return null;
124        }
125      }
126    
127      public static boolean isInPopup(UIComponent component) {
128        while (component != null) {
129          if (component instanceof AbstractUIPopup) {
130            return true;
131          }
132          component = component.getParent();
133        }
134        return false;
135      }
136    
137      public static void resetPage(FacesContext context) {
138        javax.faces.component.UIViewRoot view = context.getViewRoot();
139        if (view != null) {
140          view.getAttributes().remove(PAGE_KEY);
141        }
142      }
143    
144      @SuppressWarnings("unchecked")
145      public static AbstractUIPage findPage(FacesContext context, UIComponent component) {
146        javax.faces.component.UIViewRoot view = context.getViewRoot();
147        if (view != null) {
148          TransientStateHolder stateHolder = (TransientStateHolder) view.getAttributes().get(PAGE_KEY);
149          if (stateHolder == null || stateHolder.isEmpty()) {
150            AbstractUIPage page = findPage(component);
151            stateHolder = new TransientStateHolder(page);
152            context.getViewRoot().getAttributes().put(PAGE_KEY, stateHolder);
153          }
154          return (AbstractUIPage) stateHolder.get();
155        } else {
156          return findPage(component);
157        }
158      }
159    
160      public static AbstractUIPage findPage(UIComponent component) {
161        while (component != null) {
162          if (component instanceof AbstractUIPage) {
163            return (AbstractUIPage) component;
164          }
165          component = component.getParent();
166        }
167        return null;
168      }
169    
170      public static AbstractUIPage findPage(FacesContext facesContext) {
171        return findPageBreadthFirst(facesContext.getViewRoot());
172      }
173    
174      private static AbstractUIPage findPageBreadthFirst(UIComponent component) {
175        for (Object o : component.getChildren()) {
176          UIComponent child = (UIComponent) o;
177          if (child instanceof AbstractUIPage) {
178            return (AbstractUIPage) child;
179          }
180        }
181        for (Object o : component.getChildren()) {
182          UIComponent child = (UIComponent) o;
183          AbstractUIPage result = findPageBreadthFirst(child);
184          if (result != null) {
185            return result;
186          }
187        }
188        return null;
189      }
190    
191    
192      public static AbstractUIForm findForm(UIComponent component) {
193        while (component != null) {
194          if (component instanceof AbstractUIForm) {
195            return (AbstractUIForm) component;
196          }
197          component = component.getParent();
198        }
199        return null;
200      }
201    
202      public static <T> T findAncestor(UIComponent component, Class<T> type) {
203    
204        while (component != null) {
205          if (type.isAssignableFrom(component.getClass())) {
206            return (T) component;
207          }
208          component = component.getParent();
209        }
210        return null;
211      }
212    
213      /**
214       * Find all sub forms of a component, and collects it.
215       * It does not find sub forms of sub forms.
216       */
217      public static List<AbstractUIForm> findSubForms(UIComponent component) {
218        List<AbstractUIForm> collect = new ArrayList<AbstractUIForm>();
219        findSubForms(collect, component);
220        return collect;
221      }
222    
223      @SuppressWarnings("unchecked")
224      private static void findSubForms(List<AbstractUIForm> collect, UIComponent component) {
225        Iterator<UIComponent> kids = component.getFacetsAndChildren();
226        while (kids.hasNext()) {
227          UIComponent child = kids.next();
228          if (child instanceof AbstractUIForm) {
229            collect.add((AbstractUIForm) child);
230          } else {
231            findSubForms(collect, child);
232          }
233        }
234      }
235    
236      /**
237       * Searches the component tree beneath the component and return the first component matching the type.
238       */
239      public static <T extends UIComponent> T findDescendant(UIComponent component, Class<T> type) {
240    
241        for (UIComponent child : (List<UIComponent>) component.getChildren()) {
242          if (type.isAssignableFrom(child.getClass())) {
243            return (T) child;
244          }
245          final T descendant = findDescendant(child, type);
246          if (descendant != null) {
247            return descendant;
248          }
249        }
250        return null;
251      }
252    
253      /**
254       * Looks for the attribute "for" in the component. If there is any
255       * search for the component which is referenced by the "for" attribute,
256       * and return their clientId.
257       * If there is no "for" attribute, return the "clientId" of the parent
258       * (if it has a parent). This is useful for labels.
259       */
260      public static String findClientIdFor(UIComponent component, FacesContext facesContext) {
261        UIComponent forComponent = findFor(component);
262        if (forComponent != null) {
263          String clientId = forComponent.getClientId(facesContext);
264          if (LOG.isDebugEnabled()) {
265            LOG.debug("found clientId: '" + clientId + "'");
266          }
267          return clientId;
268        }
269        if (LOG.isDebugEnabled()) {
270          LOG.debug("found no clientId");
271        }
272        return null;
273      }
274    
275      public static UIComponent findFor(UIComponent component) {
276        String forValue = (String) component.getAttributes().get(Attributes.FOR);
277        if (forValue == null) {
278          return component.getParent();
279        }
280        return component.findComponent(forValue);
281      }
282    
283      /**
284       * Looks for the attribute "for" of the component.
285       * In case that the value is equals to "@auto" the children of the parent will be
286       * checked if they are a UIInput. The "id" of the first one will be used to reset the "for"
287       * attribute of the component.
288       */
289      public static void evaluateAutoFor(UIComponent component) {
290        String forComponent = (String) component.getAttributes().get(Attributes.FOR);
291        if (LOG.isDebugEnabled()) {
292          LOG.debug("for = '" + forComponent + "'");
293        }
294        if ("@auto".equals(forComponent)) {
295          for (Object object : component.getParent().getChildren()) {
296            UIComponent child = (UIComponent) object;
297            if (child instanceof UIInput) {
298              forComponent = child.getId();
299              component.getAttributes().put(Attributes.FOR, forComponent);
300              break;
301            }
302          }
303        }
304      }
305    
306      public static boolean isInActiveForm(UIComponent component) {
307        while (component != null) {
308          if (component instanceof AbstractUIForm) {
309            AbstractUIForm form = (AbstractUIForm) component;
310            if (form.isSubmitted()) {
311              return true;
312            }
313          }
314          component = component.getParent();
315        }
316        return false;
317      }
318    
319      public static FacesMessage.Severity getMaximumSeverity(UIComponent component) {
320        final boolean invalid = component instanceof javax.faces.component.UIInput
321            && !((javax.faces.component.UIInput) component).isValid();
322        FacesMessage.Severity max = invalid ? FacesMessage.SEVERITY_ERROR : null;
323        FacesContext facesContext = FacesContext.getCurrentInstance();
324        final Iterator messages = facesContext.getMessages(component.getClientId(facesContext));
325        while (messages.hasNext()) {
326          FacesMessage message = (FacesMessage) messages.next();
327          if (max == null || message.getSeverity().getOrdinal() > max.getOrdinal()) {
328            max = message.getSeverity();
329          }
330        }
331        return max;
332      }
333    
334      public static boolean isError(javax.faces.component.UIInput uiInput) {
335        FacesContext facesContext = FacesContext.getCurrentInstance();
336        return !uiInput.isValid()
337            || facesContext.getMessages(uiInput.getClientId(facesContext)).hasNext();
338      }
339    
340      public static boolean isError(UIComponent component) {
341        if (component instanceof AbstractUIInput) {
342          return isError((AbstractUIInput) component);
343        }
344        return false;
345      }
346    
347      public static boolean isOutputOnly(UIComponent component) {
348        return getBooleanAttribute(component, Attributes.DISABLED)
349            || getBooleanAttribute(component, Attributes.READONLY);
350      }
351    
352      public static boolean mayValidate(UIComponent component) {
353        return !isOutputOnly(component)
354            && ComponentUtils.isInActiveForm(component);
355      }
356    
357      public static boolean mayUpdateModel(UIComponent component) {
358        return mayValidate(component);
359      }
360    
361      public static boolean getBooleanAttribute(UIComponent component, String name) {
362    
363        Object bool = component.getAttributes().get(name);
364        if (bool == null) {
365          return false;
366        }
367        if (bool instanceof ValueBinding) {
368          bool = ((ValueBinding) bool).getValue(FacesContext.getCurrentInstance());
369        }
370        if (bool instanceof Boolean) {
371          return (Boolean) bool;
372        } else if (bool instanceof String) {
373          LOG.warn("Searching for a boolean, but find a String. Should not happen. "
374              + "attribute: '" + name + "' id: '" + component.getClientId(FacesContext.getCurrentInstance())
375              + "' comp: '" + component + "'");
376          return Boolean.valueOf((String) bool);
377        } else {
378          LOG.warn("Unknown type '" + bool.getClass().getName()
379              + "' for boolean attribute: " + name + " id: " + component.getClientId(FacesContext.getCurrentInstance())
380              + " comp: " + component);
381          return false;
382        }
383      }
384    
385    
386    
387      public static ValueBinding createValueBinding(String value) {
388        return FacesContext.getCurrentInstance().getApplication().createValueBinding(value);
389      }
390    
391      /**
392       * @deprecated Please define a {@link Markup} and set it to the component with
393       * {@link SupportsMarkup#setMarkup(Markup markup)} before the rendering phase.
394       */
395      @Deprecated
396      public static void setStyleClasses(UIComponent component, String styleClasses) {
397        if (styleClasses != null) {
398          if (UIComponentTag.isValueReference(styleClasses)) {
399            component.setValueBinding(Attributes.STYLE_CLASS, createValueBinding(styleClasses));
400          } else {
401            String[] classes = splitList(styleClasses);
402            if (classes.length > 0) {
403              StyleClasses styles = StyleClasses.ensureStyleClasses(component);
404              for (String clazz : classes) {
405                styles.addFullQualifiedClass(clazz);
406              }
407            }
408          }
409        }
410      }
411    
412      /**
413       * @deprecated since 1.5.0
414       */
415      @Deprecated
416      public static void setMarkup(UIComponent markupComponent, String markup) {
417        if (markup != null) {
418          if (markupComponent instanceof SupportsMarkup) {
419            if (UIComponentTag.isValueReference(markup)) {
420              markupComponent.setValueBinding(Attributes.MARKUP, createValueBinding(markup));
421            } else {
422              ((SupportsMarkup) markupComponent).setMarkup(Markup.valueOf(markup));
423            }
424          } else {
425            LOG.error("Component did not support Markup " + markupComponent.getClass().getName());
426          }
427        }
428      }
429    
430      public static Object getAttribute(UIComponent component, String name) {
431        Object value = component.getAttributes().get(name);
432        if (value instanceof ValueBinding) {
433          value = ((ValueBinding) value).getValue(FacesContext.getCurrentInstance());
434        }
435        return value;
436      }
437    
438      public static Object getObjectAttribute(UIComponent component, String name) {
439        return getAttribute(component, name);
440      }
441    
442      public static String getStringAttribute(UIComponent component, String name) {
443        return (String) getAttribute(component, name);
444      }
445    
446      public static int getIntAttribute(UIComponent component, String name) {
447        return getIntAttribute(component, name, 0);
448      }
449    
450      public static int getIntAttribute(UIComponent component, String name,
451          int defaultValue) {
452        Object integer = component.getAttributes().get(name);
453        if (integer instanceof Number) {
454          return ((Number) integer).intValue();
455        } else if (integer instanceof String) {
456          try {
457            return Integer.parseInt((String) integer);
458          } catch (NumberFormatException e) {
459            LOG.warn("Can't parse number from string : \"" + integer + "\"!");
460            return defaultValue;
461          }
462        } else if (integer == null) {
463          return defaultValue;
464        } else {
465          LOG.warn("Unknown type '" + integer.getClass().getName()
466              + "' for integer attribute: " + name + " comp: " + component);
467          return defaultValue;
468        }
469      }
470    
471      public static Character getCharacterAttribute(UIComponent component, String name) {
472        Object character = component.getAttributes().get(name);
473        if (character == null) {
474          return null;
475        } else if (character instanceof Character) {
476          return ((Character) character);
477        } else if (character instanceof String) {
478          String asString = ((String) character);
479          return asString.length() > 0 ? asString.charAt(0) : null;
480        } else {
481          LOG.warn("Unknown type '" + character.getClass().getName()
482              + "' for integer attribute: " + name + " comp: " + component);
483          return null;
484        }
485      }
486    
487      public static boolean isFacetOf(UIComponent component, UIComponent parent) {
488        for (Object o : parent.getFacets().keySet()) {
489          UIComponent facet = parent.getFacet((String) o);
490          if (component.equals(facet)) {
491            return true;
492          }
493        }
494        return false;
495      }
496    
497      public static RendererBase getRenderer(FacesContext facesContext, UIComponent component) {
498        return getRenderer(facesContext, component.getFamily(), component.getRendererType());
499      }
500    
501      public static RendererBase getRenderer(FacesContext facesContext, String family, String rendererType) {
502        if (rendererType == null) {
503          return null;
504        }
505    
506        Map<String, Object> requestMap = (Map<String, Object>) facesContext.getExternalContext().getRequestMap();
507        StringBuilder key = new StringBuilder(RENDER_KEY_PREFIX);
508        key.append(rendererType);
509        RendererBase renderer = (RendererBase) requestMap.get(key.toString());
510    
511        if (renderer == null) {
512          Renderer myRenderer = getRendererInternal(facesContext, family, rendererType);
513          if (myRenderer instanceof RendererBase) {
514            requestMap.put(key.toString(), myRenderer);
515            renderer = (RendererBase) myRenderer;
516          } else {
517            return null;
518          }
519        }
520        return renderer;
521      }
522    
523    
524      private static Renderer getRendererInternal(FacesContext facesContext, String family, String rendererType) {
525        RenderKitFactory rkFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
526        RenderKit renderKit = rkFactory.getRenderKit(facesContext, facesContext.getViewRoot().getRenderKitId());
527        Renderer myRenderer = renderKit.getRenderer(family, rendererType);
528        return myRenderer;
529      }
530    
531      public static Object findParameter(UIComponent component, String name) {
532        for (Object o : component.getChildren()) {
533          UIComponent child = (UIComponent) o;
534          if (child instanceof UIParameter) {
535            UIParameter parameter = (UIParameter) child;
536            if (LOG.isDebugEnabled()) {
537              LOG.debug("Select name='" + parameter.getName() + "'");
538              LOG.debug("Select value='" + parameter.getValue() + "'");
539            }
540            if (name.equals(parameter.getName())) {
541              return parameter.getValue();
542            }
543          }
544        }
545        return null;
546      }
547    
548      public static ActionListener createActionListener(String type)
549          throws JspException {
550        try {
551          ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
552          if (classLoader == null) {
553            classLoader = type.getClass().getClassLoader();
554          }
555          Class clazz = classLoader.loadClass(type);
556          return (ActionListener) clazz.newInstance();
557        } catch (Exception e) {
558          if (LOG.isDebugEnabled()) {
559            LOG.debug("type=" + type, e);
560          }
561          throw new JspException(e);
562        }
563      }
564    
565      public static UIGraphic getFirstGraphicChild(UIComponent component) {
566        UIGraphic graphic = null;
567        for (Object o : component.getChildren()) {
568          UIComponent uiComponent = (UIComponent) o;
569          if (uiComponent instanceof UIGraphic) {
570            graphic = (UIGraphic) uiComponent;
571            break;
572          }
573        }
574        return graphic;
575      }
576    
577      public static boolean isHoverEnabled(UIComponent component) {
578        return ComponentUtils.getBooleanAttribute(component, Attributes.HOVER);
579      }
580    
581      public static UIOutput getFirstNonGraphicChild(UIComponent component) {
582        for (UIComponent child : (List<UIComponent>) component.getChildren()) {
583          if (child instanceof UIOutput) {
584            return (UIOutput) child;
585          }
586        }
587        return null;
588      }
589    
590      /**
591       * @deprecated since 1.5.0
592       */
593      @Deprecated
594      public static void setIntegerSizeProperty(UIComponent component, String name, String value) {
595        if (value != null) {
596          if (UIComponentTag.isValueReference(value)) {
597            component.setValueBinding(name, createValueBinding(value));
598          } else {
599            value = removePx(value);
600            component.getAttributes().put(name, new Integer(value));
601          }
602        }
603      }
604    
605      public static String removePx(String value) {
606        if (value != null && value.endsWith("px")) {
607          value = value.substring(0, value.length() - 2);
608        }
609        return value;
610      }
611    
612      public static void setValueForValueBinding(String name, Object value) {
613        FacesContext context = FacesContext.getCurrentInstance();
614        ValueBinding valueBinding = context.getApplication().createValueBinding(name);
615        valueBinding.setValue(context, value);
616      }
617    
618    
619      public static boolean hasSelectedValue(List<SelectItem> items, Object value) {
620        for (SelectItem item : items) {
621          if (item.getValue().equals(value)) {
622            return true;
623          }
624        }
625        return false;
626      }
627    
628      public static int getIntValue(ValueBinding valueBinding) {
629        return getAsInt(valueBinding.getValue(FacesContext.getCurrentInstance()));
630      }
631    
632      private static int getAsInt(Object value) {
633        int result;
634        if (value instanceof Number) {
635          result = ((Number) value).intValue();
636        } else if (value instanceof String) {
637          result = Integer.parseInt((String) value);
638        } else {
639          throw new IllegalArgumentException("Can't convert " + value + " to int!");
640        }
641        return result;
642      }
643    
644    
645      public static String createPickerId(FacesContext facesContext, UIComponent component, String postfix) {
646        //String id = component.getId();
647        String id = getComponentId(facesContext, component);
648        return id + "_picker" + postfix;
649      }
650    
651      public static String getComponentId(FacesContext facesContext, UIComponent component) {
652        String id = component.getId();
653        //if (id == null) {
654        // XXX What is this?
655        //  id = component.getClientId(facesContext).substring(id.lastIndexOf('_'));
656        //}
657        return id;
658      }
659    
660      /**
661       * Checks if the Component has a label facet and if not creates one with the label attribute.
662       *
663       * Todo: check if this method should be set to deprecated. 
664       */
665      public static UIComponent provideLabel(FacesContext facesContext, UIComponent component) {
666        UIComponent label = component.getFacet(Facets.LABEL);
667    
668    
669        if (label == null) {
670          final Map attributes = component.getAttributes();
671          Object labelText = component.getValueBinding(Attributes.LABEL);
672          if (labelText == null) {
673            labelText = attributes.get(Attributes.LABEL);
674          }
675    
676          if (labelText != null) {
677            Application application = FacesContext.getCurrentInstance().getApplication();
678            label = application.createComponent(UIOutput.COMPONENT_TYPE);
679            label.setRendererType(RendererTypes.LABEL);
680            String idprefix = ComponentUtils.getComponentId(facesContext, component);
681            label.setId(idprefix + "_" + Facets.LABEL);
682            label.setRendered(true);
683    
684            if (labelText instanceof ValueBinding) {
685              label.setValueBinding(Attributes.VALUE, (ValueBinding) labelText);
686            } else {
687              label.getAttributes().put(Attributes.VALUE, labelText);
688            }
689    
690            component.getFacets().put(Facets.LABEL, label);
691          }
692        }
693        return label;
694      }
695    
696      /**
697       * @deprecated since 1.5.0
698       */
699      @Deprecated
700      public static void setValidator(EditableValueHolder editableValueHolder, String validator) {
701        if (validator != null && editableValueHolder.getValidator() == null) {
702          if (UIComponentTag.isValueReference(validator)) {
703            MethodBinding methodBinding =
704                FacesContext.getCurrentInstance().getApplication().createMethodBinding(validator, VALIDATOR_ARGS);
705            editableValueHolder.setValidator(methodBinding);
706          }
707        }
708      }
709    
710      /**
711       * @deprecated since 1.5.0
712       */
713      @Deprecated
714      public static void setConverter(ValueHolder valueHolder, String converterId) {
715        if (converterId != null && valueHolder.getConverter() == null) {
716          final FacesContext facesContext = FacesContext.getCurrentInstance();
717          final Application application = facesContext.getApplication();
718          if (UIComponentTag.isValueReference(converterId)) {
719            ValueBinding valueBinding = application.createValueBinding(converterId);
720            if (valueHolder instanceof UIComponent) {
721              ((UIComponent) valueHolder).setValueBinding(Attributes.CONVERTER, valueBinding);
722            }
723          } else {
724            Converter converter = application.createConverter(converterId);
725            valueHolder.setConverter(converter);
726          }
727        }
728      }
729    
730      /**
731       * @deprecated since 1.5.0
732       */
733      @Deprecated
734      public static void setAction(ActionSource component, String action) {
735        if (action != null) {
736          if (UIComponentTag.isValueReference(action)) {
737            final FacesContext facesContext = FacesContext.getCurrentInstance();
738            final Application application = facesContext.getApplication();
739            MethodBinding binding = application.createMethodBinding(action, null);
740            component.setAction(binding);
741          } else {
742            component.setAction(new ConstantMethodBinding(action));
743          }
744        }
745      }
746    
747      /**
748       * @deprecated since 1.5.0
749       */
750      @Deprecated
751      public static void setActionListener(ActionSource command, String actionListener) {
752        final FacesContext facesContext = FacesContext.getCurrentInstance();
753        final Application application = facesContext.getApplication();
754        if (actionListener != null) {
755          if (UIComponentTag.isValueReference(actionListener)) {
756            MethodBinding binding
757                = application.createMethodBinding(actionListener, ACTION_LISTENER_ARGS);
758            command.setActionListener(binding);
759          } else {
760            throw new IllegalArgumentException(
761                "Must be a valueReference (actionListener): " + actionListener);
762          }
763        }
764      }
765    
766      /**
767       * @deprecated since 1.5.0
768       */
769      @Deprecated
770      public static void setValueChangeListener(EditableValueHolder valueHolder, String valueChangeListener) {
771        final FacesContext facesContext = FacesContext.getCurrentInstance();
772        final Application application = facesContext.getApplication();
773        if (valueChangeListener != null) {
774          if (UIComponentTag.isValueReference(valueChangeListener)) {
775            MethodBinding binding
776                = application.createMethodBinding(valueChangeListener, VALUE_CHANGE_LISTENER_ARGS);
777            valueHolder.setValueChangeListener(binding);
778          } else {
779            throw new IllegalArgumentException(
780                "Must be a valueReference (valueChangeListener): " + valueChangeListener);
781          }
782        }
783      }
784    
785      /**
786       * @deprecated since 1.5.0
787       */
788      @Deprecated
789      public static void setValueBinding(UIComponent component, String name, String state) {
790        // TODO: check, if it is an writeable object
791        if (state != null && UIComponentTag.isValueReference(state)) {
792          ValueBinding valueBinding = createValueBinding(state);
793          component.setValueBinding(name, valueBinding);
794        }
795      }
796    
797      /**
798       * @deprecated since 1.5
799       */
800      @Deprecated
801      public static String[] getMarkupBinding(FacesContext facesContext, SupportsMarkup component) {
802        ValueBinding vb = ((UIComponent) component).getValueBinding(Attributes.MARKUP);
803        if (vb != null) {
804          Object markups = vb.getValue(facesContext);
805          if (markups instanceof String[]) {
806            return (String[]) markups;
807          } else if (markups instanceof String) {
808            String[] strings = StringUtils.split((String) markups, ", ");
809            List<String> result = new ArrayList<String>(strings.length);
810            for (String string : strings) {
811              if (string.trim().length() != 0) {
812                result.add(string.trim());
813              }
814            }
815            return result.toArray(new String[result.size()]);
816          } else if (markups == null) {
817            return ArrayUtils.EMPTY_STRING_ARRAY;
818          } else {
819            return new String[]{markups.toString()};
820          }
821        }
822    
823        return ArrayUtils.EMPTY_STRING_ARRAY;
824      }
825    
826      /**
827       * The search depends on the number of colons in the relativeId:
828       * <dl>
829       *   <dd>colonCount == 0</dd>
830       *   <dt>fully relative</dt>
831       *   <dd>colonCount == 1</dd>
832       *   <dt>absolute (still normal findComponent syntax)</dt>
833       *   <dd>colonCount > 1</dd>
834       *   <dt>for each extra colon after 1, go up a naming container</dt>
835       * </dl>
836       * (to the view root, if naming containers run out)
837       */
838      public static UIComponent findComponent(UIComponent from, String relativeId) {
839        return FindComponentUtils.findComponent(from, relativeId);
840      }
841    
842      public static String[] splitList(String renderers) {
843        return StringUtils.split(renderers, LIST_SEPARATOR_CHARS);
844      }
845    
846      public static Object getConvertedValue(
847          FacesContext facesContext, UIComponent component, String stringValue) {
848        try {
849          Renderer renderer = getRenderer(facesContext, component);
850          if (renderer != null) {
851            if (component instanceof UISelectMany) {
852              final Object converted = renderer.getConvertedValue(facesContext, component, new String[]{stringValue});
853              return ((Object[]) converted)[0];
854            } else {
855              return renderer.getConvertedValue(facesContext, component, stringValue);
856            }
857          } else if (component instanceof ValueHolder) {
858            Converter converter = ((ValueHolder) component).getConverter();
859            if (converter == null) {
860              //Try to find out by value binding
861              ValueBinding vb = component.getValueBinding("value");
862              if (vb != null) {
863                Class valueType = vb.getType(facesContext);
864                if (valueType != null) {
865                  converter = facesContext.getApplication().createConverter(valueType);
866                }
867              }
868            }
869            if (converter != null) {
870              converter.getAsObject(facesContext, component, stringValue);
871            }
872          }
873        } catch (Exception e) {
874          LOG.warn("Can't convert string value '" + stringValue + "'", e);
875        }
876        return stringValue;
877      }
878    
879      public static Markup updateMarkup(UIComponent component, Markup markup) {
880        if (markup == null) {
881          markup = Markup.NULL;
882        }
883        if (ComponentUtils.getBooleanAttribute(component, Attributes.DISABLED)) {
884          markup = markup.add(Markup.DISABLED);
885        }
886        if (ComponentUtils.getBooleanAttribute(component, Attributes.READONLY)) {
887          markup = markup.add(Markup.READONLY);
888        }
889        if (component instanceof UIInput) {
890          UIInput input = (UIInput) component;
891    
892          final FacesMessage.Severity maximumSeverity = ComponentUtils.getMaximumSeverity(input);
893          markup = markup.add(markupOfSeverity(maximumSeverity));
894    
895          if (input.isRequired()) {
896            markup = markup.add(Markup.REQUIRED);
897          }
898        }
899        return markup;
900      }
901    
902      public static Markup markupOfSeverity(FacesMessage.Severity maximumSeverity) {
903        if (FacesMessage.SEVERITY_FATAL.equals(maximumSeverity)) {
904          return Markup.FATAL;
905        } else if (FacesMessage.SEVERITY_ERROR.equals(maximumSeverity)) {
906          return Markup.ERROR;
907        } else if (FacesMessage.SEVERITY_WARN.equals(maximumSeverity)) {
908          return Markup.WARN;
909        } else if (FacesMessage.SEVERITY_INFO.equals(maximumSeverity)) {
910          return Markup.INFO;
911        }
912        return null;
913      }
914    
915      public static boolean hasChildrenWithMessages(FacesContext facesContext, NamingContainer  container) {
916        if (container instanceof UIComponent) {
917          String clientId = ((UIComponent) container).getClientId(facesContext);
918          for (Iterator ids = facesContext.getClientIdsWithMessages(); ids.hasNext();) {
919            String id = (String) ids.next();
920            if (id.startsWith(clientId)) {
921              return true;
922            }
923          }
924        }
925        return false;
926      }
927    
928      public static FacesMessage.Severity getMaximumSeverityOfChildrenMessages(
929          FacesContext facesContext, NamingContainer container) {
930        if (container instanceof UIComponent) {
931          String clientId = ((UIComponent) container).getClientId(facesContext);
932          FacesMessage.Severity max = null;
933          for (Iterator ids = facesContext.getClientIdsWithMessages(); ids.hasNext();) {
934            String id = (String) ids.next();
935            if (id != null && id.startsWith(clientId)) {
936              final Iterator messages = facesContext.getMessages(id);
937              while (messages.hasNext()) {
938                FacesMessage message = (FacesMessage) messages.next();
939                if (max == null || message.getSeverity().getOrdinal() > max.getOrdinal()) {
940                  max = message.getSeverity();
941                }
942              }
943            }
944          }
945          return max;
946        }
947        return null;
948      }
949    
950      public static String[] getChildrenWithMessages(FacesContext facesContext, NamingContainer container) {
951        if (container instanceof UIComponent) {
952          List<String> clientIds = new ArrayList<String>();
953          String clientId = ((UIComponent) container).getClientId(facesContext);
954          for (Iterator ids = facesContext.getClientIdsWithMessages(); ids.hasNext();) {
955            String id = (String) ids.next();
956            if (id.startsWith(clientId)) {
957              clientIds.add(id);
958            }
959          }
960          return clientIds.toArray(new String[clientIds.size()]);
961        }
962        return ArrayUtils.EMPTY_STRING_ARRAY;
963      }
964    }