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