View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   */
19  package org.apache.myfaces.shared.renderkit;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.FileNotFoundException;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.Serializable;
26  import java.lang.reflect.Array;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.Date;
30  import java.util.HashSet;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  
36  import javax.el.ValueExpression;
37  import javax.faces.FacesException;
38  import javax.faces.FactoryFinder;
39  import javax.faces.component.EditableValueHolder;
40  import javax.faces.component.NamingContainer;
41  import javax.faces.component.UIComponent;
42  import javax.faces.component.UIForm;
43  import javax.faces.component.UIInput;
44  import javax.faces.component.UINamingContainer;
45  import javax.faces.component.UIOutput;
46  import javax.faces.component.UISelectMany;
47  import javax.faces.component.UISelectOne;
48  import javax.faces.component.UIViewRoot;
49  import javax.faces.component.ValueHolder;
50  import javax.faces.component.html.HtmlInputText;
51  import javax.faces.context.FacesContext;
52  import javax.faces.convert.Converter;
53  import javax.faces.convert.ConverterException;
54  import javax.faces.el.PropertyNotFoundException;
55  import javax.faces.el.ValueBinding;
56  import javax.faces.event.PhaseId;
57  import javax.faces.model.SelectItem;
58  import javax.faces.render.RenderKit;
59  import javax.faces.render.RenderKitFactory;
60  import javax.faces.render.ResponseStateManager;
61  
62  import org.apache.commons.logging.Log;
63  import org.apache.commons.logging.LogFactory;
64  import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
65  import org.apache.myfaces.shared.util.HashMapUtils;
66  import org.apache.myfaces.shared.util.SelectItemsIterator;
67  
68  /***
69   * @author Manfred Geiler (latest modification by $Author: matzew $)
70   * @version $Revision: 807542 $ $Date: 2009-08-25 05:22:20 -0500 (Tue, 25 Aug 2009) $
71   */
72  public final class RendererUtils
73  {
74      private RendererUtils(){
75          //nope
76      }
77      
78      private static final Log log = LogFactory.getLog(RendererUtils.class);
79  
80      public static final String SELECT_ITEM_LIST_ATTR = RendererUtils.class.getName() + ".LIST";
81      public static final String EMPTY_STRING = "";
82      public static final Object NOTHING = new Serializable() {
83          public boolean equals(final Object o)
84          {
85              if (o != null)
86              {
87                  if (o.getClass().equals(this.getClass()))
88                  {
89                      return true;
90                  }
91              }
92              return false;
93          }
94      };
95  
96      public static final String ACTION_FOR_LIST = "org.apache.myfaces.ActionForList";
97      public static final String ACTION_FOR_PHASE_LIST = "org.apache.myfaces.ActionForPhaseList";
98  
99      public static final String SEQUENCE_PARAM = "jsf_sequence";
100 
101     private static final String RENDER_KIT_IMPL = RendererUtils.class.getName() + ".RenderKitImpl";
102 
103     public static String getPathToComponent(UIComponent component)
104     {
105         StringBuffer buf = new StringBuffer();
106 
107         if(component == null)
108         {
109             buf.append("{Component-Path : ");
110             buf.append("[null]}");
111             return buf.toString();
112         }
113 
114         getPathToComponent(component,buf);
115 
116         buf.insert(0,"{Component-Path : ");
117         buf.append("}");
118 
119         return buf.toString();
120     }
121 
122     private static void getPathToComponent(UIComponent component, StringBuffer buf)
123     {
124         if(component == null)
125             return;
126 
127         StringBuffer intBuf = new StringBuffer();
128 
129         intBuf.append("[Class: ");
130         intBuf.append(component.getClass().getName());
131         if(component instanceof UIViewRoot)
132         {
133             intBuf.append(",ViewId: ");
134             intBuf.append(((UIViewRoot) component).getViewId());
135         }
136         else
137         {
138             intBuf.append(",Id: ");
139             intBuf.append(component.getId());
140         }
141         intBuf.append("]");
142 
143         buf.insert(0,intBuf.toString());
144 
145         getPathToComponent(component.getParent(), buf);
146     }
147 
148     public static String getConcatenatedId(FacesContext context, UIComponent container,
149                                            String clientId)
150     {
151         UIComponent child = container.findComponent(clientId);
152 
153         if(child == null)
154                 return clientId;
155 
156         return getConcatenatedId(context, child);
157     }
158 
159     public static String getConcatenatedId(FacesContext context, UIComponent component)
160     {
161         if (context == null) throw new NullPointerException("context");
162 
163         StringBuffer idBuf = new StringBuffer();
164 
165         idBuf.append(component.getId());
166 
167         UIComponent parent;
168 
169         while((parent = component.getParent())!=null)
170         {
171             if(parent instanceof NamingContainer)
172             {
173                 idBuf.insert(0,NamingContainer.SEPARATOR_CHAR);
174                 idBuf.insert(0,parent.getId());
175             }
176         }
177 
178         return idBuf.toString();
179     }
180 
181     public static Boolean getBooleanValue(UIComponent component)
182     {
183         Object value = getObjectValue(component);
184         // Try to convert to Boolean if it is a String
185         if (value instanceof  String) {
186             value = Boolean.valueOf((String)value);
187         }
188         
189         if (value==null || value instanceof Boolean)
190         {
191             return (Boolean) value;
192         }
193 
194         throw new IllegalArgumentException("Expected submitted value of type Boolean for Component : "+
195                     getPathToComponent(component));
196         
197     }
198 
199     public static Date getDateValue(UIComponent component)
200     {
201         Object value = getObjectValue(component);
202         if (value==null || value instanceof Date)
203         {
204             return (Date) value;
205         }
206 
207         throw new IllegalArgumentException("Expected submitted value of type Date for component : "
208                 +getPathToComponent(component));
209     }
210 
211     public static Object getObjectValue(UIComponent component)
212     {
213         if (!(component instanceof ValueHolder))
214         {
215             throw new IllegalArgumentException("Component : "+
216                     getPathToComponent(component)+"is not a ValueHolder");
217         }
218 
219         if (component instanceof EditableValueHolder)
220         {
221             Object value = ((EditableValueHolder)component).getSubmittedValue();
222             if(value != null && !NOTHING.equals(value))
223             {
224                 return value;
225             }
226         }
227 
228         return ((ValueHolder)component).getValue();
229     }
230 
231     @Deprecated
232     public static String getStringValue(FacesContext context, ValueBinding vb)
233     {
234         Object value = vb.getValue(context);
235         if (value != null)
236         {
237             return value.toString();
238         }
239         return null;
240     }
241 
242     public static String getStringValue(FacesContext context, ValueExpression ve)
243     {
244         Object value = ve.getValue(context.getELContext());
245         if (value != null)
246         {
247             return value.toString();
248         }
249         return null;
250     }
251 
252     public static String getStringValue(FacesContext facesContext,
253                                         UIComponent component)
254     {
255         try
256         {
257             if (!(component instanceof ValueHolder))
258             {
259                 throw new IllegalArgumentException("Component : "+getPathToComponent(component)+"is not a ValueHolder");
260             }
261 
262             if (component instanceof EditableValueHolder)
263             {
264                 Object submittedValue = ((EditableValueHolder)component).getSubmittedValue();
265                 if (submittedValue != null)
266                 {
267                     if (submittedValue instanceof String)
268                     {
269                         if (log.isDebugEnabled()) log.debug("returning 1 '" + submittedValue + "'");
270                         return (String)submittedValue;
271                     }
272 
273                     throw new IllegalArgumentException("Expected submitted value of type String for component : "
274                             +getPathToComponent(component));
275                 }
276             }
277 
278             Object value;
279 
280             if(component instanceof EditableValueHolder) {
281 
282                 EditableValueHolder holder = (EditableValueHolder) component;
283                 
284                 if(holder.isLocalValueSet()) {
285                     value = holder.getLocalValue();
286                 } else {
287                     value = getValue(component);
288                 }
289             }
290             else {
291                 value = getValue(component);
292             }
293 
294             Converter converter = ((ValueHolder)component).getConverter();
295             if (converter == null  && value != null)
296             {
297 
298                 try
299                 {
300                     converter = facesContext.getApplication().createConverter(value.getClass());
301                     if (log.isDebugEnabled()) log.debug("the created converter is " + converter);
302                 }
303                 catch (FacesException e)
304                 {
305                     log.error("No converter for class " + value.getClass().getName() + " found (component id=" + component.getId() + ").");
306                     // converter stays null
307                 }
308             }
309 
310             if (converter == null)
311             {
312                 if (value == null)
313                 {
314                     if (log.isDebugEnabled()) log.debug("returning an empty string");
315                     return "";
316                 }
317 
318                 if (log.isDebugEnabled()) log.debug("returning an .toString");
319                 return value.toString();
320                 
321             }
322 
323             if (log.isDebugEnabled()) log.debug("returning converter get as string " + converter);
324             return converter.getAsString(facesContext, component, value);
325             
326         }
327         catch(PropertyNotFoundException ex)
328         {
329             log.error("Property not found - called by component : "+getPathToComponent(component),ex);
330 
331             throw ex;
332         }
333     }
334 
335     private static Object getValue(UIComponent component) {
336         Object value;
337         try
338         {
339             value = ((ValueHolder) component).getValue();
340         }
341         catch(Exception ex)
342         {
343             throw new FacesException("Could not retrieve value of component with path : "+
344                     getPathToComponent(component),ex);
345         }
346         return value;
347     }
348 
349     /***
350      * See JSF Spec. 8.5 Table 8-1
351      * @param value
352      * @return boolean
353      */
354     public static boolean isDefaultAttributeValue(Object value)
355     {
356         if (value == null)
357         {
358             return true;
359         }
360         else if (value instanceof Boolean)
361         {
362             return !((Boolean) value).booleanValue();
363         }
364         else if (value instanceof Number)
365         {
366             if (value instanceof Integer)
367             {
368                 return ((Number)value).intValue() == Integer.MIN_VALUE;
369             }
370             else if (value instanceof Double)
371             {
372                 return ((Number)value).doubleValue() == Double.MIN_VALUE;
373             }
374             else if (value instanceof Long)
375             {
376                 return ((Number)value).longValue() == Long.MIN_VALUE;
377             }
378             else if (value instanceof Byte)
379             {
380                 return ((Number)value).byteValue() == Byte.MIN_VALUE;
381             }
382             else if (value instanceof Float)
383             {
384                 return ((Number)value).floatValue() == Float.MIN_VALUE;
385             }
386             else if (value instanceof Short)
387             {
388                 return ((Number)value).shortValue() == Short.MIN_VALUE;
389             }
390         }
391         return false;
392     }
393 
394     /***
395      * Find the proper Converter for the given UIOutput component.
396      * @return the Converter or null if no Converter specified or needed
397      * @throws FacesException if the Converter could not be created
398      */
399     public static Converter findUIOutputConverter(FacesContext facesContext,
400                                                   UIOutput component)
401             throws FacesException
402     {
403         return _SharedRendererUtils.findUIOutputConverter(facesContext, component);
404     }
405 
406 
407     /***
408      * Find proper Converter for the entries in the associated List or Array of
409      * the given UISelectMany as specified in API Doc of UISelectMany.
410      * @return the Converter or null if no Converter specified or needed
411      * @throws FacesException if the Converter could not be created
412      */
413     public static Converter findUISelectManyConverter(FacesContext facesContext,
414                                                       UISelectMany component)
415     {
416         Converter converter = component.getConverter();
417         if (converter != null) return converter;
418 
419         //Try to find out by value binding
420         ValueExpression vb = component.getValueExpression("value");
421         if (vb == null) return null;
422 
423         //By some strange reason vb.getType(facesContext.getELContext());
424         //does not return the same as vb.getValue(facesContext.getELContext()).getClass(),
425         //so we need to use this instead.
426         Class valueType = null;
427         Object value = vb.getValue(facesContext.getELContext()); 
428         valueType = (value != null) ? value.getClass() :
429             vb.getType(facesContext.getELContext()) ;
430         
431         if (valueType == null) return null;
432         
433         if (List.class.isAssignableFrom(valueType))
434         {
435             //According to API Doc of UISelectMany the assumed entry type for a List is String
436             //--> so basically no converter needed
437 
438             // However, if the List contains something other than Strings, we can attempt
439             // to find a suitable converter. In JDK 1.4, we can try to find out what the List
440             // contains by looking at the SelectItem value of the first item. With generics in
441             // JDK 1.5, it would be much easier to determine the type.
442 
443             List selectItems = RendererUtils.internalGetSelectItemList(component);
444 
445             if (selectItems != null && selectItems.size() > 0)
446             {
447                 SelectItem selectItem = (SelectItem) selectItems.get(0);
448                 Class listComponentType = selectItem.getValue().getClass();
449 
450                     try
451                     {
452                         return facesContext.getApplication().createConverter(listComponentType);
453                     }
454                     catch (FacesException e)
455                     {
456                         log.error("No Converter for type " + listComponentType.getName() + " found", e);
457                         return null;
458                     }
459 
460             }
461 
462             return null;
463         }
464 
465         if (!valueType.isArray())
466         {
467             throw new IllegalArgumentException("ValueBinding for UISelectMany : "+getPathToComponent(component)+" must be of type List or Array");
468         }
469 
470         Class arrayComponentType = valueType.getComponentType();
471         if (String.class.equals(arrayComponentType)) return null;    //No converter needed for String type
472         if (Object.class.equals(arrayComponentType)) return null;    //There is no converter for Object class
473 
474         try
475         {
476             return facesContext.getApplication().createConverter(arrayComponentType);
477         }
478         catch (FacesException e)
479         {
480             log.error("No Converter for type " + arrayComponentType.getName() + " found", e);
481             return null;
482         }
483     }
484 
485 
486     public static void checkParamValidity(FacesContext facesContext, UIComponent uiComponent, Class compClass)
487     {
488         if(facesContext == null)
489             throw new NullPointerException("facesContext may not be null");
490         if(uiComponent == null)
491             throw new NullPointerException("uiComponent may not be null");
492 
493         //if (compClass != null && !(compClass.isAssignableFrom(uiComponent.getClass())))
494         // why isAssignableFrom with additional getClass method call if isInstance does the same?
495         if (compClass != null && !(compClass.isInstance(uiComponent)))
496         {
497             throw new IllegalArgumentException("uiComponent : "+getPathToComponent(uiComponent)+
498                     " is not instance of "+compClass.getName()+" as it should be");
499         }
500     }
501 
502 
503     public static void renderChildren(FacesContext facesContext, UIComponent component)
504             throws IOException
505     {
506         if (component.getChildCount() > 0)
507         {
508             for (Iterator it = component.getChildren().iterator(); it.hasNext(); )
509             {
510                 UIComponent child = (UIComponent)it.next();
511                 renderChild(facesContext, child);
512             }
513         }
514     }
515 
516 
517     public static void renderChild(FacesContext facesContext, UIComponent child)
518             throws IOException
519     {
520         if (!child.isRendered())
521         {
522             return;
523         }
524 
525         child.encodeBegin(facesContext);
526         if (child.getRendersChildren())
527         {
528             child.encodeChildren(facesContext);
529         }
530         else
531         {
532             renderChildren(facesContext, child);
533         }
534         child.encodeEnd(facesContext);
535     }
536 
537 
538 
539     /***
540      * @param uiSelectOne
541      * @return List of SelectItem Objects
542      */
543     public static List getSelectItemList(UISelectOne uiSelectOne)
544     {
545         return internalGetSelectItemList(uiSelectOne);
546     }
547 
548     /***
549      * @param uiSelectMany
550      * @return List of SelectItem Objects
551      */
552     public static List getSelectItemList(UISelectMany uiSelectMany)
553     {
554         return internalGetSelectItemList(uiSelectMany);
555     }
556 
557     private static List internalGetSelectItemList(UIComponent uiComponent)
558     {
559         /* TODO: Shall we cache the list in a component attribute?
560         ArrayList list = (ArrayList)uiComponent.getAttributes().get(SELECT_ITEM_LIST_ATTR);
561         if (list != null)
562         {
563             return list;
564         }
565          */
566         
567         List list = new ArrayList();
568         
569         for (Iterator iter = new SelectItemsIterator(uiComponent); iter.hasNext();)
570         {
571             list.add(iter.next());            
572         }        
573         return list;
574     }
575 
576 
577     /***
578      * Convenient utility method that returns the currently submitted values of
579      * a UISelectMany component as a Set, of which the contains method can then be
580      * easily used to determine if a select item is currently selected.
581      * Calling the contains method of this Set with the renderable (String converted) item value
582      * as argument returns true if this item is selected.
583      * @param uiSelectMany
584      * @return Set containing all currently selected values
585      */
586     public static Set getSubmittedValuesAsSet(FacesContext context, UIComponent component, Converter converter, UISelectMany uiSelectMany)
587     {
588         Object submittedValues = uiSelectMany.getSubmittedValue();
589         if (submittedValues == null)
590         {
591             return null;
592         }
593 
594         if(converter != null) {
595             converter = new PassThroughAsStringConverter(converter);
596         }
597 
598         return internalSubmittedOrSelectedValuesAsSet(context, component, converter, uiSelectMany, submittedValues);
599     }
600 
601 
602     /***
603      * Convenient utility method that returns the currently selected values of
604      * a UISelectMany component as a Set, of which the contains method can then be
605      * easily used to determine if a value is currently selected.
606      * Calling the contains method of this Set with the item value
607      * as argument returns true if this item is selected.
608      * @param uiSelectMany
609      * @return Set containing all currently selected values
610      */
611     public static Set getSelectedValuesAsSet(FacesContext context, UIComponent component, Converter converter, UISelectMany uiSelectMany)
612     {
613         Object selectedValues = uiSelectMany.getValue();
614 
615         return internalSubmittedOrSelectedValuesAsSet(context, component, converter, uiSelectMany, selectedValues);
616     }
617 
618 
619     /***
620      * Convenient utility method that returns the currently given value as String,
621      * using the given converter.
622      * Especially usefull for dealing with primitive types.
623      */
624     public static String getConvertedStringValue(FacesContext context,
625             UIComponent component, Converter converter, Object value) {
626         if (converter == null) {
627             if (value == null) {
628                 return "";
629             } else if (value instanceof String) {
630                 return (String) value;
631             } else if (RendererUtils.NOTHING.equals(value)) {
632                 return "";
633             } else {
634                 throw new IllegalArgumentException(
635                         "Value is no String (class=" + value.getClass().getName() + ", value=" + value + ") and component "
636                                 + component.getClientId(context) +"with path: "
637                                 + getPathToComponent(component)
638                                 + " does not have a Converter");
639             }
640         }
641 
642         if (RendererUtils.NOTHING.equals(value))
643         {
644             return converter.getAsString(context, component, "");
645         }
646         else
647         {
648             return converter.getAsString(context, component, value);
649         }
650     }
651 
652 
653     /***
654      * Convenient utility method that returns the currently given SelectItem value
655      * as String, using the given converter.
656      * Especially usefull for dealing with primitive types.
657      */
658     public static String getConvertedStringValue(FacesContext context,
659             UIComponent component, Converter converter, SelectItem selectItem) {
660         return getConvertedStringValue(context, component, converter, selectItem.getValue());
661     }
662 
663 
664     private static Set internalSubmittedOrSelectedValuesAsSet(FacesContext context,
665             UIComponent component, Converter converter, UISelectMany uiSelectMany,
666             Object values)
667     {
668         if (values == null || EMPTY_STRING.equals(values))
669         {
670             return Collections.EMPTY_SET;
671         }
672         else if (values instanceof Object[])
673         {
674             //Object array
675             Object[] ar = (Object[])values;
676             if (ar.length == 0)
677             {
678                 return Collections.EMPTY_SET;
679             }
680 
681             HashSet set = new HashSet(HashMapUtils.calcCapacity(ar.length));
682             for (int i = 0; i < ar.length; i++)
683             {
684                 set.add( getConvertedStringValue(context, component, converter, ar[i]) );
685             }
686             return set;
687         }
688         else if (values.getClass().isArray())
689         {
690             //primitive array
691             int len = Array.getLength(values);
692             HashSet set = new HashSet(org.apache.myfaces.shared.util.HashMapUtils.calcCapacity(len));
693             for (int i = 0; i < len; i++)
694             {
695                 set.add( getConvertedStringValue(context, component, converter, Array.get(values,i)) );
696             }
697             return set;
698         }
699         else if (values instanceof List)
700         {
701             List lst = (List)values;
702             if (lst.size() == 0)
703             {
704                 return Collections.EMPTY_SET;
705             }
706 
707             HashSet set = new HashSet(HashMapUtils.calcCapacity(lst.size()));
708             for (Iterator i = lst.iterator(); i.hasNext();)
709                 set.add(getConvertedStringValue(context, component, converter, i.next()));
710 
711             return set;
712 
713         }
714         else
715         {
716             throw new IllegalArgumentException("Value of UISelectMany component with path : "
717                     + getPathToComponent(uiSelectMany) + " is not of type Array or List");
718         }
719     }
720 
721     public static Object getConvertedUISelectOneValue(FacesContext facesContext, UISelectOne output, Object submittedValue){        
722         if (submittedValue != null && !(submittedValue instanceof String))
723         {
724             if (RendererUtils.NOTHING.equals(submittedValue))
725             {
726                 return null;
727             }
728             throw new IllegalArgumentException("Submitted value of type String for component : "
729                     + getPathToComponent(output) + "expected");
730         }
731         
732         //To be compatible with jsf ri, and according to issue 69
733         //[  Permit the passing of a null value to SelectItem.setValue()  ]
734         //If submittedValue == "" then convert to null.
735         if ((submittedValue != null) && (submittedValue instanceof String) && ("".equals(submittedValue)))
736         {
737             //Replace "" by null value
738             submittedValue = null;
739         }
740 
741         Converter converter;
742         try
743         {
744             converter = findUIOutputConverter(facesContext, output);
745         }
746         catch (FacesException e)
747         {
748             throw new ConverterException(e);
749         }
750 
751         return converter == null ? submittedValue : converter
752                 .getAsObject(facesContext, output, (String) submittedValue);
753     }
754     
755     public static Object getConvertedUIOutputValue(FacesContext facesContext, UIOutput output, Object submittedValue)
756             throws ConverterException
757     {
758         if (submittedValue != null && !(submittedValue instanceof String))
759         {
760             if (RendererUtils.NOTHING.equals(submittedValue))
761             {
762                 return null;
763             }
764             throw new IllegalArgumentException("Submitted value of type String for component : "
765                     + getPathToComponent(output) + "expected");
766         }
767 
768         Converter converter;
769         try
770         {
771             converter = findUIOutputConverter(facesContext, output);
772         }
773         catch (FacesException e)
774         {
775             throw new ConverterException(e);
776         }
777 
778         return converter == null ? submittedValue : converter
779                 .getAsObject(facesContext, output, (String) submittedValue);
780     }
781 
782     public static Object getConvertedUISelectManyValue(FacesContext facesContext, UISelectMany selectMany,
783             Object submittedValue) throws ConverterException
784     {
785         if (submittedValue == null)
786         {
787             return null;
788         }
789 
790         if (!(submittedValue instanceof String[]))
791         {
792             throw new ConverterException("Submitted value of type String[] for component : "
793                     + getPathToComponent(selectMany) + "expected");
794         }
795 
796         return org.apache.myfaces.shared.renderkit._SharedRendererUtils.getConvertedUISelectManyValue(facesContext,
797                 selectMany, (String[]) submittedValue);
798     }
799 
800     public static boolean getBooleanAttribute(UIComponent component, String attrName, boolean defaultValue)
801     {
802         Boolean b = (Boolean) component.getAttributes().get(attrName);
803         return b != null ? b.booleanValue() : defaultValue;
804     }
805 
806     public static int getIntegerAttribute(UIComponent component, String attrName, int defaultValue)
807     {
808         Integer i = (Integer) component.getAttributes().get(attrName);
809         return i != null ? i.intValue() : defaultValue;
810     }
811 
812     private static final String TRINIDAD_FORM_COMPONENT_FAMILY = "org.apache.myfaces.trinidad.Form";
813     private static final String ADF_FORM_COMPONENT_FAMILY = "oracle.adf.Form";
814 
815 
816     /***
817      * Find the enclosing form of a component
818      * in the view-tree.
819      * All Subclasses of <code>UIForm</code> and all known
820      * form-families are searched for.
821      * Currently those are the Trinidad form family,
822      * and the (old) ADF Faces form family.
823      * <p/>
824      * There might be additional form families
825      * which have to be explicitly entered here.
826      *
827      * @param uiComponent
828      * @param facesContext
829      * @return FormInfo Information about the form - the form itself and its name.
830      */
831     public static FormInfo findNestingForm(UIComponent uiComponent, FacesContext facesContext)
832     {
833         UIComponent parent = uiComponent.getParent();
834         while (parent != null && (!ADF_FORM_COMPONENT_FAMILY.equals(parent.getFamily()) &&
835             !TRINIDAD_FORM_COMPONENT_FAMILY.equals(parent.getFamily()) &&
836             !(parent instanceof UIForm)))
837         {
838             parent = parent.getParent();
839         }
840 
841         if (parent != null)
842         {
843             //link is nested inside a form
844             String formName = parent.getClientId(facesContext);
845             return new FormInfo(parent, formName);
846         }
847 
848         return null;
849     }
850 
851     public static boolean getBooleanValue(String attribute, Object value, boolean defaultValue) {
852         if (value instanceof Boolean) {
853             return ((Boolean) value).booleanValue();
854         }
855         else if (value instanceof String) {
856             return Boolean.valueOf((String) value).booleanValue();
857         }
858         else if (value != null) {
859             log.error("value for attribute " + attribute +
860                 " must be instanceof 'Boolean' or 'String', is of type : " + value.getClass());
861 
862             return defaultValue;
863         }
864 
865         return defaultValue;
866     }
867 
868     public static void copyHtmlInputTextAttributes(HtmlInputText src, HtmlInputText dest)
869     {
870         dest.setId(src.getId());
871         boolean forceId = getBooleanValue(
872             JSFAttr.FORCE_ID_ATTR,
873             src.getAttributes().get(JSFAttr.FORCE_ID_ATTR),
874             false);
875         if (forceId) {
876             dest.getAttributes().put(JSFAttr.FORCE_ID_ATTR, Boolean.TRUE);
877         }
878         dest.setImmediate(src.isImmediate());
879         dest.setTransient(src.isTransient());
880         dest.setAccesskey(src.getAccesskey());
881         dest.setAlt(src.getAlt());
882         dest.setConverter(src.getConverter());
883         dest.setDir(src.getDir());
884         dest.setDisabled(src.isDisabled());
885         dest.setLang(src.getLang());
886         dest.setLocalValueSet(src.isLocalValueSet());
887         dest.setMaxlength(src.getMaxlength());
888         dest.setOnblur(src.getOnblur());
889         dest.setOnchange(src.getOnchange());
890         dest.setOnclick(src.getOnclick());
891         dest.setOndblclick(src.getOndblclick());
892         dest.setOnfocus(src.getOnfocus());
893         dest.setOnkeydown(src.getOnkeydown());
894         dest.setOnkeypress(src.getOnkeypress());
895         dest.setOnkeyup(src.getOnkeyup());
896         dest.setOnmousedown(src.getOnmousedown());
897         dest.setOnmousemove(src.getOnmousemove());
898         dest.setOnmouseout(src.getOnmouseout());
899         dest.setOnmouseover(src.getOnmouseover());
900         dest.setOnmouseup(src.getOnmouseup());
901         dest.setOnselect(src.getOnselect());
902         dest.setReadonly(src.isReadonly());
903         dest.setRendered(src.isRendered());
904         dest.setRequired(src.isRequired());
905         dest.setSize(src.getSize());
906         dest.setStyle(src.getStyle());
907         dest.setStyleClass(src.getStyleClass());
908         dest.setTabindex(src.getTabindex());
909         dest.setTitle(src.getTitle());
910         dest.setValidator(src.getValidator());
911     }
912 
913     public static Integer getViewSequence(FacesContext facescontext)
914     {
915         Map map = facescontext.getExternalContext().getRequestMap();
916         Integer sequence = (Integer) map.get(SEQUENCE_PARAM);
917         if (sequence == null)
918         {
919             sequence = new Integer(1);
920             map.put(SEQUENCE_PARAM, sequence);
921 
922             synchronized (facescontext.getExternalContext().getSession(true))
923             {
924                 facescontext.getExternalContext().getSessionMap().put(RendererUtils.SEQUENCE_PARAM, sequence);
925             }
926         }
927         return sequence;
928     }
929 
930     public static UIComponent findComponent(UIComponent headerComp, Class clazz)
931     {
932         if (clazz.isAssignableFrom(headerComp.getClass()))
933         {
934             return headerComp;
935         }
936 
937         List li = headerComp.getChildren();
938 
939         for (int i = 0; i < li.size(); i++)
940         {
941             UIComponent comp = (UIComponent) li.get(i);
942 
943             //recursively iterate through children to find the component
944             UIComponent lookupComp = findComponent(comp, clazz);
945 
946             if (lookupComp != null)
947                 return lookupComp;
948         }
949 
950         return null;
951     }
952 
953     public static void addOrReplaceChild(UIInput component, UIComponent child)
954     {
955         List li = component.getChildren();
956 
957         for (int i = 0; i < li.size(); i++)
958         {
959             UIComponent oldChild = (UIComponent) li.get(i);
960 
961             if (oldChild.getId() != null && oldChild.getId().equals(child.getId()))
962             {
963                 li.set(i, child);
964                 return;
965             }
966         }
967 
968         component.getChildren().add(child);
969     }
970 
971     public static String getClientId(FacesContext facesContext, UIComponent uiComponent, String forAttr)
972     {
973         UIComponent forComponent = uiComponent.findComponent(forAttr);
974         if (forComponent == null)
975         {
976             if (log.isInfoEnabled())
977             {
978                 log
979                         .info("Unable to find component '"
980                                 + forAttr
981                                 + "' (calling findComponent on component '"
982                                 + uiComponent.getClientId(facesContext)
983                                 + "')."
984                                 + " We'll try to return a guessed client-id anyways -"
985                                 + " this will be a problem if you put the referenced component"
986                                 + " into a different naming-container. If this is the case, you can always use the full client-id.");
987             }
988             if (forAttr.length() > 0 && forAttr.charAt(0) == UINamingContainer.SEPARATOR_CHAR)
989             {
990                 //absolute id path
991                 return forAttr.substring(1);
992             }
993 
994             //relative id path, we assume a component on the same level as the label component
995             String labelClientId = uiComponent.getClientId(facesContext);
996             int colon = labelClientId.lastIndexOf(UINamingContainer.SEPARATOR_CHAR);
997 
998             return colon == -1 ? forAttr : labelClientId.substring(0, colon + 1) + forAttr;
999 
1000         }
1001 
1002         return forComponent.getClientId(facesContext);
1003 
1004     }
1005 
1006     public static List convertIdsToClientIds(String actionFor, FacesContext facesContext, UIComponent component)
1007     {
1008         List li = new ArrayList();
1009 
1010         String[] ids = actionFor.split(",");
1011 
1012         for (int i = 0; i < ids.length; i++)
1013         {
1014             String trimedId = ids[i].trim();
1015             if (trimedId.equals("none"))
1016                 li.add(trimedId);
1017             else
1018                 li.add(RendererUtils.getClientId(facesContext, component, trimedId));
1019         }
1020         return li;
1021     }
1022 
1023     public static List convertPhasesToPhasesIds(String actionForPhase)
1024     {
1025         List li = new ArrayList();
1026 
1027         if (actionForPhase == null)
1028         {
1029             return li;
1030         }
1031 
1032         String[] ids = actionForPhase.split(",");
1033 
1034         for (int i = 0; i < ids.length; i++)
1035         {
1036             if (ids[i].equals("PROCESS_VALIDATIONS"))
1037             {
1038                 li.add(PhaseId.PROCESS_VALIDATIONS);
1039             }
1040             else if (ids[i].equals("UPDATE_MODEL_VALUES"))
1041             {
1042                 li.add(PhaseId.UPDATE_MODEL_VALUES);
1043             }
1044         }
1045         return li;
1046     }
1047 
1048     /***
1049      * Helper method which loads a resource file (such as css) by a given context path and a file name.
1050      * Useful to provide css files (or js files) inline.
1051      * 
1052      * @param ctx <code>FacesContext</code> object to calculate the context path of the web application.
1053      * @param file name of the resource file (e.g. <code>foo.css</code>).
1054      * @return the content of the resource file, or <code>null</code> if no such file is available.
1055      */
1056     public static String loadResourceFile(FacesContext ctx, String file)
1057     {
1058 
1059         ByteArrayOutputStream content = new ByteArrayOutputStream(10240);
1060 
1061         InputStream in = null;
1062         try {
1063             in = ctx.getExternalContext().getResourceAsStream(file);
1064             if (in == null)
1065             {
1066                 return null;
1067             }
1068 
1069             byte[] fileBuffer = new byte[10240];
1070             int read;
1071             while ((read = in.read(fileBuffer)) > -1)
1072             {
1073                 content.write(fileBuffer, 0, read);
1074             }
1075         }
1076         catch (FileNotFoundException e) {
1077             if (log.isWarnEnabled())
1078                 log.warn("no such file " + file, e);
1079             content = null;
1080         }
1081         catch (IOException e) {
1082             if (log.isWarnEnabled())
1083                 log.warn("problems during processing resource " + file, e);
1084             content = null;
1085         }
1086         finally
1087         {
1088             try
1089             {
1090                 content.close();
1091             }
1092             catch (IOException e)
1093             {
1094                 log.warn(e.getLocalizedMessage(), e);
1095             }
1096             if (in != null)
1097             {
1098                 try
1099                 {
1100                     in.close();
1101                 }
1102                 catch (IOException e)
1103                 {
1104                     log.warn(e.getLocalizedMessage(), e);
1105                 }
1106             }
1107         }
1108 
1109         return content.toString();
1110     }
1111 
1112     /***
1113      * check for partial validation or model update attributes being set
1114      * and initialize the request-map accordingly.
1115      * SubForms will work with this information.
1116      */
1117     public static void initPartialValidationAndModelUpdate(UIComponent component, FacesContext facesContext)
1118     {
1119         String actionFor = (String) component.getAttributes().get("actionFor");
1120 
1121         if (actionFor != null)
1122         {
1123             List li = convertIdsToClientIds(actionFor, facesContext, component);
1124 
1125             facesContext.getExternalContext().getRequestMap().put(ACTION_FOR_LIST, li);
1126 
1127             String actionForPhase = (String) component.getAttributes().get("actionForPhase");
1128 
1129             if (actionForPhase != null)
1130             {
1131                 List phaseList = convertPhasesToPhasesIds(actionForPhase);
1132 
1133                 facesContext.getExternalContext().getRequestMap().put(ACTION_FOR_PHASE_LIST, phaseList);
1134             }
1135         }
1136     }
1137 
1138     public static boolean isAdfOrTrinidadForm(UIComponent component) {
1139         if (component == null)
1140             return false;
1141         return ADF_FORM_COMPONENT_FAMILY.equals(component.getFamily()) ||
1142             TRINIDAD_FORM_COMPONENT_FAMILY.equals(component.getFamily());
1143     }
1144 
1145 
1146     /***
1147      * Gets the ResponseStateManager for the renderKit Id provided
1148      */
1149     public static ResponseStateManager getResponseStateManager(FacesContext facesContext, String renderKitId)
1150             throws FacesException
1151     {
1152         RenderKit renderKit = facesContext.getRenderKit();
1153 
1154         if (renderKit == null)
1155         {
1156             // look for the renderkit in the request
1157             Map requestMap = facesContext.getExternalContext().getRequestMap();
1158             RenderKitFactory factory = (RenderKitFactory) requestMap.get(RENDER_KIT_IMPL);
1159 
1160             if (factory != null)
1161             {
1162                 renderKit = factory.getRenderKit(facesContext, renderKitId);
1163             }
1164             else
1165             {
1166                 factory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
1167 
1168                 if (factory == null)
1169                 {
1170                     throw new IllegalStateException("Factory is null");
1171                 }
1172 
1173                 requestMap.put(RENDER_KIT_IMPL, factory);
1174 
1175                 renderKit = factory.getRenderKit(facesContext, renderKitId);
1176             }
1177         }
1178         
1179         if (renderKit == null)
1180         {
1181             throw new IllegalArgumentException("Could not find a RenderKit for \"" + renderKitId + "\""); 
1182         }
1183         
1184         return renderKit.getResponseStateManager();
1185     }
1186 
1187     /***
1188      * Special converter for handling submitted values which don't need to be converted.
1189      *
1190      * @author mathias (latest modification by $Author: matzew $)
1191      * @version $Revision: 807542 $ $Date: 2009-08-25 05:22:20 -0500 (Tue, 25 Aug 2009) $
1192      */
1193     private static class PassThroughAsStringConverter implements Converter
1194     {
1195         private final Converter converter;
1196 
1197         public PassThroughAsStringConverter(Converter converter)
1198         {
1199             this.converter = converter;
1200         }
1201 
1202         public Object getAsObject(FacesContext context, UIComponent component,
1203                 String value) throws ConverterException
1204         {
1205             return converter.getAsObject(context, component, value);
1206         }
1207 
1208         public String getAsString(FacesContext context, UIComponent component,
1209                 Object value) throws ConverterException
1210         {
1211             return (String)value;
1212         }
1213 
1214     }
1215 }