001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.myfaces.tobago.component;
021    
022    import org.apache.commons.lang.StringUtils;
023    import org.apache.commons.lang.math.NumberUtils;
024    import org.apache.commons.logging.Log;
025    import org.apache.commons.logging.LogFactory;
026    import org.apache.myfaces.tobago.TobagoConstants;
027    import org.apache.myfaces.tobago.context.TransientStateHolder;
028    import org.apache.myfaces.tobago.el.ConstantMethodBinding;
029    import org.apache.myfaces.tobago.event.PopupActionListener;
030    import org.apache.myfaces.tobago.event.SheetStateChangeEvent;
031    import org.apache.myfaces.tobago.renderkit.LayoutableRendererBase;
032    import org.apache.myfaces.tobago.renderkit.html.StyleClasses;
033    import org.apache.myfaces.tobago.util.Callback;
034    import org.apache.myfaces.tobago.util.RangeParser;
035    import org.apache.myfaces.tobago.util.TobagoCallback;
036    
037    import javax.faces.FactoryFinder;
038    import javax.faces.application.Application;
039    import javax.faces.application.FacesMessage;
040    import javax.faces.component.ActionSource;
041    import javax.faces.component.EditableValueHolder;
042    import javax.faces.component.NamingContainer;
043    import javax.faces.component.UICommand;
044    import javax.faces.component.UIComponent;
045    import javax.faces.component.UIGraphic;
046    import javax.faces.component.UIOutput;
047    import javax.faces.component.UIParameter;
048    import javax.faces.component.UISelectItem;
049    import javax.faces.component.UISelectItems;
050    import javax.faces.component.ValueHolder;
051    import javax.faces.context.FacesContext;
052    import javax.faces.convert.Converter;
053    import javax.faces.el.MethodBinding;
054    import javax.faces.el.ValueBinding;
055    import javax.faces.event.ActionEvent;
056    import javax.faces.event.ActionListener;
057    import javax.faces.event.PhaseId;
058    import javax.faces.event.ValueChangeEvent;
059    import javax.faces.model.SelectItem;
060    import javax.faces.render.RenderKit;
061    import javax.faces.render.RenderKitFactory;
062    import javax.faces.render.Renderer;
063    import javax.faces.webapp.UIComponentTag;
064    import javax.servlet.jsp.JspException;
065    import java.util.ArrayList;
066    import java.util.Arrays;
067    import java.util.Collection;
068    import java.util.Collections;
069    import java.util.Iterator;
070    import java.util.List;
071    import java.util.Map;
072    import java.util.Set;
073    
074    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ACTION_LINK;
075    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ACTION_ONCLICK;
076    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ALIGN;
077    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_CONVERTER;
078    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_CREATE_SPAN;
079    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_DISABLED;
080    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ESCAPE;
081    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_FOR;
082    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_HOVER;
083    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LABEL;
084    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_MARKUP;
085    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_READONLY;
086    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_RENDERED_PARTIALLY;
087    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_RENDER_RANGE;
088    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_RENDER_RANGE_EXTERN;
089    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SORTABLE;
090    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE_CLASS;
091    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_VALUE;
092    import static org.apache.myfaces.tobago.TobagoConstants.COMMAND_TYPE_NAVIGATE;
093    import static org.apache.myfaces.tobago.TobagoConstants.COMMAND_TYPE_RESET;
094    import static org.apache.myfaces.tobago.TobagoConstants.COMMAND_TYPE_SCRIPT;
095    import static org.apache.myfaces.tobago.TobagoConstants.FACET_ITEMS;
096    import static org.apache.myfaces.tobago.TobagoConstants.FACET_LABEL;
097    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_LABEL;
098    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_OUT;
099    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_SELECT_BOOLEAN_CHECKBOX;
100    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_SELECT_ONE_RADIO;
101    
102    public class ComponentUtil {
103    
104      private static final Log LOG = LogFactory.getLog(ComponentUtil.class);
105    
106      private static final String RENDER_KEY_PREFIX
107          = "org.apache.myfaces.tobago.component.ComponentUtil.RendererKeyPrefix_";
108    
109      public static final Class[] ACTION_ARGS = {};
110      public static final Class[] ACTION_LISTENER_ARGS = {ActionEvent.class};
111      public static final Class[] VALUE_CHANGE_LISTENER_ARGS = {ValueChangeEvent.class};
112      public static final Class[] VALIDATOR_ARGS = {FacesContext.class, UIComponent.class, Object.class};
113    
114      private ComponentUtil() {
115      }
116    
117    
118      public static boolean hasErrorMessages(FacesContext context) {
119        for (Iterator iter = context.getMessages(); iter.hasNext();) {
120          FacesMessage message = (FacesMessage) iter.next();
121          if (FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0) {
122            return true;
123          }
124        }
125        return false;
126      }
127    
128      public static boolean containsPopupActionListener(UICommand command) {
129        ActionListener[] actionListeners = command.getActionListeners();
130        for (ActionListener actionListener : actionListeners) {
131          if (actionListener instanceof PopupActionListener) {
132            return true;
133          }
134        }
135        return false;
136      }
137    
138      public static String getFacesMessageAsString(FacesContext facesContext, UIComponent component) {
139        Iterator messages = facesContext.getMessages(
140            component.getClientId(facesContext));
141        StringBuilder stringBuffer = new StringBuilder();
142        while (messages.hasNext()) {
143          FacesMessage message = (FacesMessage) messages.next();
144          stringBuffer.append(message.getDetail());
145        }
146        if (stringBuffer.length() > 0) {
147          return stringBuffer.toString();
148        } else {
149          return null;
150        }
151      }
152    
153      public static boolean isInPopup(UIComponent component) {
154        while (component != null) {
155          if (component instanceof UIPopup) {
156            return true;
157          }
158          component = component.getParent();
159        }
160        return false;
161      }
162    
163      public static void resetPage(FacesContext context) {
164        javax.faces.component.UIViewRoot view = context.getViewRoot();
165        if (view != null) {
166          view.getAttributes().remove(UIPage.COMPONENT_TYPE);
167        }
168      }
169    
170      @SuppressWarnings(value = "unchecked")
171      public static UIPage findPage(FacesContext context, UIComponent component) {
172        javax.faces.component.UIViewRoot view = context.getViewRoot();
173        if (view != null) {
174          TransientStateHolder stateHolder = (TransientStateHolder) view.getAttributes().get(UIPage.COMPONENT_TYPE);
175          if (stateHolder == null || stateHolder.isEmpty()) {
176            UIPage page = findPage(component);
177            stateHolder = new TransientStateHolder(page);
178            context.getViewRoot().getAttributes().put(UIPage.COMPONENT_TYPE, stateHolder);
179          }
180          return (UIPage) stateHolder.get();
181        } else {
182          return findPage(component);
183        }
184      }
185    
186      public static UIPage findPage(UIComponent component) {
187        while (component != null) {
188          if (component instanceof UIPage) {
189            return (UIPage) component;
190          }
191          component = component.getParent();
192        }
193        return null;
194      }
195    
196      public static void addStyles(UIComponent component, String[] styles) {
197        UIPage uiPage = ComponentUtil.findPage(component);
198        uiPage.getStyleFiles().addAll(Arrays.asList(styles));
199      }
200    
201      public static void addScripts(UIComponent component, String[] scripts) {
202        UIPage uiPage = ComponentUtil.findPage(component);
203        uiPage.getScriptFiles().addAll(Arrays.asList(scripts));
204      }
205    
206      public static void addOnloadCommands(UIComponent component, String[] cmds) {
207        UIPage uiPage = ComponentUtil.findPage(component);
208        uiPage.getOnloadScripts().addAll(Arrays.asList(cmds));
209      }
210    
211      public static UIPage findPage(FacesContext facesContext) {
212        return findPageBreadthFirst(facesContext.getViewRoot());
213      }
214    
215      private static UIPage findPageBreadthFirst(UIComponent component) {
216        for (Object o : component.getChildren()) {
217          UIComponent child = (UIComponent) o;
218          if (child instanceof UIPage) {
219            return (UIPage) child;
220          }
221        }
222        for (Object o : component.getChildren()) {
223          UIComponent child = (UIComponent) o;
224          UIPage result = findPageBreadthFirst(child);
225          if (result != null) {
226            return result;
227          }
228        }
229        return null;
230      }
231    
232    
233      public static UIForm findForm(UIComponent component) {
234        while (component != null) {
235          if (component instanceof UIForm) {
236            return (UIForm) component;
237          }
238          component = component.getParent();
239        }
240        return null;
241      }
242    
243      /**
244       * Find all subforms of a component, and collects it.
245       * It does not find subforms of subforms.
246       */
247      public static List<UIForm> findSubForms(UIComponent component) {
248        List<UIForm> collect = new ArrayList<UIForm>();
249        findSubForms(collect, component);
250        return collect;
251      }
252    
253      @SuppressWarnings(value = "unchecked")
254      private static void findSubForms(List<UIForm> collect, UIComponent component) {
255        Iterator<UIComponent> kids = component.getFacetsAndChildren();
256        while (kids.hasNext()) {
257          UIComponent child = kids.next();
258          if (child instanceof UIForm) {
259            collect.add((UIForm) child);
260          } else {
261            findSubForms(collect, child);
262          }
263        }
264      }
265    
266      /**
267       * Searches the component tree beneath the component and return the first component matching the type.
268       */
269      public static <T extends UIComponent> T findDescendant(UIComponent component, Class<T> type) {
270    
271        for (UIComponent child : (List<UIComponent>) component.getChildren()) {
272          if (child.getClass().equals(type)) {
273            return (T) child;
274          }
275          final T descendant = findDescendant(child, type);
276          if (descendant != null) {
277            return descendant;
278          }
279        }
280        return null;
281      }
282    
283      /**
284       * Looks for the attribute "for" in the component. If there is any
285       * search for the component which is referenced by the "for" attribute,
286       * and return their clientId.
287       * If there is no "for" attribute, return the "clientId" of the parent
288       * (if it has a parent). This is useful for labels.
289       */
290      public static String findClientIdFor(UIComponent component,
291          FacesContext facesContext) {
292        UIComponent forComponent = findFor(component);
293        if (forComponent != null) {
294          String clientId = forComponent.getClientId(facesContext);
295          if (LOG.isDebugEnabled()) {
296            LOG.debug("found clientId: '" + clientId + "'");
297          }
298          return clientId;
299        }
300        if (LOG.isDebugEnabled()) {
301          LOG.debug("found no clientId");
302        }
303        return null;
304      }
305    
306      public static UIComponent findFor(UIComponent component) {
307        String forValue = (String) component.getAttributes().get(ATTR_FOR);
308        if (forValue == null) {
309          return component.getParent();
310        }
311        return component.findComponent(forValue);
312      }
313    
314      public static boolean isInActiveForm(UIComponent component) {
315        while (component != null) {
316          //log.debug("compoent= " + component.getClientId(FacesContext.getCurrentInstance())
317          // + " " + component.getRendererType());
318          if (component instanceof UIForm) {
319            UIForm form = (UIForm) component;
320            if (form.isSubmitted()) {
321              //log.debug("in active form = " + form.getClientId(FacesContext.getCurrentInstance()));
322              return true;
323            } /*else {
324              log.debug("form found but not active = " + form.getClientId(FacesContext.getCurrentInstance()));
325            } */
326          }
327          component = component.getParent();
328        }
329        //log.debug("not in an active form");
330        return false;
331      }
332    
333      public static FacesMessage.Severity getMaximumSeverity(UIComponent component) {
334        final boolean invalid = component instanceof javax.faces.component.UIInput
335            && !((javax.faces.component.UIInput) component).isValid();
336        FacesMessage.Severity max = invalid ? FacesMessage.SEVERITY_ERROR : null;
337        FacesContext facesContext = FacesContext.getCurrentInstance();
338        final Iterator messages = facesContext.getMessages(component.getClientId(facesContext));
339        while (messages.hasNext()) {
340          FacesMessage message = (FacesMessage) messages.next();
341          if (max == null || message.getSeverity().getOrdinal() > max.getOrdinal()) {
342            max = message.getSeverity();
343          }
344        }
345        return max;
346      }
347    
348      public static boolean isError(javax.faces.component.UIInput uiInput) {
349        FacesContext facesContext = FacesContext.getCurrentInstance();
350        return !uiInput.isValid()
351            || facesContext.getMessages(uiInput.getClientId(facesContext)).hasNext();
352      }
353    
354      public static boolean isError(UIComponent component) {
355        if (component instanceof UIInput) {
356          return isError((UIInput) component);
357        }
358        return false;
359      }
360    
361      public static boolean isOutputOnly(UIComponent component) {
362        return getBooleanAttribute(component, ATTR_DISABLED)
363            || getBooleanAttribute(component, ATTR_READONLY);
364      }
365    
366      public static boolean mayValidate(UIComponent component) {
367        return !isOutputOnly(component)
368            && ComponentUtil.isInActiveForm(component);
369      }
370    
371      public static boolean mayUpdateModel(UIComponent component) {
372        return mayValidate(component);
373      }
374    
375      public static boolean getBooleanAttribute(UIComponent component, String name) {
376    
377        Object bool = component.getAttributes().get(name);
378        if (bool == null) {
379          return false;
380        }
381        if (bool instanceof ValueBinding) {
382          bool = ((ValueBinding) bool).getValue(FacesContext.getCurrentInstance());
383        }
384        if (bool instanceof Boolean) {
385          return (Boolean) bool;
386        } else if (bool instanceof String) {
387          if (LOG.isInfoEnabled()) {
388            LOG.info("Searching for a boolean, but find a String. Should not happen. "
389                + "attribute: '" + name + "' id: '" + component.getClientId(FacesContext.getCurrentInstance())
390                + "' comp: '" + component + "'");
391          }
392          return Boolean.valueOf((String) bool);
393        } else {
394          LOG.warn("Unknown type '" + bool.getClass().getName()
395              + "' for boolean attribute: " + name + " id: " + component.getClientId(FacesContext.getCurrentInstance())
396              + " comp: " + component);
397          return false;
398        }
399      }
400    
401      public static void setRenderedPartially(org.apache.myfaces.tobago.component.UICommand command,
402          String renderers) {
403        if (renderers != null) {
404          if (UIComponentTag.isValueReference(renderers)) {
405            command.setValueBinding(ATTR_RENDERED_PARTIALLY, createValueBinding(renderers));
406          } else {
407            String[] components = StringUtils.split(renderers, ",");
408            command.setRenderedPartially(components);
409          }
410        }
411      }
412    
413      public static void setStyleClasses(UIComponent component, String styleClasses) {
414        if (styleClasses != null) {
415          if (UIComponentTag.isValueReference(styleClasses)) {
416            component.setValueBinding(ATTR_STYLE_CLASS, createValueBinding(styleClasses));
417          } else {
418            String[] classes = StringUtils.split(styleClasses, ", ");
419            if (classes.length > 0) {
420              StyleClasses styles = StyleClasses.ensureStyleClasses(component);
421              for (String clazz : classes) {
422                styles.addFullQualifiedClass(clazz);
423              }
424            }
425          }
426        }
427      }
428    
429      public static void setMarkup(UIComponent markupComponent, String markup) {
430        if (markup != null) {
431          if (markupComponent instanceof SupportsMarkup) {
432            if (UIComponentTag.isValueReference(markup)) {
433              markupComponent.setValueBinding(ATTR_MARKUP, createValueBinding(markup));
434            } else {
435              String[] markups = StringUtils.split(markup, ",");
436              ((SupportsMarkup) markupComponent).setMarkup(markups);
437            }
438          } else {
439            LOG.error("Component did not support Markup " + markupComponent.getClass().getName());
440          }
441        }
442      }
443    
444      public static Object getAttribute(UIComponent component, String name) {
445        Object value = component.getAttributes().get(name);
446        if (value instanceof ValueBinding) {
447          value = ((ValueBinding) value).getValue(FacesContext.getCurrentInstance());
448        }
449        return value;
450      }
451    
452      public static String getStringAttribute(UIComponent component, String name) {
453        return (String) getAttribute(component, name);
454      }
455    
456      public static int getIntAttribute(UIComponent component, String name) {
457        return getIntAttribute(component, name, 0);
458      }
459    
460      public static int getIntAttribute(UIComponent component, String name,
461          int defaultValue) {
462        Object integer = component.getAttributes().get(name);
463        if (integer instanceof Number) {
464          return ((Number) integer).intValue();
465        } else if (integer instanceof String) {
466          try {
467            return Integer.parseInt((String) integer);
468          } catch (NumberFormatException e) {
469            LOG.warn("Can't parse number from string : \"" + integer + "\"!");
470            return defaultValue;
471          }
472        } else if (integer == null) {
473          return defaultValue;
474        } else {
475          LOG.warn("Unknown type '" + integer.getClass().getName()
476              + "' for integer attribute: " + name + " comp: " + component);
477          return defaultValue;
478        }
479      }
480    
481      /**
482       * @param component
483       * @param name
484       * @deprecated please use the  method {@link #getCharacterAttribute(javax.faces.component.UIComponent, String)}
485       */
486      @Deprecated
487      public static Character getCharakterAttribute(UIComponent component, String name) {
488        return getCharacterAttribute(component, name);
489      }
490    
491      public static Character getCharacterAttribute(UIComponent component, String name) {
492        Object character = component.getAttributes().get(name);
493        if (character == null) {
494          return null;
495        } else if (character instanceof Character) {
496          return ((Character) character);
497        } else if (character instanceof String) {
498          String asString = ((String) character);
499          return asString.length() > 0 ? asString.charAt(0) : null;
500        } else {
501          LOG.warn("Unknown type '" + character.getClass().getName()
502              + "' for integer attribute: " + name + " comp: " + component);
503          return null;
504        }
505      }
506    
507      public static boolean isFacetOf(UIComponent component, UIComponent parent) {
508        for (Object o : parent.getFacets().keySet()) {
509          UIComponent facet = parent.getFacet((String) o);
510          if (component.equals(facet)) {
511            return true;
512          }
513        }
514        return false;
515      }
516    
517      // TODO This should not be neseccary, but UIComponentBase.getRenderer() is protected
518      public static LayoutableRendererBase getRenderer(FacesContext facesContext, UIComponent component) {
519        return getRenderer(facesContext, component.getFamily(), component.getRendererType());
520    
521      }
522    
523      public static LayoutableRendererBase getRenderer(FacesContext facesContext, String family, String rendererType) {
524        if (rendererType == null) {
525          return null;
526        }
527    
528        LayoutableRendererBase renderer;
529    
530        Map requestMap = facesContext.getExternalContext().getRequestMap();
531        renderer = (LayoutableRendererBase) requestMap.get(RENDER_KEY_PREFIX + rendererType);
532    
533        if (renderer == null) {
534          RenderKitFactory rkFactory = (RenderKitFactory)
535              FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
536          RenderKit renderKit = rkFactory.getRenderKit(facesContext, facesContext.getViewRoot().getRenderKitId());
537          Renderer myRenderer = renderKit.getRenderer(family, rendererType);
538          if (myRenderer instanceof LayoutableRendererBase) {
539            requestMap.put(RENDER_KEY_PREFIX + rendererType, myRenderer);
540            renderer = (LayoutableRendererBase) myRenderer;
541          } else {
542            return null;
543          }
544        }
545        return renderer;
546      }
547    
548      public static String currentValue(UIComponent component) {
549        String currentValue = null;
550        if (component instanceof ValueHolder) {
551          Object value;
552          if (component instanceof EditableValueHolder) {
553            value = ((EditableValueHolder) component).getSubmittedValue();
554            if (value != null) {
555              return (String) value;
556            }
557          }
558    
559          value = ((ValueHolder) component).getValue();
560          if (value != null) {
561            Converter converter = ((ValueHolder) component).getConverter();
562            if (converter == null) {
563              FacesContext context = FacesContext.getCurrentInstance();
564              converter = context.getApplication().createConverter(value.getClass());
565            }
566            if (converter != null) {
567              currentValue =
568                  converter.getAsString(FacesContext.getCurrentInstance(),
569                      component, value);
570            } else {
571              currentValue = value.toString();
572            }
573          }
574        }
575        return currentValue;
576      }
577    
578      public static List<SelectItem> getSelectItems(UIComponent component) {
579    
580        ArrayList<SelectItem> list = new ArrayList<SelectItem>();
581    
582        for (Object o1 : component.getChildren()) {
583          UIComponent kid = (UIComponent) o1;
584          if (LOG.isDebugEnabled()) {
585            LOG.debug("kid " + kid);
586            LOG.debug("kid " + kid.getClass().getName());
587          }
588          if (kid instanceof UISelectItem) {
589            Object value = ((UISelectItem) kid).getValue();
590            if (value == null) {
591              UISelectItem item = (UISelectItem) kid;
592              if (kid instanceof org.apache.myfaces.tobago.component.UISelectItem) {
593                list.add(new org.apache.myfaces.tobago.model.SelectItem(
594                    (org.apache.myfaces.tobago.component.UISelectItem) kid));
595              } else {
596                list.add(new SelectItem(item.getItemValue() == null ? "" : item.getItemValue(),
597                    item.getItemLabel() != null ? item.getItemLabel() : item.getItemValue().toString(),
598                    item.getItemDescription()));
599              }
600            } else if (value instanceof SelectItem) {
601              list.add((SelectItem) value);
602            } else {
603              throw new IllegalArgumentException("TYPE ERROR: value NOT instanceof SelectItem. type="
604                  + value.getClass().getName());
605            }
606          } else if (kid instanceof UISelectItems) {
607            Object value = ((UISelectItems) kid).getValue();
608            if (LOG.isDebugEnabled()) {
609              LOG.debug("value " + value);
610              if (value != null) {
611                LOG.debug("value " + value.getClass().getName());
612              }
613            }
614            if (value == null) {
615              if (LOG.isDebugEnabled()) {
616                LOG.debug("value is null");
617              }
618            } else if (value instanceof SelectItem) {
619              list.add((SelectItem) value);
620            } else if (value instanceof SelectItem[]) {
621              SelectItem[] items = (SelectItem[]) value;
622              list.addAll(Arrays.asList(items));
623            } else if (value instanceof Collection) {
624              for (Object o : ((Collection) value)) {
625                list.add((SelectItem) o);
626              }
627            } else if (value instanceof Map) {
628              for (Object key : ((Map) value).keySet()) {
629                if (key != null) {
630                  Object val = ((Map) value).get(key);
631                  if (val != null) {
632                    list.add(new SelectItem(val.toString(), key.toString(), null));
633                  }
634                }
635              }
636            } else {
637              throw new IllegalArgumentException("TYPE ERROR: value NOT instanceof "
638                  + "SelectItem, SelectItem[], Collection, Map. type="
639                  + value.getClass().getName());
640            }
641          }
642        }
643    
644        return list;
645      }
646    
647      public static Object findParameter(UIComponent component, String name) {
648        for (Object o : component.getChildren()) {
649          UIComponent child = (UIComponent) o;
650          if (child instanceof UIParameter) {
651            UIParameter parameter = (UIParameter) child;
652            if (LOG.isDebugEnabled()) {
653              LOG.debug("Select name='" + parameter.getName() + "'");
654              LOG.debug("Select value='" + parameter.getValue() + "'");
655            }
656            if (name.equals(parameter.getName())) {
657              return parameter.getValue();
658            }
659          }
660        }
661        return null;
662      }
663    
664      public static String toString(UIComponent component, int offset) {
665        return toString(component, offset, false);
666      }
667    
668      private static String toString(UIComponent component, int offset, boolean asFacet) {
669        StringBuilder result = new StringBuilder();
670        if (component == null) {
671          result.append("null");
672        } else {
673          result.append('\n');
674          if (!asFacet) {
675            result.append(spaces(offset));
676            result.append(toString(component));
677          }
678          Map facets = component.getFacets();
679          if (facets.size() > 0) {
680            for (Map.Entry<String, UIComponent> entry : (Set<Map.Entry<String, UIComponent>>) facets.entrySet()) {
681              UIComponent facet = entry.getValue();
682              result.append('\n');
683              result.append(spaces(offset + 1));
684              result.append('\"');
685              result.append(entry.getKey());
686              result.append("\" = ");
687              result.append(toString(facet));
688              result.append(toString(facet, offset + 1, true));
689            }
690          }
691          for (Object o : component.getChildren()) {
692            result.append(toString((UIComponent) o, offset + 1, false));
693          }
694        }
695        return result.toString();
696      }
697    
698      private static String toString(UIComponent component) {
699        StringBuilder buf = new StringBuilder(component.getClass().getName());
700        buf.append('@');
701        buf.append(Integer.toHexString(component.hashCode()));
702        buf.append(" ");
703        buf.append(component.getRendererType());
704        buf.append(" ");
705        if (component instanceof javax.faces.component.UIViewRoot) {
706          buf.append(((javax.faces.component.UIViewRoot) component).getViewId());
707        } else {
708          buf.append(component.getId());
709          buf.append(" ");
710          buf.append(component.getClientId(FacesContext.getCurrentInstance()));
711        }
712        return buf.toString();
713      }
714    
715      private static String spaces(int n) {
716        StringBuilder buffer = new StringBuilder(n * 2);
717        for (int i = 0; i < n; i++) {
718          buffer.append("  ");
719        }
720        return buffer.toString();
721      }
722    
723      public static ActionListener createActionListener(String type)
724          throws JspException {
725        try {
726          ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
727          if (classLoader == null) {
728            classLoader = type.getClass().getClassLoader();
729          }
730          Class clazz = classLoader.loadClass(type);
731          return (ActionListener) clazz.newInstance();
732        } catch (Exception e) {
733          if (LOG.isDebugEnabled()) {
734            LOG.debug("type=" + type, e);
735          }
736          throw new JspException(e);
737        }
738      }
739    
740      public static UIGraphic getFirstGraphicChild(UIComponent component) {
741        UIGraphic graphic = null;
742        for (Object o : component.getChildren()) {
743          UIComponent uiComponent = (UIComponent) o;
744          if (uiComponent instanceof UIGraphic) {
745            graphic = (UIGraphic) uiComponent;
746            break;
747          }
748        }
749        return graphic;
750      }
751    
752      public static boolean isHoverEnabled(UIComponent component) {
753        return ComponentUtil.getBooleanAttribute(component, ATTR_HOVER);
754      }
755    
756      public static UIOutput getFirstNonGraphicChild(UIComponent component) {
757        UIOutput output = null;
758        for (Object o : component.getChildren()) {
759          UIComponent uiComponent = (UIComponent) o;
760          if ((uiComponent instanceof UIOutput)
761              && !(uiComponent instanceof UIGraphic)) {
762            output = (UIOutput) uiComponent;
763            break;
764          }
765        }
766        return output;
767      }
768    
769      public static void setIntegerSizeProperty(UIComponent component,
770          String name, String value) {
771        if (value != null) {
772          if (UIComponentTag.isValueReference(value)) {
773            component.setValueBinding(name, createValueBinding(value));
774          } else {
775            value = removePx(value);
776            component.getAttributes().put(name, new Integer(value));
777          }
778        }
779      }
780    
781      public static String removePx(String value) {
782        if (value != null && value.endsWith("px")) {
783          value = value.substring(0, value.length() - 2);
784        }
785        return value;
786      }
787    
788      public static void setIntegerProperty(UIComponent component,
789          String name, String value) {
790        if (value != null) {
791          if (UIComponentTag.isValueReference(value)) {
792            component.setValueBinding(name, createValueBinding(value));
793          } else {
794            component.getAttributes().put(name, new Integer(value));
795          }
796        }
797      }
798    
799      public static void setBooleanProperty(UIComponent component,
800          String name, String value) {
801        if (value != null) {
802          if (UIComponentTag.isValueReference(value)) {
803            component.setValueBinding(name, createValueBinding(value));
804          } else {
805            component.getAttributes().put(name, Boolean.valueOf(value));
806          }
807        }
808      }
809    
810      public static void setStringProperty(UIComponent component, String name,
811          String value) {
812        if (value != null) {
813          if (UIComponentTag.isValueReference(value)) {
814            component.setValueBinding(name, createValueBinding(value));
815          } else {
816            component.getAttributes().put(name, value);
817          }
818        }
819      }
820    
821      public static void setValueForValueBinding(String name, Object value) {
822        FacesContext context = FacesContext.getCurrentInstance();
823        ValueBinding valueBinding = context.getApplication().createValueBinding(name);
824        valueBinding.setValue(context, value);
825      }
826    
827      public static ValueBinding createValueBinding(String value) {
828        return FacesContext.getCurrentInstance().getApplication()
829            .createValueBinding(value);
830      }
831    
832      public static String getValueFromEl(String script) {
833        if (UIComponentTag.isValueReference(script)) {
834          ValueBinding valueBinding = ComponentUtil.createValueBinding(script);
835          script = (String) valueBinding.getValue(FacesContext.getCurrentInstance());
836        }
837        return script;
838      }
839    
840      /**
841       * Please use createComponent(String componentType, String rendererType, String id)
842       *
843       * @deprecated
844       */
845      @Deprecated
846      public static UIComponent createComponent(String componentType, String rendererType) {
847        return createComponent(componentType, rendererType,  null);
848      }
849    
850      public static UIComponent createComponent(String componentType, String rendererType, String id) {
851        final FacesContext facesContext = FacesContext.getCurrentInstance();
852        return createComponent(facesContext, componentType, rendererType, id);
853      }
854    
855      /**
856       * Please use createComponent(FacesContext facesContext, String componentType, String rendererType, String id)
857       *
858       * @deprecated
859       */
860      @Deprecated
861      public static UIComponent createComponent(FacesContext facesContext, String componentType, String rendererType) {
862        return createComponent(facesContext, componentType, rendererType, null);
863      }
864    
865      public static UIComponent createComponent(
866          FacesContext facesContext, String componentType, String rendererType, String id) {
867        UIComponent component
868            = facesContext.getApplication().createComponent(componentType);
869        component.setRendererType(rendererType);
870        component.setId(id);
871        return component;
872      }
873    
874      /**
875      * Please use createTextColumn(String label, String sortable, String align, String value, String id)
876      *
877      * @deprecated
878      */
879     @Deprecated
880      public static UIColumn createTextColumn(String label, String sortable, String align, String value) {
881        return createTextColumn(label, sortable, align, value, null);
882      }
883    
884      public static UIColumn createTextColumn(String label, String sortable, String align, String value, String id) {
885        UIComponent text = createComponent(UIOutput.COMPONENT_TYPE, RENDERER_TYPE_OUT, id + "_t");
886        setStringProperty(text, ATTR_VALUE, value);
887        setBooleanProperty(text, ATTR_CREATE_SPAN, "false");
888        setBooleanProperty(text, ATTR_ESCAPE, "false");
889        return createColumn(label, sortable, align, text, id);
890      }
891    
892      /**
893      * Please use createColumn(String label, String sortable, String align, UIComponent child)
894      *
895      * @deprecated
896      */
897     @Deprecated
898      public static UIColumn createColumn(String label, String sortable, String align, UIComponent child) {
899        return createColumn(label, sortable, align, child, null);
900      }
901    
902      public static UIColumn createColumn(String label, String sortable, String align, UIComponent child, String id) {
903        UIColumn column = createColumn(label, sortable, align, id);
904        column.getChildren().add(child);
905        return column;
906      }
907    
908      private static UIColumn createColumn(String label, String sortable, String align, String id) {
909        UIColumn column = (UIColumn) createComponent(UIColumn.COMPONENT_TYPE, null, id);
910        setStringProperty(column, ATTR_LABEL, label);
911        setBooleanProperty(column, ATTR_SORTABLE, sortable);
912        setStringProperty(column, ATTR_ALIGN, align);
913        return column;
914      }
915    
916      /**
917      * Please use createUIMenuSelectOneFacet(FacesContext facesContext, UICommand command, String id)
918      *
919      * @deprecated
920      */
921     @Deprecated
922      public static UIMenuSelectOne createUIMenuSelectOneFacet(FacesContext facesContext, UICommand command) {
923        return createUIMenuSelectOneFacet(facesContext, command, null);
924      }
925    
926      public static UIMenuSelectOne createUIMenuSelectOneFacet(FacesContext facesContext, UICommand command, String id) {
927        UIMenuSelectOne radio = null;
928        final ValueBinding valueBinding = command.getValueBinding(ATTR_VALUE);
929        if (valueBinding != null) {
930          radio = (UIMenuSelectOne) createComponent(facesContext,
931              UIMenuSelectOne.COMPONENT_TYPE, RENDERER_TYPE_SELECT_ONE_RADIO, id);
932          command.getFacets().put(FACET_ITEMS, radio);
933          radio.setValueBinding(ATTR_VALUE, valueBinding);
934        }
935        return radio;
936      }
937    
938    
939      public static boolean hasSelectedValue(List<SelectItem> items, Object value) {
940        for (SelectItem item : items) {
941          if (item.getValue().equals(value)) {
942            return true;
943          }
944        }
945        return false;
946      }
947    
948      /**
949      * Please use createUISelectBooleanFacet(FacesContext facesContext, UICommand command, String id)
950      *
951      * @deprecated
952      */
953     @Deprecated
954      public static UIComponent createUISelectBooleanFacet(FacesContext facesContext, UICommand command) {
955        return createUISelectBooleanFacet(facesContext, command, null);
956      }
957    
958      public static UIComponent createUISelectBooleanFacet(FacesContext facesContext, UICommand command, String id) {
959        UIComponent checkbox
960            = createComponent(facesContext, UISelectBoolean.COMPONENT_TYPE, RENDERER_TYPE_SELECT_BOOLEAN_CHECKBOX, id);
961        command.getFacets().put(FACET_ITEMS, checkbox);
962        ValueBinding valueBinding = command.getValueBinding(ATTR_VALUE);
963        if (valueBinding != null) {
964          checkbox.setValueBinding(ATTR_VALUE, valueBinding);
965        } else {
966          checkbox.getAttributes().put(ATTR_VALUE, command.getAttributes().get(ATTR_VALUE));
967        }
968        return checkbox;
969      }
970    
971      public static int getIntValue(ValueBinding valueBinding) {
972        return getAsInt(valueBinding.getValue(FacesContext.getCurrentInstance()));
973      }
974    
975      private static int getAsInt(Object value) {
976        int result;
977        if (value instanceof Number) {
978          result = ((Number) value).intValue();
979        } else if (value instanceof String) {
980          result = Integer.parseInt((String) value);
981        } else {
982          throw new IllegalArgumentException("Can't convert " + value + " to int!");
983        }
984        return result;
985      }
986    
987    
988      public static String createPickerId(FacesContext facesContext, UIComponent component, String postfix) {
989        //String id = component.getId();
990        String id = getComponentId(facesContext, component);
991        return id + "_picker" + postfix;
992      }
993    
994      public static String getComponentId(FacesContext facesContext, UIComponent component) {
995        String id = component.getId();
996        //if (id == null) {
997        // XXX What is this?
998        //  id = component.getClientId(facesContext).substring(id.lastIndexOf('_'));
999        //}
1000        return id;
1001      }
1002    
1003      public static UIComponent provideLabel(FacesContext facesContext, UIComponent component) {
1004        UIComponent label = component.getFacet(FACET_LABEL);
1005    
1006    
1007        if (label == null) {
1008          final Map attributes = component.getAttributes();
1009          Object labelText = component.getValueBinding(ATTR_LABEL);
1010          if (labelText == null) {
1011            labelText = attributes.get(ATTR_LABEL);
1012          }
1013    
1014          if (labelText != null) {
1015            Application application = FacesContext.getCurrentInstance().getApplication();
1016            label = application.createComponent(UIOutput.COMPONENT_TYPE);
1017            label.setRendererType(RENDERER_TYPE_LABEL);
1018            String idprefix = ComponentUtil.getComponentId(facesContext, component);
1019            label.setId(idprefix + "_" + FACET_LABEL);
1020            label.setRendered(true);
1021    
1022            if (labelText instanceof ValueBinding) {
1023              label.setValueBinding(ATTR_VALUE, (ValueBinding) labelText);
1024            } else {
1025              label.getAttributes().put(ATTR_VALUE, labelText);
1026            }
1027    
1028            component.getFacets().put(FACET_LABEL, label);
1029          }
1030        }
1031        return label;
1032      }
1033    
1034      /*public static void debug(UIComponent component) {
1035          LOG.error("###############################");
1036          LOG.error("ID " + component.getId());
1037          LOG.error("ClassName " + component.getClass().getName());
1038          if (component instanceof EditableValueHolder) {
1039            EditableValueHolder editableValueHolder = (EditableValueHolder) component;
1040            LOG.error("Valid " + editableValueHolder.isValid());
1041            LOG.error("SubmittedValue " + editableValueHolder.getSubmittedValue());
1042          }
1043        for (Iterator it = component.getFacetsAndChildren(); it.hasNext(); ) {
1044          debug((UIComponent)it.next());
1045        }
1046      } */
1047    
1048    
1049      public static List<SelectItem> getItemsToRender(javax.faces.component.UISelectOne component) {
1050        return getItems(component);
1051      }
1052    
1053      public static List<SelectItem> getItemsToRender(javax.faces.component.UISelectMany component) {
1054        return getItems(component);
1055      }
1056    
1057      private static List<SelectItem> getItems(javax.faces.component.UIInput component) {
1058    
1059        List<SelectItem> selectItems = ComponentUtil.getSelectItems(component);
1060    
1061        String renderRange = (String)
1062            component.getAttributes().get(ATTR_RENDER_RANGE_EXTERN);
1063        if (renderRange == null) {
1064          renderRange = (String)
1065              component.getAttributes().get(ATTR_RENDER_RANGE);
1066        }
1067        if (renderRange == null) {
1068          return selectItems;
1069        }
1070    
1071        int[] indices = RangeParser.getIndices(renderRange);
1072        List<SelectItem> items = new ArrayList<SelectItem>(indices.length);
1073    
1074        if (selectItems.size() != 0) {
1075          for (int indice : indices) {
1076            items.add(selectItems.get(indice));
1077          }
1078        } else {
1079          LOG.warn("No items found! rendering dummys instead!");
1080          for (int i = 0; i < indices.length; i++) {
1081            items.add(new SelectItem(Integer.toString(i), "Item " + i, ""));
1082          }
1083        }
1084        return items;
1085      }
1086    
1087      public static void setValidator(EditableValueHolder editableValueHolder, String validator) {
1088        if (validator != null && editableValueHolder.getValidator() == null) {
1089          if (UIComponentTag.isValueReference(validator)) {
1090            MethodBinding methodBinding =
1091                FacesContext.getCurrentInstance().getApplication().createMethodBinding(validator, VALIDATOR_ARGS);
1092            editableValueHolder.setValidator(methodBinding);
1093          }
1094        }
1095      }
1096    
1097      /**
1098       * @param component
1099       * @param converterId
1100       * @deprecated please use the typesave method {@link #setConverter(javax.faces.component.ValueHolder, String)}
1101       */
1102      @Deprecated
1103      public static void setConverter(UIComponent component, String converterId) {
1104        if (component instanceof ValueHolder) {
1105          setConverter((ValueHolder) component, converterId);
1106        }
1107      }
1108    
1109      public static void setConverter(ValueHolder valueHolder, String converterId) {
1110        if (converterId != null && valueHolder.getConverter() == null) {
1111          final FacesContext facesContext = FacesContext.getCurrentInstance();
1112          final Application application = facesContext.getApplication();
1113          if (UIComponentTag.isValueReference(converterId)) {
1114            ValueBinding valueBinding = application.createValueBinding(converterId);
1115            if (valueHolder instanceof UIComponent) {
1116              ((UIComponent) valueHolder).setValueBinding(ATTR_CONVERTER, valueBinding);
1117            }
1118          } else {
1119            Converter converter = application.createConverter(converterId);
1120            valueHolder.setConverter(converter);
1121          }
1122        }
1123      }
1124    
1125      /**
1126       * @param component
1127       * @param type
1128       * @param action
1129       * @deprecated please use the typesave method {@link #setAction(javax.faces.component.UICommand, String, String)}
1130       */
1131      @Deprecated
1132      public static void setAction(UIComponent component, String type, String action) {
1133        if (component instanceof UICommand) {
1134          setAction((UICommand) component, type, action);
1135        }
1136      }
1137    
1138      public static void setAction(UICommand component, String type, String action) {
1139        String commandType;
1140        final FacesContext facesContext = FacesContext.getCurrentInstance();
1141        final Application application = facesContext.getApplication();
1142        if (type != null && UIComponentTag.isValueReference(type)) {
1143          commandType = (String) application.createValueBinding(type).getValue(facesContext);
1144        } else {
1145          commandType = type;
1146        }
1147        if (commandType != null
1148            && (commandType.equals(COMMAND_TYPE_NAVIGATE)
1149            || commandType.equals(COMMAND_TYPE_RESET)
1150            || commandType.equals(COMMAND_TYPE_SCRIPT))) {
1151          if (commandType.equals(COMMAND_TYPE_NAVIGATE)) {
1152            setStringProperty(component, ATTR_ACTION_LINK, action);
1153          } else if (commandType.equals(COMMAND_TYPE_SCRIPT)) {
1154            setStringProperty(component, ATTR_ACTION_ONCLICK, action);
1155          } else {
1156            LOG.warn("Type reset is not supported");
1157          }
1158        } else {
1159          if (action != null) {
1160            if (UIComponentTag.isValueReference(action)) {
1161              MethodBinding binding = application.createMethodBinding(action, null);
1162              component.setAction(binding);
1163            } else {
1164              component.setAction(new ConstantMethodBinding(action));
1165            }
1166          }
1167        }
1168    
1169      }
1170    
1171      /**
1172       * @param component
1173       * @param suggestMethod
1174       * @deprecated please use the typesave method {@link #setSuggestMethodBinding(UIInput, String)}
1175       */
1176      @Deprecated
1177      public static void setSuggestMethodBinding(UIComponent component, String suggestMethod) {
1178        if (component instanceof UIInput) {
1179          setSuggestMethodBinding((UIInput) component, suggestMethod);
1180        }
1181      }
1182    
1183      public static void setSuggestMethodBinding(UIInput component, String suggestMethod) {
1184        if (suggestMethod != null) {
1185          if (UIComponentTag.isValueReference(suggestMethod)) {
1186            final MethodBinding methodBinding = FacesContext.getCurrentInstance().getApplication()
1187                .createMethodBinding(suggestMethod, new Class[]{String.class});
1188            component.setSuggestMethod(methodBinding);
1189          } else {
1190            throw new IllegalArgumentException(
1191                "Must be a valueReference (suggestMethod): " + suggestMethod);
1192          }
1193        }
1194      }
1195    
1196      public static void setActionListener(ActionSource command, String actionListener) {
1197        final FacesContext facesContext = FacesContext.getCurrentInstance();
1198        final Application application = facesContext.getApplication();
1199        if (actionListener != null) {
1200          if (UIComponentTag.isValueReference(actionListener)) {
1201            MethodBinding binding
1202                = application.createMethodBinding(actionListener, ACTION_LISTENER_ARGS);
1203            command.setActionListener(binding);
1204          } else {
1205            throw new IllegalArgumentException(
1206                "Must be a valueReference (actionListener): " + actionListener);
1207          }
1208        }
1209      }
1210    
1211      public static void setValueChangeListener(EditableValueHolder valueHolder, String valueChangeListener) {
1212        final FacesContext facesContext = FacesContext.getCurrentInstance();
1213        final Application application = facesContext.getApplication();
1214        if (valueChangeListener != null) {
1215          if (UIComponentTag.isValueReference(valueChangeListener)) {
1216            MethodBinding binding
1217                = application.createMethodBinding(valueChangeListener, VALUE_CHANGE_LISTENER_ARGS);
1218            valueHolder.setValueChangeListener(binding);
1219          } else {
1220            throw new IllegalArgumentException(
1221                "Must be a valueReference (valueChangeListener): " + valueChangeListener);
1222          }
1223        }
1224      }
1225    
1226    
1227      public static void setSortActionListener(UIData data, String actionListener) {
1228        final FacesContext facesContext = FacesContext.getCurrentInstance();
1229        final Application application = facesContext.getApplication();
1230        if (actionListener != null) {
1231          if (UIComponentTag.isValueReference(actionListener)) {
1232            MethodBinding binding = application.createMethodBinding(
1233                actionListener, ACTION_LISTENER_ARGS);
1234            data.setSortActionListener(binding);
1235          } else {
1236            throw new IllegalArgumentException(
1237                "Must be a valueReference (sortActionListener): " + actionListener);
1238          }
1239        }
1240      }
1241    
1242      public static void setValueBinding(UIComponent component, String name, String state) {
1243        // TODO: check, if it is an writeable object
1244        if (state != null && UIComponentTag.isValueReference(state)) {
1245          ValueBinding valueBinding = createValueBinding(state);
1246          component.setValueBinding(name, valueBinding);
1247        }
1248      }
1249    
1250      public static void setStateChangeListener(UIData data, String stateChangeListener) {
1251        final FacesContext facesContext = FacesContext.getCurrentInstance();
1252        final Application application = facesContext.getApplication();
1253    
1254        if (stateChangeListener != null) {
1255          if (UIComponentTag.isValueReference(stateChangeListener)) {
1256            Class[] arguments = {SheetStateChangeEvent.class};
1257            MethodBinding binding
1258                = application.createMethodBinding(stateChangeListener, arguments);
1259            data.setStateChangeListener(binding);
1260          } else {
1261            throw new IllegalArgumentException(
1262                "Must be a valueReference (actionListener): " + stateChangeListener);
1263          }
1264        }
1265      }
1266    
1267    
1268      public static String[] getMarkupBinding(FacesContext facesContext, SupportsMarkup component) {
1269        ValueBinding vb = ((UIComponent) component).getValueBinding(ATTR_MARKUP);
1270        if (vb != null) {
1271          Object markups = vb.getValue(facesContext);
1272          if (markups instanceof String[]) {
1273            return (String[]) markups;
1274          } else if (markups instanceof String) {
1275            String[] strings = StringUtils.split((String) markups, ", ");
1276            List<String> result = new ArrayList<String>(strings.length);
1277            for (String string : strings) {
1278              if (string.trim().length() != 0) {
1279                result.add(string.trim());
1280              }
1281            }
1282            return result.toArray(new String[result.size()]);
1283          } else if (markups == null) {
1284            return new String[0];
1285          } else {
1286            return new String[]{markups.toString()};
1287          }
1288        }
1289    
1290        return new String[0];
1291      }
1292    
1293      /**
1294       * colonCount == 0: fully relative
1295       * colonCount == 1: absolute (still normal findComponent syntax)
1296       * colonCount > 1: for each extra colon after 1, go up a naming container
1297       * (to the view root, if naming containers run out)
1298       */
1299    
1300      public static UIComponent findComponent(UIComponent from, String relativeId) {
1301        int idLength = relativeId.length();
1302        // Figure out how many colons
1303        int colonCount = 0;
1304        while (colonCount < idLength) {
1305          if (relativeId.charAt(colonCount) != NamingContainer.SEPARATOR_CHAR) {
1306            break;
1307          }
1308          colonCount++;
1309        }
1310    
1311        // colonCount == 0: fully relative
1312        // colonCount == 1: absolute (still normal findComponent syntax)
1313        // colonCount > 1: for each extra colon after 1, go up a naming container
1314        // (to the view root, if naming containers run out)
1315        if (colonCount > 1) {
1316          relativeId = relativeId.substring(colonCount);
1317          for (int j = 1; j < colonCount; j++) {
1318            while (from.getParent() != null) {
1319              from = from.getParent();
1320              if (from instanceof NamingContainer) {
1321                break;
1322              }
1323            }
1324          }
1325        }
1326        return from.findComponent(relativeId);
1327      }
1328    
1329      public static void invokeOnComponent(FacesContext facesContext, String clientId, UIComponent component,
1330          Callback callback) {
1331        List<UIComponent> list = new ArrayList<UIComponent>();
1332        while (component != null) {
1333          list.add(component);
1334          component = component.getParent();
1335        }
1336        Collections.reverse(list);
1337        invokeOrPrepare(facesContext, list, clientId, callback);
1338        facesContext.getExternalContext().getRequestMap().remove(TobagoConstants.ATTR_ZINDEX);
1339      }
1340    
1341      private static void invokeOrPrepare(FacesContext facesContext, List<UIComponent> list, String clientId,
1342          Callback callback) {
1343        if (list.size() == 1) {
1344          callback.execute(facesContext, list.get(0));
1345        } else if (list.get(0) instanceof UIData) {
1346          prepareOnUIData(facesContext, list, clientId, callback);
1347        } else if (list.get(0) instanceof UIForm) {
1348          prepareOnUIForm(facesContext, list, clientId, callback);
1349        } else if (list.get(0) instanceof UIPopup) {
1350          prepareOnUIPopup(facesContext, list, clientId, callback);
1351        } else {
1352          prepareOnUIComponent(facesContext, list, clientId, callback);
1353        }
1354      }
1355    
1356      @SuppressWarnings(value = "unchecked")
1357      private static void prepareOnUIForm(FacesContext facesContext, List<UIComponent> list, String clientId,
1358          Callback callback) {
1359        UIComponent currentComponent = list.remove(0);
1360        if (!(currentComponent instanceof UIForm)) {
1361          throw new IllegalStateException(currentComponent.getClass().getName());
1362        }
1363        // TODO is this needed?
1364        if (callback instanceof TobagoCallback) {
1365          if (PhaseId.APPLY_REQUEST_VALUES.equals(((TobagoCallback) callback).getPhaseId())) {
1366            currentComponent.decode(facesContext);
1367          }
1368        }
1369        UIForm uiForm = (UIForm) currentComponent;
1370        facesContext.getExternalContext().getRequestMap().put(UIForm.SUBMITTED_MARKER, uiForm.isSubmitted());
1371        invokeOrPrepare(facesContext, list, clientId, callback);
1372    
1373      }
1374    
1375      private static void prepareOnUIComponent(
1376          FacesContext facesContext, List<UIComponent> list, String clientId, Callback callback) {
1377        list.remove(0);
1378        invokeOrPrepare(facesContext, list, clientId, callback);
1379      }
1380    
1381      private static void prepareOnUIPopup(
1382          FacesContext facesContext, List<UIComponent> list, String clientId, Callback callback) {
1383        if (callback instanceof TobagoCallback
1384            && PhaseId.RENDER_RESPONSE.equals(((TobagoCallback) callback).getPhaseId())) {
1385          Integer zIndex = (Integer) facesContext.getExternalContext().getRequestMap().get(TobagoConstants.ATTR_ZINDEX);
1386          if (zIndex == null) {
1387            zIndex = 0;
1388          } else {
1389            zIndex += 10;
1390          }
1391          facesContext.getExternalContext().getRequestMap().put(TobagoConstants.ATTR_ZINDEX, zIndex);
1392        }
1393        list.remove(0);
1394        invokeOrPrepare(facesContext, list, clientId, callback);
1395      }
1396    
1397      private static void prepareOnUIData(FacesContext facesContext, List<UIComponent> list, String clientId,
1398          Callback callback) {
1399        UIComponent currentComponent = list.remove(0);
1400        if (!(currentComponent instanceof UIData)) {
1401          throw new IllegalStateException(currentComponent.getClass().getName());
1402        }
1403    
1404        // we may need setRowIndex on UIData
1405        javax.faces.component.UIData uiData = (javax.faces.component.UIData) currentComponent;
1406        int oldRowIndex = uiData.getRowIndex();
1407        String sheetId = uiData.getClientId(facesContext);
1408        String idRemainder = clientId.substring(sheetId.length());
1409        if (LOG.isInfoEnabled()) {
1410          LOG.info("idRemainder = \"" + idRemainder + "\"");
1411        }
1412        if (idRemainder.startsWith(String.valueOf(NamingContainer.SEPARATOR_CHAR))) {
1413          idRemainder = idRemainder.substring(1);
1414          int idx = idRemainder.indexOf(NamingContainer.SEPARATOR_CHAR);
1415          if (idx > 0) {
1416            String firstPart = idRemainder.substring(0, idx);
1417            if (NumberUtils.isDigits(firstPart)) {
1418              try {
1419                int rowIndex = Integer.parseInt(firstPart);
1420                if (LOG.isInfoEnabled()) {
1421                  LOG.info("set rowIndex = \"" + rowIndex + "\"");
1422                }
1423                uiData.setRowIndex(rowIndex);
1424              } catch (NumberFormatException e) {
1425                LOG.error("idRemainder = \"" + idRemainder + "\"", e);
1426              }
1427            }
1428          }
1429        } else {
1430          if (LOG.isInfoEnabled()) {
1431            LOG.info("no match for \"^:\\d+:.*\"");
1432          }
1433        }
1434    
1435        invokeOrPrepare(facesContext, list, clientId, callback);
1436    
1437        // we should reset rowIndex on UIData
1438        uiData.setRowIndex(oldRowIndex);
1439      }
1440    
1441      public static Object getConvertedValue(
1442          FacesContext facesContext, javax.faces.component.UIInput component, String stringValue) {
1443        try {
1444          Renderer renderer = getRenderer(facesContext, component);
1445          if (renderer != null) {
1446            if (component instanceof UISelectMany) {
1447              final Object converted = renderer.getConvertedValue(facesContext, component, new String[]{stringValue});
1448              return ((Object[]) converted)[0];
1449            } else {
1450              return renderer.getConvertedValue(facesContext, component, stringValue);
1451            }
1452          } else {
1453            Converter converter = component.getConverter();
1454            if (converter == null) {
1455              //Try to find out by value binding
1456              ValueBinding vb = component.getValueBinding("value");
1457              if (vb != null) {
1458                Class valueType = vb.getType(facesContext);
1459                if (valueType != null) {
1460                  converter = facesContext.getApplication().createConverter(valueType);
1461                }
1462              }
1463            }
1464            if (converter != null) {
1465              converter.getAsObject(facesContext, component, stringValue);
1466            }
1467          }
1468        } catch (Exception e) {
1469          LOG.warn("Can't convert string value '" + stringValue + "'", e);
1470        }
1471        return stringValue;
1472      }
1473    }