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