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 578592 2007-09-23 18:51:32Z bommel $
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    
063    import javax.faces.FactoryFinder;
064    import javax.faces.application.Application;
065    import javax.faces.application.FacesMessage;
066    import javax.faces.component.ActionSource;
067    import javax.faces.component.EditableValueHolder;
068    import javax.faces.component.NamingContainer;
069    import javax.faces.component.UICommand;
070    import javax.faces.component.UIComponent;
071    import javax.faces.component.UIGraphic;
072    import javax.faces.component.UIOutput;
073    import javax.faces.component.UIParameter;
074    import javax.faces.component.UISelectItem;
075    import javax.faces.component.UISelectItems;
076    import javax.faces.component.ValueHolder;
077    import javax.faces.context.FacesContext;
078    import javax.faces.convert.Converter;
079    import javax.faces.el.MethodBinding;
080    import javax.faces.el.ValueBinding;
081    import javax.faces.event.ActionEvent;
082    import javax.faces.event.ActionListener;
083    import javax.faces.event.ValueChangeEvent;
084    import javax.faces.model.SelectItem;
085    import javax.faces.render.RenderKit;
086    import javax.faces.render.RenderKitFactory;
087    import javax.faces.render.Renderer;
088    import javax.faces.webapp.UIComponentTag;
089    import javax.servlet.jsp.JspException;
090    import java.util.ArrayList;
091    import java.util.Collection;
092    import java.util.Collections;
093    import java.util.Iterator;
094    import java.util.List;
095    import java.util.Map;
096    import java.util.Set;
097    
098    public class ComponentUtil {
099    
100      private static final Log LOG = LogFactory.getLog(ComponentUtil.class);
101    
102      private static final String RENDER_KEY_PREFIX
103          = "org.apache.myfaces.tobago.component.ComponentUtil.RendererKeyPrefix_";
104      
105      public static final Class[] ACTION_ARGS = {};
106      public static final Class[] ACTION_LISTENER_ARGS = {ActionEvent.class};
107      public static final Class[] VALUE_CHANGE_LISTENER_ARGS = {ValueChangeEvent.class};
108      public static final Class[] VALIDATOR_ARGS = {FacesContext.class, UIComponent.class, Object.class};
109    
110      private ComponentUtil() {
111      }
112    
113    
114      public static boolean hasErrorMessages(FacesContext context) {
115        for (Iterator iter = context.getMessages(); iter.hasNext();) {
116          FacesMessage message = (FacesMessage) iter.next();
117          if (FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0) {
118            return true;
119          }
120        }
121        return false;
122      }
123    
124      public static boolean containsPopupActionListener(UICommand command) {
125        ActionListener [] actionListeners = command.getActionListeners();
126        for(ActionListener actionListener: actionListeners) {
127          if (actionListener instanceof PopupActionListener) {
128            return true;
129          }
130        }
131        return false;
132      }
133    
134      public static String getFacesMessageAsString(FacesContext facesContext, UIComponent component) {
135        Iterator messages = facesContext.getMessages(
136            component.getClientId(facesContext));
137        StringBuilder stringBuffer = new StringBuilder();
138        while (messages.hasNext()) {
139          FacesMessage message = (FacesMessage) messages.next();
140          stringBuffer.append(message.getDetail());
141        }
142        if (stringBuffer.length() > 0) {
143          return stringBuffer.toString();
144        } else {
145          return null;
146        }
147      }
148    
149      public static boolean isInPopup(UIComponent component) {
150        while (component != null) {
151          if (component instanceof UIPopup) {
152            return true;
153          }
154          component = component.getParent();
155        }
156        return false;
157      }
158    
159      public static void resetPage(FacesContext context) {
160        javax.faces.component.UIViewRoot view = context.getViewRoot();
161        if (view != null) {
162          view.getAttributes().remove(UIPage.COMPONENT_TYPE);
163        }
164      }
165      public static UIPage findPage(FacesContext context, UIComponent component) {
166        javax.faces.component.UIViewRoot view = context.getViewRoot();
167        if (view != null) {
168          TransientStateHolder stateHolder = (TransientStateHolder) view.getAttributes().get(UIPage.COMPONENT_TYPE);
169          if (stateHolder == null || stateHolder.isEmpty()) {
170            UIPage page = findPage(component);
171            stateHolder = new TransientStateHolder(page);
172            context.getViewRoot().getAttributes().put(UIPage.COMPONENT_TYPE, stateHolder);
173          }
174          return (UIPage) stateHolder.get();
175        } else {
176          return findPage(component);
177        }
178      }
179      public static UIPage findPage(UIComponent component) {
180        while (component != null) {
181          if (component instanceof UIPage) {
182            return (UIPage) component;
183          }
184          component = component.getParent();
185        }
186        return null;
187      }
188    
189      public static void addStyles(UIComponent component, String[] styles) {
190        UIPage uiPage = ComponentUtil.findPage(component);
191        for (String style : styles) {
192          uiPage.getStyleFiles().add(style);
193        }
194      }
195      public static void addScripts(UIComponent component, String[] scripts) {
196        UIPage uiPage = ComponentUtil.findPage(component);
197        for (String script : scripts) {
198          uiPage.getScriptFiles().add(script);
199        }
200      }
201      public static void addOnloadCommands(UIComponent component, String[] cmds) {
202        UIPage uiPage = ComponentUtil.findPage(component);
203        for (String cmd : cmds) {
204          uiPage.getOnloadScripts().add(cmd);
205        }
206      }
207    
208      public static UIPage findPage(FacesContext facesContext) {
209        return findPageBreadthFirst(facesContext.getViewRoot());
210      }
211    
212      private static UIPage findPageBreadthFirst(UIComponent component) {
213        for (Object o : component.getChildren()) {
214          UIComponent child = (UIComponent) o;
215          if (child instanceof UIPage) {
216            return (UIPage) child;
217          }
218        }
219        for (Object o : component.getChildren()) {
220          UIComponent child = (UIComponent) o;
221          UIPage result = findPageBreadthFirst(child);
222          if (result != null) {
223            return result;
224          }
225        }
226        return null;
227      }
228    
229    
230      public static UIForm findForm(UIComponent component) {
231        while (component != null) {
232          if (component instanceof UIForm) {
233            return (UIForm) component;
234          }
235          component = component.getParent();
236        }
237        return null;
238      }
239    
240      /**
241       * Find all subforms of a component, and collects it.
242       * It does not find subforms of subforms.
243       */
244      public static List<UIForm> findSubForms(UIComponent component) {
245        List<UIForm> collect = new ArrayList<UIForm>();
246        findSubForms(collect, component);
247        return collect;
248      }
249    
250      @SuppressWarnings(value = "unchecked")
251      private static void findSubForms(List<UIForm> collect, UIComponent component) {
252        Iterator<UIComponent> kids = component.getFacetsAndChildren();
253        while (kids.hasNext()) {
254          UIComponent child = kids.next();
255          if (child instanceof UIForm) {
256            collect.add((UIForm) child);
257          } else {
258            findSubForms(collect, child);
259          }
260        }
261      }
262    
263      /**
264       * Looks for the attribute "for" in the component. If there is any
265       * search for the component which is referenced by the "for" attribute,
266       * and return their clientId.
267       * If there is no "for" attribute, return the "clientId" of the parent
268       * (if it has a parent). This is useful for labels.
269       */
270      public static String findClientIdFor(UIComponent component,
271          FacesContext facesContext) {
272        UIComponent forComponent = findFor(component);
273        if (forComponent != null) {
274          String clientId = forComponent.getClientId(facesContext);
275          if (LOG.isDebugEnabled()) {
276            LOG.debug("found clientId: '" + clientId + "'");
277          }
278          return clientId;
279        }
280        if (LOG.isDebugEnabled()) {
281          LOG.debug("found no clientId");
282        }
283        return null;
284      }
285    
286      public static UIComponent findFor(UIComponent component) {
287        String forValue = (String) component.getAttributes().get(ATTR_FOR);
288        if (forValue == null) {
289          return component.getParent();
290        }
291        return component.findComponent(forValue);
292      }
293    
294      public static boolean isInActiveForm(UIComponent component) {
295        while (component != null) {
296          //log.debug("compoent= " + component.getClientId(FacesContext.getCurrentInstance())
297          // + " " + component.getRendererType());
298          if (component instanceof UIForm) {
299            UIForm form = (UIForm) component;
300            if (form.isSubmitted()) {
301              //log.debug("in active form = " + form.getClientId(FacesContext.getCurrentInstance()));
302              return true;
303            } /*else {
304              log.debug("form found but not active = " + form.getClientId(FacesContext.getCurrentInstance()));
305            } */
306          }
307          component = component.getParent();
308        }
309        //log.debug("not in an active form");
310        return false;
311      }
312    
313      public static boolean isError(javax.faces.component.UIInput uiInput) {
314        FacesContext facesContext = FacesContext.getCurrentInstance();
315        return !uiInput.isValid()
316            || facesContext.getMessages(uiInput.getClientId(facesContext)).hasNext();
317      }
318    
319      public static boolean isError(UIComponent component) {
320        if (component instanceof UIInput) {
321           return isError((UIInput) component);
322        }
323        return false;
324      }
325    
326      public static boolean isOutputOnly(UIComponent component) {
327        return getBooleanAttribute(component, ATTR_DISABLED)
328            || getBooleanAttribute(component, ATTR_READONLY);
329      }
330    
331      public static boolean mayValidate(UIComponent component) {
332        return !isOutputOnly(component)
333                && ComponentUtil.isInActiveForm(component);
334      }
335    
336      public static boolean mayUpdateModel(UIComponent component) {
337        return mayValidate(component);
338      }
339    
340      public static boolean getBooleanAttribute(UIComponent component, String name) {
341    
342        Object bool = component.getAttributes().get(name);
343        if (bool == null) {
344          return false;
345        }
346        if (bool instanceof ValueBinding) {
347          bool = ((ValueBinding) bool).getValue(FacesContext.getCurrentInstance());
348        }
349        if (bool instanceof Boolean) {
350          return (Boolean) bool;
351        } else if (bool instanceof String) {
352          LOG.warn("Searching for a boolean, but find a String. Should not happen. "
353              + "attribute: '" + name + "' id: '" + component.getClientId(FacesContext.getCurrentInstance())
354              + "' comp: '" + component + "'");
355          return Boolean.valueOf((String) bool);
356        } else {
357          LOG.warn("Unknown type '" + bool.getClass().getName()
358              + "' for boolean attribute: " + name + " id: " + component.getClientId(FacesContext.getCurrentInstance())
359              + " comp: " + component);
360          return false;
361        }
362      }
363    
364      public static void setRenderedPartially(org.apache.myfaces.tobago.component.UICommand command,
365          String renderers) {
366        if (renderers != null) {
367          if (UIComponentTag.isValueReference(renderers)) {
368            command.setValueBinding(ATTR_RENDERED_PARTIALLY, createValueBinding(renderers));
369          } else {
370            String [] components  = renderers.split(",");
371            command.setRenderedPartially(components);
372          }
373        }
374      }
375    
376      public static void setStyleClasses(UIComponent component, String styleClasses) {
377        if (styleClasses != null) {
378          if (UIComponentTag.isValueReference(styleClasses)) {
379            component.setValueBinding(ATTR_STYLE_CLASS, createValueBinding(styleClasses));
380          } else {
381            String [] classes  = styleClasses.split("[,  ]");
382            if (classes.length > 0) {
383              StyleClasses styles = StyleClasses.ensureStyleClasses(component);
384              for (String clazz: classes) {
385                styles.addFullQualifiedClass(clazz);
386              }
387            }
388          }
389        }
390      }
391    
392      public static void setMarkup(UIComponent markupComponent, String markup) {
393        if (markup != null) {
394          if (markupComponent instanceof SupportsMarkup) {
395            if (UIComponentTag.isValueReference(markup)) {
396              markupComponent.setValueBinding(ATTR_MARKUP, createValueBinding(markup));
397            } else {
398              String [] markups  = markup.split(",");
399              ((SupportsMarkup) markupComponent).setMarkup(markups);
400            }
401          } else {
402            LOG.error("Component did not support Markup " + markupComponent.getClass().getName());
403          }
404        }
405      }
406    
407      public static Object getAttribute(UIComponent component, String name) {
408        Object value = component.getAttributes().get(name);
409        if (value instanceof ValueBinding) {
410          value = ((ValueBinding) value).getValue(FacesContext.getCurrentInstance());
411        }
412        return value;
413      }
414    
415      public static String getStringAttribute(UIComponent component, String name) {
416        return (String) getAttribute(component, name);
417      }
418    
419      public static int getIntAttribute(UIComponent component, String name) {
420        return getIntAttribute(component, name, 0);
421      }
422    
423      public static int getIntAttribute(UIComponent component, String name,
424          int defaultValue) {
425        Object integer = component.getAttributes().get(name);
426        if (integer instanceof Number) {
427          return ((Number) integer).intValue();
428        } else if (integer instanceof String) {
429          try {
430            return Integer.parseInt((String) integer);
431          } catch (NumberFormatException e) {
432            LOG.warn("Can't parse number from string : \"" + integer + "\"!");
433            return defaultValue;
434          }
435        } else if (integer == null) {
436          return defaultValue;
437        } else {
438          LOG.warn("Unknown type '" + integer.getClass().getName()
439              + "' for integer attribute: " + name + " comp: " + component);
440          return defaultValue;
441        }
442      }
443    
444      /**
445       * @deprecated please use the  method {@link #getCharacterAttribute(javax.faces.component.UIComponent, String)}
446       * @param component
447       * @param name
448       */
449      @Deprecated
450      public static Character getCharakterAttribute(UIComponent component, String name) {
451        return getCharacterAttribute(component, name);
452      }
453    
454      public static Character getCharacterAttribute(UIComponent component, String name) {
455        Object character = component.getAttributes().get(name);
456        if (character == null) {
457          return null;
458        } else if (character instanceof Character) {
459          return ((Character) character);
460        } else if (character instanceof String) {
461          String asString = ((String) character);
462          return asString.length() > 0 ? Character.valueOf(asString.charAt(0)) : null;
463        } else {
464          LOG.warn("Unknown type '" + character.getClass().getName()
465              + "' for integer attribute: " + name + " comp: " + component);
466          return null;
467        }
468      }
469    
470      public static boolean isFacetOf(UIComponent component, UIComponent parent) {
471        for (Object o : parent.getFacets().keySet()) {
472          UIComponent facet = parent.getFacet((String) o);
473          if (component.equals(facet)) {
474            return true;
475          }
476        }
477        return false;
478      }
479    
480      // TODO This should not be neseccary, but UIComponentBase.getRenderer() is protected
481      public static LayoutableRendererBase getRenderer(FacesContext facesContext, UIComponent component) {
482        return getRenderer(facesContext, component.getFamily(), component.getRendererType());
483    
484      }
485    
486      public static LayoutableRendererBase getRenderer(FacesContext facesContext, String family, String rendererType) {
487        if (rendererType == null) {
488          return null;
489        }
490    
491        LayoutableRendererBase renderer;
492    
493        Map requestMap = facesContext.getExternalContext().getRequestMap();
494        renderer = (LayoutableRendererBase) requestMap.get(RENDER_KEY_PREFIX + rendererType);
495    
496        if (renderer == null) {
497          RenderKitFactory rkFactory = (RenderKitFactory)
498              FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
499          RenderKit renderKit = rkFactory.getRenderKit(facesContext, facesContext.getViewRoot().getRenderKitId());
500          Renderer myRenderer = renderKit.getRenderer(family, rendererType);
501          if (myRenderer instanceof LayoutableRendererBase) {
502            requestMap.put(RENDER_KEY_PREFIX + rendererType, myRenderer);
503            renderer = (LayoutableRendererBase) myRenderer;
504          } else {
505            return null;
506          }
507        }
508        return renderer;
509      }
510    
511      public static String currentValue(UIComponent component) {
512        String currentValue = null;
513        if (component instanceof ValueHolder) {
514          Object value;
515          if (component instanceof EditableValueHolder) {
516            value = ((EditableValueHolder) component).getSubmittedValue();
517            if (value != null) {
518              return (String) value;
519            }
520          }
521    
522          value = ((ValueHolder) component).getValue();
523          if (value != null) {
524            Converter converter = ((ValueHolder) component).getConverter();
525            if (converter == null) {
526              FacesContext context = FacesContext.getCurrentInstance();
527              converter = context.getApplication().createConverter(value.getClass());
528            }
529            if (converter != null) {
530              currentValue =
531                  converter.getAsString(FacesContext.getCurrentInstance(),
532                      component, value);
533            } else {
534              currentValue = value.toString();
535            }
536          }
537        }
538        return currentValue;
539      }
540    
541      public static List<SelectItem> getSelectItems(UIComponent component) {
542    
543        ArrayList<SelectItem> list = new ArrayList<SelectItem>();
544    
545        for (Object o1 : component.getChildren()) {
546          UIComponent kid = (UIComponent) o1;
547          if (LOG.isDebugEnabled()) {
548            LOG.debug("kid " + kid);
549            LOG.debug("kid " + kid.getClass().getName());
550          }
551          if (kid instanceof UISelectItem) {
552            Object value = ((UISelectItem) kid).getValue();
553            if (value == null) {
554              UISelectItem item = (UISelectItem) kid;
555              if (kid instanceof org.apache.myfaces.tobago.component.UISelectItem) {
556                list.add(new org.apache.myfaces.tobago.model.SelectItem(
557                    (org.apache.myfaces.tobago.component.UISelectItem) kid));
558              } else {
559                list.add(new SelectItem(item.getItemValue() == null ? "" : item.getItemValue(),
560                    item.getItemLabel(),
561                    item.getItemDescription()));
562              }
563            } else if (value instanceof SelectItem) {
564              list.add((SelectItem) value);
565            } else {
566              throw new IllegalArgumentException("TYPE ERROR: value NOT instanceof SelectItem. type="
567                  + value.getClass().getName());
568            }
569          } else if (kid instanceof UISelectItems) {
570            Object value = ((UISelectItems) kid).getValue();
571            if (LOG.isDebugEnabled()) {
572              LOG.debug("value " + value);
573              if (value != null) {
574                LOG.debug("value " + value.getClass().getName());
575              }
576            }
577            if (value == null) {
578              if (LOG.isDebugEnabled()) {
579                LOG.debug("value is null");
580              }
581            } else if (value instanceof SelectItem) {
582              list.add((SelectItem) value);
583            } else if (value instanceof SelectItem[]) {
584              SelectItem[] items = (SelectItem[]) value;
585              for (SelectItem item : items) {
586                list.add(item);
587              }
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      public static void setValueForValueBinding(String name, Object value) {
786        FacesContext context = FacesContext.getCurrentInstance();
787        ValueBinding valueBinding = context.getApplication().createValueBinding(name);
788        valueBinding.setValue(context, value);
789      }
790      public static ValueBinding createValueBinding(String value) {
791        return FacesContext.getCurrentInstance().getApplication()
792            .createValueBinding(value);
793      }
794    
795      public static String getValueFromEl(String script) {
796        if (UIComponentTag.isValueReference(script)) {
797          ValueBinding valueBinding = ComponentUtil.createValueBinding(script);
798          script = (String) valueBinding.getValue(FacesContext.getCurrentInstance());
799        }
800        return script;
801      }
802    
803      public static UIComponent createComponent(String componentType, String rendererType) {
804        final FacesContext facesContext = FacesContext.getCurrentInstance();
805        return createComponent(facesContext, componentType, rendererType);
806      }
807    
808      public static UIComponent createComponent(FacesContext facesContext, String componentType, String rendererType) {
809        UIComponent component
810            = facesContext.getApplication().createComponent(componentType);
811        component.setRendererType(rendererType);
812        return component;
813      }
814    
815      public static UIColumn createTextColumn(String label, String sortable, String align, String value) {
816        UIComponent text = createComponent(UIOutput.COMPONENT_TYPE, RENDERER_TYPE_OUT);
817        setStringProperty(text, ATTR_VALUE, value);
818        setBooleanProperty(text, ATTR_CREATE_SPAN, "false");
819        setBooleanProperty(text, ATTR_ESCAPE, "false");
820        return createColumn(label, sortable, align, text);
821      }
822    
823      public static UIColumn createColumn(String label, String sortable, String align, UIComponent child) {
824        UIColumn column = createColumn(label, sortable, align);
825        column.getChildren().add(child);
826        return column;
827      }
828    
829      private static UIColumn createColumn(String label, String sortable, String align) {
830        UIColumn column = (UIColumn) createComponent(UIColumn.COMPONENT_TYPE, null);
831        setStringProperty(column, ATTR_LABEL, label);
832        setBooleanProperty(column, ATTR_SORTABLE, sortable);
833        setStringProperty(column, ATTR_ALIGN, align);
834        return column;
835      }
836    
837      public static UIMenuSelectOne createUIMenuSelectOneFacet(FacesContext facesContext, UICommand command) {
838        UIMenuSelectOne radio = null;
839        final ValueBinding valueBinding = command.getValueBinding(ATTR_VALUE);
840        if (valueBinding != null) {
841          radio = (UIMenuSelectOne) createComponent(facesContext,
842              UIMenuSelectOne.COMPONENT_TYPE, RENDERER_TYPE_SELECT_ONE_RADIO);
843          command.getFacets().put(FACET_ITEMS, radio);
844          radio.setValueBinding(ATTR_VALUE, valueBinding);
845        }
846        return radio;
847      }
848    
849    
850      public static boolean hasSelectedValue(List<SelectItem> items, Object value) {
851        for (SelectItem item : items) {
852          if (item.getValue().equals(value)) {
853            return true;
854          }
855        }
856        return false;
857      }
858    
859      public static UIComponent createUISelectBooleanFacet(FacesContext facesContext, UICommand command) {
860        UIComponent checkbox
861            = createComponent(facesContext, UISelectBoolean.COMPONENT_TYPE, RENDERER_TYPE_SELECT_BOOLEAN_CHECKBOX);
862        command.getFacets().put(FACET_ITEMS, checkbox);
863        ValueBinding valueBinding = command.getValueBinding(ATTR_VALUE);
864        if (valueBinding != null) {
865          checkbox.setValueBinding(ATTR_VALUE, valueBinding);
866        } else {
867          checkbox.getAttributes().put(ATTR_VALUE, command.getAttributes().get(ATTR_VALUE));
868        }
869        return checkbox;
870      }
871    
872      public static int getIntValue(ValueBinding valueBinding) {
873        return getAsInt(valueBinding.getValue(FacesContext.getCurrentInstance()));
874      }
875    
876      private static int getAsInt(Object value) {
877        int result;
878        if (value instanceof Number) {
879          result = ((Number) value).intValue();
880        } else if (value instanceof String) {
881          result = Integer.parseInt((String) value);
882        } else {
883          throw new IllegalArgumentException("Can't convert " + value + " to int!");
884        }
885        return result;
886      }
887    
888    
889      public static String createPickerId(FacesContext facesContext, UIComponent component, String postfix) {
890        //String id = component.getId();
891        String id = getComponentId(facesContext, component);
892        return id + "_picker" + postfix;
893      }
894    
895      public static String getComponentId(FacesContext facesContext, UIComponent component) {
896        String id = component.getId();
897        //if (id == null) {
898          // XXX What is this?
899        //  id = component.getClientId(facesContext).substring(id.lastIndexOf('_'));
900        //}
901        return id;
902      }
903    
904      public static UIComponent provideLabel(FacesContext facesContext, UIComponent component) {
905        UIComponent label = component.getFacet(FACET_LABEL);
906    
907    
908        if (label == null) {
909          final Map attributes = component.getAttributes();
910          Object labelText = component.getValueBinding(ATTR_LABEL);
911          if (labelText ==null) {
912            labelText = attributes.get(ATTR_LABEL);
913          }
914    
915          if (labelText != null) {
916            Application application = FacesContext.getCurrentInstance().getApplication();
917            label = application.createComponent(UIOutput.COMPONENT_TYPE);
918            label.setRendererType(RENDERER_TYPE_LABEL);
919            String idprefix = ComponentUtil.getComponentId(facesContext, component);
920            label.setId(idprefix + "_" + FACET_LABEL);
921            label.setRendered(true);
922    
923            if (labelText instanceof ValueBinding) {
924              label.setValueBinding(ATTR_VALUE, (ValueBinding) labelText);
925            } else {
926              label.getAttributes().put(ATTR_VALUE, labelText);
927            }
928    
929            component.getFacets().put(FACET_LABEL, label);
930          }
931        }
932        return label;
933      }
934    
935      /*public static void debug(UIComponent component) {
936          LOG.error("###############################");
937          LOG.error("ID " + component.getId());
938          LOG.error("ClassName " + component.getClass().getName());
939          if (component instanceof EditableValueHolder) {
940            EditableValueHolder editableValueHolder = (EditableValueHolder) component;
941            LOG.error("Valid " + editableValueHolder.isValid());
942            LOG.error("SubmittedValue " + editableValueHolder.getSubmittedValue());
943          }
944        for (Iterator it = component.getFacetsAndChildren(); it.hasNext(); ) {
945          debug((UIComponent)it.next());
946        }
947      } */
948      
949    
950      public static List<SelectItem> getItemsToRender(javax.faces.component.UISelectOne component) {
951        return getItems(component);
952      }
953    
954      public static List<SelectItem> getItemsToRender(javax.faces.component.UISelectMany component) {
955        return getItems(component);
956      }
957    
958      private static List<SelectItem> getItems(javax.faces.component.UIInput component) {
959    
960        List<SelectItem> selectItems = ComponentUtil.getSelectItems(component);
961    
962        String renderRange = (String)
963            component.getAttributes().get(ATTR_RENDER_RANGE_EXTERN);
964        if (renderRange == null) {
965          renderRange = (String)
966              component.getAttributes().get(ATTR_RENDER_RANGE);
967        }
968        if (renderRange == null) {
969          return selectItems;
970        }
971    
972        int[] indices = RangeParser.getIndices(renderRange);
973        List<SelectItem> items = new ArrayList<SelectItem>(indices.length);
974    
975        if (selectItems.size() != 0) {
976          for (int indice : indices) {
977            items.add(selectItems.get(indice));
978          }
979        } else {
980          LOG.warn("No items found! rendering dummys instead!");
981          for (int i = 0; i < indices.length; i++) {
982            items.add(new SelectItem(Integer.toString(i), "Item " + i, ""));
983          }
984        }
985        return items;
986      }
987      public static void setValidator(EditableValueHolder editableValueHolder, String validator) {
988        if (validator != null && editableValueHolder.getValidator() == null) {
989          if (UIComponentTag.isValueReference(validator)) {
990            MethodBinding methodBinding =
991                FacesContext.getCurrentInstance().getApplication().createMethodBinding(validator, VALIDATOR_ARGS);
992            editableValueHolder.setValidator(methodBinding);
993          }
994        }
995      }
996    
997      /**
998       * @deprecated please use the typesave method {@link #setConverter(javax.faces.component.ValueHolder, String)}
999       * @param component
1000       * @param converterId
1001       */
1002      @Deprecated
1003      public static void setConverter(UIComponent component, String converterId) {
1004        if (component instanceof ValueHolder) {
1005          setConverter((ValueHolder) component, converterId);
1006        }
1007      }
1008    
1009      public static void setConverter(ValueHolder valueHolder, String converterId) {
1010        if (converterId != null && valueHolder.getConverter() == null) {
1011          final FacesContext facesContext = FacesContext.getCurrentInstance();
1012          final Application application = facesContext.getApplication();
1013          if (UIComponentTag.isValueReference(converterId)) {
1014            ValueBinding valueBinding = application.createValueBinding(converterId);
1015            if (valueHolder instanceof UIComponent) {
1016              ((UIComponent) valueHolder).setValueBinding(ATTR_CONVERTER, valueBinding);
1017            }
1018          } else {
1019            Converter converter = application.createConverter(converterId);
1020            valueHolder.setConverter(converter);
1021          }
1022        }
1023      }
1024    
1025      /**
1026       * @deprecated please use the typesave method {@link #setAction(javax.faces.component.UICommand, String, String)}
1027       * @param component
1028       * @param type
1029       * @param action
1030       */
1031      @Deprecated
1032      public static void setAction(UIComponent component, String type, String action) {
1033        if (component instanceof UICommand) {
1034          setAction((UICommand) component, type, action);
1035        }
1036      }
1037      
1038      public static void setAction(UICommand component, String type, String action) {
1039        String commandType;
1040        final FacesContext facesContext = FacesContext.getCurrentInstance();
1041        final Application application = facesContext.getApplication();
1042        if (type != null && UIComponentTag.isValueReference(type)) {
1043          commandType = (String) application.createValueBinding(type).getValue(facesContext);
1044        } else {
1045          commandType = type;
1046        }
1047        if (commandType != null
1048            && (commandType.equals(COMMAND_TYPE_NAVIGATE)
1049              || commandType.equals(COMMAND_TYPE_RESET)
1050              || commandType.equals(COMMAND_TYPE_SCRIPT))) {
1051          if (commandType.equals(COMMAND_TYPE_NAVIGATE)) {
1052            setStringProperty(component, ATTR_ACTION_LINK, action);
1053          } else if (commandType.equals(COMMAND_TYPE_SCRIPT)) {
1054            setStringProperty(component, ATTR_ACTION_ONCLICK, action);
1055          } else {
1056            LOG.warn("Type reset is not supported");
1057          }
1058        } else {
1059          if (action != null) {
1060            if (UIComponentTag.isValueReference(action)) {
1061              MethodBinding binding = application.createMethodBinding(action, null);
1062              component.setAction(binding);
1063            } else {
1064              component.setAction(new ConstantMethodBinding(action));
1065            }
1066          }
1067        }
1068    
1069      }
1070    
1071      /**
1072       * @deprecated please use the typesave method {@link #setSuggestMethodBinding(UIInput, String)}
1073       * @param component
1074       * @param suggestMethod
1075       */
1076      @Deprecated
1077      public static void setSuggestMethodBinding(UIComponent component, String suggestMethod) {
1078        if (component instanceof UIInput) {
1079          setSuggestMethodBinding((UIInput) component, suggestMethod);
1080        }
1081      }
1082      public static void setSuggestMethodBinding(UIInput component, String suggestMethod) {
1083        if (suggestMethod != null) {
1084          if (UIComponentTag.isValueReference(suggestMethod)) {
1085            final MethodBinding methodBinding = FacesContext.getCurrentInstance().getApplication()
1086                .createMethodBinding(suggestMethod, new Class[] {String.class});
1087            component.setSuggestMethod(methodBinding);
1088          } else {
1089            throw new IllegalArgumentException(
1090                "Must be a valueReference (suggestMethod): " + suggestMethod);
1091          }
1092        }
1093      }
1094      
1095      public static void setActionListener(ActionSource command, String actionListener) {
1096        final FacesContext facesContext = FacesContext.getCurrentInstance();
1097        final Application application = facesContext.getApplication();
1098        if (actionListener != null) {
1099          if (UIComponentTag.isValueReference(actionListener)) {
1100            MethodBinding binding
1101                = application.createMethodBinding(actionListener, ACTION_LISTENER_ARGS);
1102            command.setActionListener(binding);
1103          } else {
1104            throw new IllegalArgumentException(
1105                "Must be a valueReference (actionListener): " + actionListener);
1106          }
1107        }
1108      }
1109    
1110      public static void setValueChangeListener(EditableValueHolder valueHolder, String valueChangeListener) {
1111         final FacesContext facesContext = FacesContext.getCurrentInstance();
1112         final Application application = facesContext.getApplication();
1113         if (valueChangeListener != null) {
1114           if (UIComponentTag.isValueReference(valueChangeListener)) {
1115             MethodBinding binding
1116                 = application.createMethodBinding(valueChangeListener, VALUE_CHANGE_LISTENER_ARGS);
1117             valueHolder.setValueChangeListener(binding);
1118           } else {
1119             throw new IllegalArgumentException(
1120                 "Must be a valueReference (valueChangeListener): " + valueChangeListener);
1121           }
1122         }
1123       }
1124    
1125    
1126      public static void setSortActionListener(UIData data, String actionListener) {
1127        final FacesContext facesContext = FacesContext.getCurrentInstance();
1128        final Application application = facesContext.getApplication();
1129        if (actionListener != null) {
1130          if (UIComponentTag.isValueReference(actionListener)) {
1131            MethodBinding binding = application.createMethodBinding(
1132                actionListener, ACTION_LISTENER_ARGS);
1133            data.setSortActionListener(binding);
1134          } else {
1135            throw new IllegalArgumentException(
1136                "Must be a valueReference (sortActionListener): " + actionListener);
1137          }
1138        }
1139      }
1140    
1141      public static void setValueBinding(UIComponent component, String name, String state) {
1142        // TODO: check, if it is an writeable object
1143        if (state != null && UIComponentTag.isValueReference(state)) {
1144          ValueBinding valueBinding = createValueBinding(state);
1145          component.setValueBinding(name, valueBinding);
1146        }
1147      }
1148    
1149      public static void setStateChangeListener(UIData data, String stateChangeListener) {
1150        final FacesContext facesContext = FacesContext.getCurrentInstance();
1151        final Application application = facesContext.getApplication();
1152    
1153        if (stateChangeListener != null) {
1154          if (UIComponentTag.isValueReference(stateChangeListener)) {
1155            Class[] arguments = {SheetStateChangeEvent.class};
1156            MethodBinding binding
1157                = application.createMethodBinding(stateChangeListener, arguments);
1158            data.setStateChangeListener(binding);
1159          } else {
1160            throw new IllegalArgumentException(
1161                "Must be a valueReference (actionListener): " + stateChangeListener);
1162          }
1163        }
1164      }
1165    
1166    
1167    
1168      public static String[] getMarkupBinding(FacesContext facesContext, SupportsMarkup component) {
1169        ValueBinding vb = ((UIComponent) component).getValueBinding(ATTR_MARKUP);
1170        if (vb != null) {
1171          Object markups = vb.getValue(facesContext);
1172          if (markups instanceof String[]) {
1173            return (String[]) markups;
1174          } else if (markups instanceof String) {
1175            String[] strings = ((String) markups).split("[, ]");
1176            List<String> result = new ArrayList<String>(strings.length);
1177            for (String string : strings) {
1178              if (string.trim().length() != 0) {
1179                result.add(string.trim());
1180              }
1181            }
1182            return result.toArray(new String[result.size()]);
1183          } else if (markups == null) {
1184            return new String[0];
1185          } else {
1186            return new String[]{markups.toString()};
1187          }
1188        }
1189    
1190        return new String[0];
1191      }
1192    
1193      public static UIComponent findComponent(UIComponent from, String relativeId) {
1194        int idLength = relativeId.length();
1195        // Figure out how many colons
1196        int colonCount = 0;
1197        while (colonCount < idLength) {
1198          if (relativeId.charAt(colonCount) != NamingContainer.SEPARATOR_CHAR) {
1199            break;
1200          }
1201          colonCount++;
1202        }
1203    
1204        // colonCount == 0: fully relative
1205        // colonCount == 1: absolute (still normal findComponent syntax)
1206        // colonCount > 1: for each extra colon after 1, go up a naming container
1207        // (to the view root, if naming containers run out)
1208        if (colonCount > 1) {
1209          relativeId = relativeId.substring(colonCount);
1210          for (int j = 1; j < colonCount; j++) {
1211            while (from.getParent() != null) {
1212              from = from.getParent();
1213              if (from instanceof NamingContainer) {
1214                break;
1215              }
1216            }
1217          }
1218        }
1219        return from.findComponent(relativeId);
1220      }
1221    
1222      public static void invokeOnComponent(FacesContext facesContext, String clientId, UIComponent component,
1223          Callback callback) {
1224        List<UIComponent> list = new ArrayList<UIComponent>();
1225        while (component != null) {
1226          list.add(component);
1227          component = component.getParent();
1228        }
1229        Collections.reverse(list);
1230        invokeOrPrepare(facesContext, list, clientId, callback);
1231      }
1232    
1233      private static void invokeOrPrepare(FacesContext facesContext, List<UIComponent> list, String clientId,
1234          Callback callback) {
1235        if (list.size() == 1) {
1236          callback.execute(facesContext, list.get(0));
1237        } else if (list.get(0) instanceof UIData) {
1238          prepareOnUIData(facesContext, list, clientId, callback);
1239        } else {
1240          prepareOnUIComponent(facesContext, list, clientId, callback);
1241        }
1242      }
1243    
1244      private static void prepareOnUIComponent(FacesContext facesContext, List<UIComponent> list, String clientId,
1245          Callback callback) {
1246        list.remove(0);
1247        invokeOrPrepare(facesContext, list, clientId, callback);
1248      }
1249    
1250      private static void prepareOnUIData(FacesContext facesContext, List<UIComponent> list, String clientId,
1251          Callback callback) {
1252        UIComponent currentComponent = list.remove(0);
1253        if (!(currentComponent instanceof UIData)) {
1254          throw new IllegalStateException(currentComponent.getClass().getName());
1255        }
1256    
1257        // we may need setRowIndex on UIData
1258        javax.faces.component.UIData uiData = (javax.faces.component.UIData) currentComponent;
1259        int oldRowIndex = uiData.getRowIndex();
1260        String sheetId = uiData.getClientId(facesContext);
1261        String idRemainder = clientId.substring(sheetId.length());
1262        LOG.info("idRemainder = \"" + idRemainder + "\"");
1263        if (idRemainder.matches("^:\\d+:.*")) {
1264          idRemainder = idRemainder.substring(1);
1265          int idx = idRemainder.indexOf(":");
1266          try {
1267            int rowIndex = Integer.parseInt(idRemainder.substring(0, idx));
1268            LOG.info("set rowIndex = \"" + rowIndex + "\"");
1269            uiData.setRowIndex(rowIndex);
1270          } catch (NumberFormatException e) {
1271            LOG.error("idRemainder = \"" + idRemainder + "\"" , e);
1272          }
1273        } else {
1274          LOG.info("no match for \"^:\\d+:.*\"");
1275        }
1276    
1277        invokeOrPrepare(facesContext, list, clientId, callback);
1278    
1279        // we should reset rowIndex on UIData
1280        uiData.setRowIndex(oldRowIndex);
1281      }
1282    }