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