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