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