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.Collection;
29  import java.util.Collections;
30  import java.util.Date;
31  import java.util.HashSet;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Set;
36  import java.util.logging.Level;
37  import java.util.logging.Logger;
38  
39  import javax.el.ValueExpression;
40  import javax.faces.FacesException;
41  import javax.faces.FactoryFinder;
42  import javax.faces.application.FacesMessage;
43  import javax.faces.application.ProjectStage;
44  import javax.faces.application.Resource;
45  import javax.faces.application.ResourceHandler;
46  import javax.faces.component.EditableValueHolder;
47  import javax.faces.component.NamingContainer;
48  import javax.faces.component.UIComponent;
49  import javax.faces.component.UIForm;
50  import javax.faces.component.UIInput;
51  import javax.faces.component.UINamingContainer;
52  import javax.faces.component.UIOutput;
53  import javax.faces.component.UISelectMany;
54  import javax.faces.component.UISelectOne;
55  import javax.faces.component.UIViewRoot;
56  import javax.faces.component.ValueHolder;
57  import javax.faces.component.html.HtmlInputText;
58  import javax.faces.context.FacesContext;
59  import javax.faces.convert.Converter;
60  import javax.faces.convert.ConverterException;
61  import javax.faces.el.PropertyNotFoundException;
62  import javax.faces.el.ValueBinding;
63  import javax.faces.event.PhaseId;
64  import javax.faces.model.SelectItem;
65  import javax.faces.render.RenderKit;
66  import javax.faces.render.RenderKitFactory;
67  import javax.faces.render.ResponseStateManager;
68  
69  import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
70  import org.apache.myfaces.shared.util.HashMapUtils;
71  import org.apache.myfaces.shared.util.SelectItemsIterator;
72  
73  /**
74   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
75   * @version $Revision: 1341542 $ $Date: 2012-05-22 10:47:26 -0500 (Tue, 22 May 2012) $
76   */
77  public final class RendererUtils
78  {
79      private RendererUtils()
80      {
81          //nope
82      }
83  
84      //private static final Log log = LogFactory.getLog(RendererUtils.class);
85      private static final Logger log = Logger.getLogger(RendererUtils.class
86              .getName());
87  
88      public static final String SELECT_ITEM_LIST_ATTR = RendererUtils.class
89              .getName() + ".LIST";
90      public static final String EMPTY_STRING = "";
91      //This constant is no longer used by UISelectOne/UISelectMany instances
92      public static final Object NOTHING = new Serializable()
93      {
94          public boolean equals(final Object o)
95          {
96              if (o != null)
97              {
98                  if (o.getClass().equals(this.getClass()))
99                  {
100                     return true;
101                 }
102             }
103             return false;
104         }
105 
106         @Override
107         public int hashCode()
108         {
109             return super.hashCode();
110         }
111     };
112 
113     public static final String ACTION_FOR_LIST = "org.apache.myfaces.ActionForList";
114     public static final String ACTION_FOR_PHASE_LIST = "org.apache.myfaces.ActionForPhaseList";
115 
116     public static final String SEQUENCE_PARAM = "jsf_sequence";
117 
118     private static final String RENDER_KIT_IMPL = RendererUtils.class.getName()
119             + ".RenderKitImpl";
120 
121     // This nice constant is "specified" 13.1.1.2 The Resource API Approach in Spec as an example
122     public static final String RES_NOT_FOUND = "RES_NOT_FOUND";
123 
124     public static String getPathToComponent(UIComponent component)
125     {
126         StringBuilder buf = new StringBuilder();
127 
128         if (component == null)
129         {
130             buf.append("{Component-Path : ");
131             buf.append("[null]}");
132             return buf.toString();
133         }
134 
135         getPathToComponent(component, buf);
136 
137         buf.insert(0, "{Component-Path : ");
138         Object location = component.getAttributes().get(
139                 UIComponent.VIEW_LOCATION_KEY);
140         if (location != null)
141         {
142             buf.append(" Location: ").append(location);
143         }
144         buf.append("}");
145 
146         return buf.toString();
147     }
148 
149     private static void getPathToComponent(UIComponent component,
150             StringBuilder buf)
151     {
152         if (component == null)
153         {
154             return;
155         }
156 
157         StringBuilder intBuf = new StringBuilder();
158 
159         intBuf.append("[Class: ");
160         intBuf.append(component.getClass().getName());
161         if (component instanceof UIViewRoot)
162         {
163             intBuf.append(",ViewId: ");
164             intBuf.append(((UIViewRoot) component).getViewId());
165         }
166         else
167         {
168             intBuf.append(",Id: ");
169             intBuf.append(component.getId());
170         }
171         intBuf.append("]");
172 
173         buf.insert(0, intBuf.toString());
174 
175         getPathToComponent(component.getParent(), buf);
176     }
177 
178     public static String getConcatenatedId(FacesContext context,
179             UIComponent container, String clientId)
180     {
181         UIComponent child = container.findComponent(clientId);
182 
183         if (child == null)
184         {
185             return clientId;
186         }
187 
188         return getConcatenatedId(context, child);
189     }
190 
191     public static String getConcatenatedId(FacesContext context,
192             UIComponent component)
193     {
194         if (context == null)
195         {
196             throw new NullPointerException("context");
197         }
198 
199         StringBuilder idBuf = new StringBuilder();
200 
201         idBuf.append(component.getId());
202 
203         UIComponent parent;
204 
205         while ((parent = component.getParent()) != null)
206         {
207             if (parent instanceof NamingContainer)
208             {
209                 idBuf.insert(0, UINamingContainer.getSeparatorChar(context));
210                 idBuf.insert(0, parent.getId());
211             }
212         }
213 
214         return idBuf.toString();
215     }
216 
217     public static Boolean getBooleanValue(UIComponent component)
218     {
219         Object value = getObjectValue(component);
220         // Try to convert to Boolean if it is a String
221         if (value instanceof String)
222         {
223             value = Boolean.valueOf((String) value);
224         }
225 
226         if (value == null || value instanceof Boolean)
227         {
228             return (Boolean) value;
229         }
230 
231         throw new IllegalArgumentException(
232                 "Expected submitted value of type Boolean for Component : "
233                         + getPathToComponent(component));
234 
235     }
236 
237     public static Date getDateValue(UIComponent component)
238     {
239         Object value = getObjectValue(component);
240         if (value == null || value instanceof Date)
241         {
242             return (Date) value;
243         }
244 
245         throw new IllegalArgumentException(
246                 "Expected submitted value of type Date for component : "
247                         + getPathToComponent(component));
248     }
249 
250     public static Object getObjectValue(UIComponent component)
251     {
252         if (!(component instanceof ValueHolder))
253         {
254             throw new IllegalArgumentException("Component : "
255                     + getPathToComponent(component) + "is not a ValueHolder");
256         }
257 
258         if (component instanceof EditableValueHolder)
259         {
260             Object value = ((EditableValueHolder) component)
261                     .getSubmittedValue();
262             if (value != null)
263             {
264                 return value;
265             }
266         }
267 
268         return ((ValueHolder) component).getValue();
269     }
270 
271     @Deprecated
272     public static String getStringValue(FacesContext context, ValueBinding vb)
273     {
274         Object value = vb.getValue(context);
275         if (value != null)
276         {
277             return value.toString();
278         }
279         return null;
280     }
281 
282     public static String getStringValue(FacesContext context, ValueExpression ve)
283     {
284         Object value = ve.getValue(context.getELContext());
285         if (value != null)
286         {
287             return value.toString();
288         }
289         return null;
290     }
291 
292     public static String getStringValue(FacesContext facesContext,
293             UIComponent component)
294     {
295         if (!(component instanceof ValueHolder))
296         {
297             throw new IllegalArgumentException("Component : "
298                     + getPathToComponent(component)
299                     + "is not a ValueHolder");
300         }
301 
302         if (component instanceof EditableValueHolder)
303         {
304             Object submittedValue = ((EditableValueHolder) component)
305                     .getSubmittedValue();
306             if (submittedValue != null)
307             {
308                 if (log.isLoggable(Level.FINE))
309                 {
310                     log.fine("returning 1 '" + submittedValue + "'");
311                 }
312                 return submittedValue.toString();
313             }
314         }
315 
316         Object value;
317 
318         if (component instanceof EditableValueHolder)
319         {
320 
321             EditableValueHolder holder = (EditableValueHolder) component;
322 
323             if (holder.isLocalValueSet())
324             {
325                 value = holder.getLocalValue();
326             }
327             else
328             {
329                 value = getValue(component);
330             }
331         }
332         else
333         {
334             value = getValue(component);
335         }
336 
337         Converter converter = ((ValueHolder) component).getConverter();
338         if (converter == null && value != null)
339         {
340 
341             try
342             {
343                 converter = facesContext.getApplication().createConverter(
344                         value.getClass());
345                 if (log.isLoggable(Level.FINE))
346                 {
347                     log.fine("the created converter is " + converter);
348                 }
349             }
350             catch (FacesException e)
351             {
352                 log.log(Level.SEVERE, "No converter for class "
353                         + value.getClass().getName()
354                         + " found (component id=" + component.getId()
355                         + ").", e);
356                 // converter stays null
357             }
358         }
359 
360         if (converter == null)
361         {
362             if (value == null)
363             {
364                 if (log.isLoggable(Level.FINE))
365                 {
366                     log.fine("returning an empty string");
367                 }
368                 return "";
369             }
370 
371             if (log.isLoggable(Level.FINE))
372             {
373                 log.fine("returning an .toString");
374             }
375             return value.toString();
376 
377         }
378 
379         if (log.isLoggable(Level.FINE))
380         {
381             log.fine("returning converter get as string " + converter);
382         }
383         return converter.getAsString(facesContext, component, value);
384     }
385 
386     public static String getStringFromSubmittedValueOrLocalValueReturnNull(
387             FacesContext facesContext, UIComponent component)
388     {
389         try
390         {
391             if (!(component instanceof ValueHolder))
392             {
393                 throw new IllegalArgumentException("Component : "
394                         + getPathToComponent(component)
395                         + "is not a ValueHolder");
396             }
397 
398             if (component instanceof EditableValueHolder)
399             {
400                 Object submittedValue = ((EditableValueHolder) component)
401                         .getSubmittedValue();
402                 if (submittedValue != null)
403                 {
404                     if (log.isLoggable(Level.FINE))
405                     {
406                         log.fine("returning 1 '" + submittedValue + "'");
407                     }
408                     return submittedValue.toString();
409                 }
410             }
411 
412             Object value;
413 
414             if (component instanceof EditableValueHolder)
415             {
416 
417                 EditableValueHolder holder = (EditableValueHolder) component;
418 
419                 if (holder.isLocalValueSet())
420                 {
421                     value = holder.getLocalValue();
422                 }
423                 else
424                 {
425                     value = getValue(component);
426                 }
427             }
428             else
429             {
430                 value = getValue(component);
431             }
432 
433             Converter converter = ((ValueHolder) component).getConverter();
434             if (converter == null && value != null)
435             {
436 
437                 try
438                 {
439                     converter = facesContext.getApplication().createConverter(
440                             value.getClass());
441                     if (log.isLoggable(Level.FINE))
442                     {
443                         log.fine("the created converter is " + converter);
444                     }
445                 }
446                 catch (FacesException e)
447                 {
448                     log.log(Level.SEVERE, "No converter for class "
449                             + value.getClass().getName()
450                             + " found (component id=" + component.getId()
451                             + ").", e);
452                     // converter stays null
453                 }
454             }
455 
456             if (converter == null)
457             {
458                 if (value == null)
459                 {
460                     //if (log.isLoggable(Level.FINE))
461                     //    log.fine("returning an empty string");
462                     return null;
463                 }
464 
465                 if (log.isLoggable(Level.FINE))
466                 {
467                     log.fine("returning an .toString");
468                 }
469                 return value.toString();
470 
471             }
472 
473             if (log.isLoggable(Level.FINE))
474             {
475                 log.fine("returning converter get as string " + converter);
476             }
477             return converter.getAsString(facesContext, component, value);
478 
479         }
480         catch (PropertyNotFoundException ex)
481         {
482             log.log(Level.SEVERE, "Property not found - called by component : "
483                     + getPathToComponent(component), ex);
484 
485             throw ex;
486         }
487     }
488 
489     private static Object getValue(UIComponent component)
490     {
491         Object value = ((ValueHolder) component).getValue();
492         return value;
493     }
494 
495     /**
496      * See JSF Spec. 8.5 Table 8-1
497      * @param value
498      * @return boolean
499      */
500     public static boolean isDefaultAttributeValue(Object value)
501     {
502         if (value == null)
503         {
504             return true;
505         }
506         else if (value instanceof Boolean)
507         {
508             return !((Boolean) value).booleanValue();
509         }
510         else if (value instanceof Number)
511         {
512             if (value instanceof Integer)
513             {
514                 return ((Number) value).intValue() == Integer.MIN_VALUE;
515             }
516             else if (value instanceof Double)
517             {
518                 return ((Number) value).doubleValue() == Double.MIN_VALUE;
519             }
520             else if (value instanceof Long)
521             {
522                 return ((Number) value).longValue() == Long.MIN_VALUE;
523             }
524             else if (value instanceof Byte)
525             {
526                 return ((Number) value).byteValue() == Byte.MIN_VALUE;
527             }
528             else if (value instanceof Float)
529             {
530                 return ((Number) value).floatValue() == Float.MIN_VALUE;
531             }
532             else if (value instanceof Short)
533             {
534                 return ((Number) value).shortValue() == Short.MIN_VALUE;
535             }
536         }
537         return false;
538     }
539 
540     /**
541      * Find the proper Converter for the given UIOutput component.
542      * @return the Converter or null if no Converter specified or needed
543      * @throws FacesException if the Converter could not be created
544      */
545     public static Converter findUIOutputConverter(FacesContext facesContext,
546             UIOutput component) throws FacesException
547     {
548         return _SharedRendererUtils.findUIOutputConverter(facesContext,
549                 component);
550     }
551 
552     /**
553      * Calls findUISelectManyConverter with considerValueType = false.
554      * @param facesContext
555      * @param component
556      * @return
557      */
558     public static Converter findUISelectManyConverter(
559             FacesContext facesContext, UISelectMany component)
560     {
561         return findUISelectManyConverter(facesContext, component, false);
562     }
563 
564     /**
565      * Find proper Converter for the entries in the associated Collection or array of
566      * the given UISelectMany as specified in API Doc of UISelectMany.
567      * If considerValueType is true, the valueType attribute will be used
568      * in addition to the standard algorithm to get a valid converter.
569      * 
570      * @return the Converter or null if no Converter specified or needed
571      * @throws FacesException if the Converter could not be created
572      */
573     public static Converter findUISelectManyConverter(
574             FacesContext facesContext, UISelectMany component,
575             boolean considerValueType)
576     {
577         // If the component has an attached Converter, use it.
578         Converter converter = component.getConverter();
579         if (converter != null)
580         {
581             return converter;
582         }
583 
584         if (considerValueType)
585         {
586             // try to get a converter from the valueType attribute
587             converter = _SharedRendererUtils.getValueTypeConverter(
588                     facesContext, component);
589             if (converter != null)
590             {
591                 return converter;
592             }
593         }
594 
595         //Try to find out by value expression
596         ValueExpression ve = component.getValueExpression("value");
597         if (ve == null)
598         {
599             return null;
600         }
601 
602         // Try to get the type from the actual value or,
603         // if value == null, obtain the type from the ValueExpression
604         Class<?> valueType = null;
605         Object value = ve.getValue(facesContext.getELContext());
606         valueType = (value != null) ? value.getClass() : ve
607                 .getType(facesContext.getELContext());
608 
609         if (valueType == null)
610         {
611             return null;
612         }
613 
614         // a valueType of Object is also permitted, in order to support
615         // managed bean properties of type Object that resolve to null at this point
616         if (Collection.class.isAssignableFrom(valueType)
617                 || Object.class.equals(valueType))
618         {
619             // try to get the by-type-converter from the type of the SelectItems
620             return _SharedRendererUtils.getSelectItemsValueConverter(
621                     new SelectItemsIterator(component, facesContext),
622                     facesContext);
623         }
624 
625         if (!valueType.isArray())
626         {
627             throw new IllegalArgumentException(
628                     "ValueExpression for UISelectMany : "
629                             + getPathToComponent(component)
630                             + " must be of type Collection or Array");
631         }
632 
633         Class<?> arrayComponentType = valueType.getComponentType();
634         if (String.class.equals(arrayComponentType))
635         {
636             return null; //No converter needed for String type
637         }
638 
639         if (Object.class.equals(arrayComponentType))
640         {
641             // There is no converter for Object class
642             // try to get the by-type-converter from the type of the SelectItems
643             return _SharedRendererUtils.getSelectItemsValueConverter(
644                     new SelectItemsIterator(component, facesContext),
645                     facesContext);
646         }
647 
648         try
649         {
650             return facesContext.getApplication().createConverter(
651                     arrayComponentType);
652         }
653         catch (FacesException e)
654         {
655             log.log(Level.SEVERE,
656                     "No Converter for type " + arrayComponentType.getName()
657                             + " found", e);
658             return null;
659         }
660     }
661 
662     public static void checkParamValidity(FacesContext facesContext,
663             UIComponent uiComponent, Class compClass)
664     {
665         if (facesContext == null)
666         {
667             throw new NullPointerException("facesContext may not be null");
668         }
669         if (uiComponent == null)
670         {
671             throw new NullPointerException("uiComponent may not be null");
672         }
673 
674         //if (compClass != null && !(compClass.isAssignableFrom(uiComponent.getClass())))
675         // why isAssignableFrom with additional getClass method call if isInstance does the same?
676         if (compClass != null && !(compClass.isInstance(uiComponent)))
677         {
678             throw new IllegalArgumentException("uiComponent : "
679                     + getPathToComponent(uiComponent) + " is not instance of "
680                     + compClass.getName() + " as it should be");
681         }
682     }
683 
684     public static void renderChildren(FacesContext facesContext,
685             UIComponent component) throws IOException
686     {
687         if (component.getChildCount() > 0)
688         {
689             for (int i = 0; i < component.getChildCount(); i++)
690             {
691                 UIComponent child = component.getChildren().get(i);
692                 //renderChild(facesContext, child);
693                 child.encodeAll(facesContext);
694             }
695         }
696     }
697 
698     /**
699      * 
700      * @param facesContext
701      * @param child
702      * @throws IOException
703      * @deprecated use UIComponent.encodeAll() instead
704      */
705     @Deprecated
706     public static void renderChild(FacesContext facesContext, UIComponent child)
707             throws IOException
708     {
709         // The next isRendered() call is only shortcut:
710         // methods encodeBegin, encodeChildren and encodeEnd should proceed only if 
711         // "If our rendered property is true, render the (beginning, child, ending) of this component"
712         if (!isRendered(facesContext, child))
713         {
714             return;
715         }
716 
717         child.encodeBegin(facesContext);
718         if (child.getRendersChildren())
719         {
720             child.encodeChildren(facesContext);
721         }
722         else
723         {
724             renderChildren(facesContext, child);
725         }
726         child.encodeEnd(facesContext);
727     }
728 
729     /**
730      * Call {@link #pushComponentToEL(javax.faces.context.FacesContext,javax.faces.component.UIComponent)}, 
731      * reads the isRendered property, call {@link
732      * UIComponent#popComponentFromEL} and returns the value of isRendered.
733      */
734     public static boolean isRendered(FacesContext facesContext,
735             UIComponent uiComponent)
736     {
737         // We must call pushComponentToEL here because ValueExpression may have 
738         // implicit object "component" used. 
739         try
740         {
741             uiComponent.pushComponentToEL(facesContext, uiComponent);
742             return uiComponent.isRendered();
743         }
744         finally
745         {
746             uiComponent.popComponentFromEL(facesContext);
747         }
748     }
749 
750     public static List getSelectItemList(UISelectOne uiSelectOne)
751     {
752         return internalGetSelectItemList(uiSelectOne,
753                 FacesContext.getCurrentInstance());
754     }
755 
756     /**
757      * @param uiSelectOne
758      * @param facesContext
759      * @return List of SelectItem Objects
760      */
761     public static List getSelectItemList(UISelectOne uiSelectOne,
762             FacesContext facesContext)
763     {
764         return internalGetSelectItemList(uiSelectOne, facesContext);
765     }
766 
767     public static List getSelectItemList(UISelectMany uiSelectMany)
768     {
769         return internalGetSelectItemList(uiSelectMany,
770                 FacesContext.getCurrentInstance());
771     }
772 
773     /**
774      * @param uiSelectMany
775      * @param facesContext
776      * @return List of SelectItem Objects
777      */
778     public static List getSelectItemList(UISelectMany uiSelectMany,
779             FacesContext facesContext)
780     {
781         return internalGetSelectItemList(uiSelectMany, facesContext);
782     }
783 
784     private static List internalGetSelectItemList(UIComponent uiComponent,
785             FacesContext facesContext)
786     {
787         /* TODO: Shall we cache the list in a component attribute?
788         ArrayList list = (ArrayList)uiComponent.getAttributes().get(SELECT_ITEM_LIST_ATTR);
789         if (list != null)
790         {
791             return list;
792         }
793          */
794 
795         List list = new ArrayList();
796 
797         for (Iterator iter = new SelectItemsIterator(uiComponent, facesContext); iter
798                 .hasNext();)
799         {
800             list.add(iter.next());
801         }
802         return list;
803     }
804 
805     /**
806      * Convenient utility method that returns the currently submitted values of
807      * a UISelectMany component as a Set, of which the contains method can then be
808      * easily used to determine if a select item is currently selected.
809      * Calling the contains method of this Set with the renderable (String converted) item value
810      * as argument returns true if this item is selected.
811      * @param uiSelectMany
812      * @return Set containing all currently selected values
813      */
814     public static Set getSubmittedValuesAsSet(FacesContext context,
815             UIComponent component, Converter converter,
816             UISelectMany uiSelectMany)
817     {
818         Object submittedValues = uiSelectMany.getSubmittedValue();
819         if (submittedValues == null)
820         {
821             return null;
822         }
823 
824         if (converter != null)
825         {
826             converter = new PassThroughAsStringConverter(converter);
827         }
828 
829         return internalSubmittedOrSelectedValuesAsSet(context, component,
830                 converter, uiSelectMany, submittedValues, false);
831     }
832 
833     /**
834      * Convenient utility method that returns the currently selected values of
835      * a UISelectMany component as a Set, of which the contains method can then be
836      * easily used to determine if a value is currently selected.
837      * Calling the contains method of this Set with the item value
838      * as argument returns true if this item is selected.
839      * @param uiSelectMany
840      * @return Set containing all currently selected values
841      */
842     public static Set getSelectedValuesAsSet(FacesContext context,
843             UIComponent component, Converter converter,
844             UISelectMany uiSelectMany)
845     {
846         Object selectedValues = uiSelectMany.getValue();
847 
848         return internalSubmittedOrSelectedValuesAsSet(context, component,
849                 converter, uiSelectMany, selectedValues, true);
850     }
851 
852     /**
853      * Convenient utility method that returns the currently given value as String,
854      * using the given converter.
855      * Especially usefull for dealing with primitive types.
856      */
857     public static String getConvertedStringValue(FacesContext context,
858             UIComponent component, Converter converter, Object value)
859     {
860         if (converter == null)
861         {
862             if (value == null)
863             {
864                 return "";
865             }
866             else if (value instanceof String)
867             {
868                 return (String) value;
869             }
870             else
871             {
872                 return value.toString();
873             }
874         }
875 
876         return converter.getAsString(context, component, value);
877     }
878 
879     /**
880      * Convenient utility method that returns the currently given SelectItem value
881      * as String, using the given converter.
882      * Especially usefull for dealing with primitive types.
883      */
884     public static String getConvertedStringValue(FacesContext context,
885             UIComponent component, Converter converter, SelectItem selectItem)
886     {
887         return getConvertedStringValue(context, component, converter,
888                 selectItem.getValue());
889     }
890 
891     private static Set internalSubmittedOrSelectedValuesAsSet(
892             FacesContext context, UIComponent component, Converter converter,
893             UISelectMany uiSelectMany, Object values,
894             boolean allowNonArrayOrCollectionValue)
895     {
896         if (values == null || EMPTY_STRING.equals(values))
897         {
898             return Collections.EMPTY_SET;
899         }
900         else if (values instanceof Object[])
901         {
902             //Object array
903             Object[] ar = (Object[]) values;
904             if (ar.length == 0)
905             {
906                 return Collections.EMPTY_SET;
907             }
908 
909             HashSet set = new HashSet(HashMapUtils.calcCapacity(ar.length));
910             for (int i = 0; i < ar.length; i++)
911             {
912                 set.add(getConvertedStringValue(context, component, converter,
913                         ar[i]));
914             }
915             return set;
916         }
917         else if (values.getClass().isArray())
918         {
919             //primitive array
920             int len = Array.getLength(values);
921             HashSet set = new HashSet(
922                     org.apache.myfaces.shared.util.HashMapUtils
923                             .calcCapacity(len));
924             for (int i = 0; i < len; i++)
925             {
926                 set.add(getConvertedStringValue(context, component, converter,
927                         Array.get(values, i)));
928             }
929             return set;
930         }
931         else if (values instanceof Collection)
932         {
933             Collection col = (Collection) values;
934             if (col.size() == 0)
935             {
936                 return Collections.EMPTY_SET;
937             }
938 
939             HashSet set = new HashSet(HashMapUtils.calcCapacity(col.size()));
940             for (Iterator i = col.iterator(); i.hasNext();)
941             {
942                 set.add(getConvertedStringValue(context, component, converter,
943                         i.next()));
944             }
945 
946             return set;
947 
948         }
949         else if (allowNonArrayOrCollectionValue)
950         {
951             HashSet set = new HashSet(HashMapUtils.calcCapacity(1));
952             set.add(values);
953             return set;
954         }
955         else
956         {
957             throw new IllegalArgumentException(
958                     "Value of UISelectMany component with path : "
959                             + getPathToComponent(uiSelectMany)
960                             + " is not of type Array or List");
961         }
962     }
963 
964     public static Object getConvertedUISelectOneValue(
965             FacesContext facesContext, UISelectOne output, Object submittedValue)
966     {
967         if (submittedValue != null && !(submittedValue instanceof String))
968         {
969             throw new IllegalArgumentException(
970                     "Submitted value of type String for component : "
971                             + getPathToComponent(output) + "expected");
972         }
973 
974         //To be compatible with jsf ri, and according to issue 69
975         //[  Permit the passing of a null value to SelectItem.setValue()  ]
976         //If submittedValue == "" then convert to null.
977         if ((submittedValue != null) && (submittedValue instanceof String)
978                 && ("".equals(submittedValue)))
979         {
980             //Replace "" by null value
981             submittedValue = null;
982         }
983 
984         Converter converter;
985         try
986         {
987             converter = findUIOutputConverter(facesContext, output);
988         }
989         catch (FacesException e)
990         {
991             throw new ConverterException(e);
992         }
993 
994         return converter == null ? submittedValue : converter.getAsObject(
995                 facesContext, output, (String) submittedValue);
996     }
997 
998     public static Object getConvertedUIOutputValue(FacesContext facesContext,
999             UIOutput output, Object submittedValue) throws ConverterException
1000     {
1001         if (submittedValue != null && !(submittedValue instanceof String))
1002         {
1003             submittedValue = submittedValue.toString();
1004         }
1005 
1006         Converter converter;
1007         try
1008         {
1009             converter = findUIOutputConverter(facesContext, output);
1010         }
1011         catch (FacesException e)
1012         {
1013             throw new ConverterException(e);
1014         }
1015 
1016         return converter == null ? submittedValue : converter.getAsObject(
1017                 facesContext, output, (String) submittedValue);
1018     }
1019 
1020     /**
1021      * Invokes getConvertedUISelectManyValue() with considerValueType = false, thus
1022      * implementing the standard behavior of the spec (valueType comes from Tomahawk).
1023      * 
1024      * @param facesContext
1025      * @param selectMany
1026      * @param submittedValue
1027      * @return
1028      * @throws ConverterException
1029      */
1030     public static Object getConvertedUISelectManyValue(
1031             FacesContext facesContext, UISelectMany selectMany,
1032             Object submittedValue) throws ConverterException
1033     {
1034         // do not consider the valueType attribute
1035         return getConvertedUISelectManyValue(facesContext, selectMany,
1036                 submittedValue, false);
1037     }
1038 
1039     /**
1040      * Gets the converted value of a UISelectMany component.
1041      * 
1042      * @param facesContext
1043      * @param selectMany
1044      * @param submittedValue
1045      * @param considerValueType if true, the valueType attribute of the component will
1046      *                          also be used (applies for Tomahawk UISelectMany components)
1047      * @return
1048      * @throws ConverterException
1049      */
1050     public static Object getConvertedUISelectManyValue(
1051             FacesContext facesContext, UISelectMany selectMany,
1052             Object submittedValue, boolean considerValueType)
1053             throws ConverterException
1054     {
1055         if (submittedValue == null)
1056         {
1057             return null;
1058         }
1059 
1060         if (!(submittedValue instanceof String[]))
1061         {
1062             throw new ConverterException(
1063                     "Submitted value of type String[] for component : "
1064                             + getPathToComponent(selectMany) + "expected");
1065         }
1066 
1067         return _SharedRendererUtils.getConvertedUISelectManyValue(facesContext,
1068                 selectMany, (String[]) submittedValue, considerValueType);
1069     }
1070 
1071     public static boolean getBooleanAttribute(UIComponent component,
1072             String attrName, boolean defaultValue)
1073     {
1074         Boolean b = (Boolean) component.getAttributes().get(attrName);
1075         return b != null ? b.booleanValue() : defaultValue;
1076     }
1077 
1078     public static int getIntegerAttribute(UIComponent component,
1079             String attrName, int defaultValue)
1080     {
1081         Integer i = (Integer) component.getAttributes().get(attrName);
1082         return i != null ? i.intValue() : defaultValue;
1083     }
1084 
1085     private static final String TRINIDAD_FORM_COMPONENT_FAMILY = "org.apache.myfaces.trinidad.Form";
1086     private static final String ADF_FORM_COMPONENT_FAMILY = "oracle.adf.Form";
1087 
1088     /**
1089      * Find the enclosing form of a component
1090      * in the view-tree.
1091      * All Subclasses of <code>UIForm</code> and all known
1092      * form-families are searched for.
1093      * Currently those are the Trinidad form family,
1094      * and the (old) ADF Faces form family.
1095      * <p/>
1096      * There might be additional form families
1097      * which have to be explicitly entered here.
1098      *
1099      * @param uiComponent
1100      * @param facesContext
1101      * @return FormInfo Information about the form - the form itself and its name.
1102      */
1103     public static FormInfo findNestingForm(UIComponent uiComponent,
1104             FacesContext facesContext)
1105     {
1106         UIComponent parent = uiComponent.getParent();
1107         while (parent != null
1108                 && (!ADF_FORM_COMPONENT_FAMILY.equals(parent.getFamily())
1109                         && !TRINIDAD_FORM_COMPONENT_FAMILY.equals(parent
1110                                 .getFamily()) && !(parent instanceof UIForm)))
1111         {
1112             parent = parent.getParent();
1113         }
1114 
1115         if (parent != null)
1116         {
1117             //link is nested inside a form
1118             String formName = parent.getClientId(facesContext);
1119             return new FormInfo(parent, formName);
1120         }
1121 
1122         return null;
1123     }
1124 
1125     public static boolean getBooleanValue(String attribute, Object value,
1126             boolean defaultValue)
1127     {
1128         if (value instanceof Boolean)
1129         {
1130             return ((Boolean) value).booleanValue();
1131         }
1132         else if (value instanceof String)
1133         {
1134             return Boolean.valueOf((String) value).booleanValue();
1135         }
1136         else if (value != null)
1137         {
1138             log.severe("value for attribute "
1139                     + attribute
1140                     + " must be instanceof 'Boolean' or 'String', is of type : "
1141                     + value.getClass());
1142 
1143             return defaultValue;
1144         }
1145 
1146         return defaultValue;
1147     }
1148 
1149     public static void copyHtmlInputTextAttributes(HtmlInputText src,
1150             HtmlInputText dest)
1151     {
1152         dest.setId(src.getId());
1153         boolean forceId = getBooleanValue(JSFAttr.FORCE_ID_ATTR, src
1154                 .getAttributes().get(JSFAttr.FORCE_ID_ATTR), false);
1155         if (forceId)
1156         {
1157             dest.getAttributes().put(JSFAttr.FORCE_ID_ATTR, Boolean.TRUE);
1158         }
1159         dest.setImmediate(src.isImmediate());
1160         dest.setTransient(src.isTransient());
1161         dest.setAccesskey(src.getAccesskey());
1162         dest.setAlt(src.getAlt());
1163         dest.setConverter(src.getConverter());
1164         dest.setDir(src.getDir());
1165         dest.setDisabled(src.isDisabled());
1166         dest.setLang(src.getLang());
1167         dest.setLocalValueSet(src.isLocalValueSet());
1168         dest.setMaxlength(src.getMaxlength());
1169         dest.setOnblur(src.getOnblur());
1170         dest.setOnchange(src.getOnchange());
1171         dest.setOnclick(src.getOnclick());
1172         dest.setOndblclick(src.getOndblclick());
1173         dest.setOnfocus(src.getOnfocus());
1174         dest.setOnkeydown(src.getOnkeydown());
1175         dest.setOnkeypress(src.getOnkeypress());
1176         dest.setOnkeyup(src.getOnkeyup());
1177         dest.setOnmousedown(src.getOnmousedown());
1178         dest.setOnmousemove(src.getOnmousemove());
1179         dest.setOnmouseout(src.getOnmouseout());
1180         dest.setOnmouseover(src.getOnmouseover());
1181         dest.setOnmouseup(src.getOnmouseup());
1182         dest.setOnselect(src.getOnselect());
1183         dest.setReadonly(src.isReadonly());
1184         dest.setRendered(src.isRendered());
1185         dest.setRequired(src.isRequired());
1186         dest.setSize(src.getSize());
1187         dest.setStyle(src.getStyle());
1188         dest.setStyleClass(src.getStyleClass());
1189         dest.setTabindex(src.getTabindex());
1190         dest.setTitle(src.getTitle());
1191         dest.setValidator(src.getValidator());
1192     }
1193 
1194     /**
1195      * @deprecated Logic corrected and encapsulated better in org.apache.myfaces.renderkit.ServerSideStateCacheImpl
1196      */
1197     @Deprecated
1198     public static Integer getViewSequence(FacesContext facescontext)
1199     {
1200         Map map = facescontext.getExternalContext().getRequestMap();
1201         Integer sequence = (Integer) map.get(SEQUENCE_PARAM);
1202         if (sequence == null)
1203         {
1204             sequence = new Integer(1);
1205             map.put(SEQUENCE_PARAM, sequence);
1206 
1207             synchronized (facescontext.getExternalContext().getSession(true))
1208             {
1209                 facescontext.getExternalContext().getSessionMap()
1210                         .put(RendererUtils.SEQUENCE_PARAM, sequence);
1211             }
1212         }
1213         return sequence;
1214     }
1215 
1216     public static UIComponent findComponent(UIComponent headerComp, Class clazz)
1217     {
1218         if (clazz.isAssignableFrom(headerComp.getClass()))
1219         {
1220             return headerComp;
1221         }
1222 
1223         List li = headerComp.getChildren();
1224 
1225         for (int i = 0; i < li.size(); i++)
1226         {
1227             UIComponent comp = (UIComponent) li.get(i);
1228 
1229             //recursively iterate through children to find the component
1230             UIComponent lookupComp = findComponent(comp, clazz);
1231 
1232             if (lookupComp != null)
1233             {
1234                 return lookupComp;
1235             }
1236         }
1237 
1238         return null;
1239     }
1240 
1241     public static void addOrReplaceChild(UIInput component, UIComponent child)
1242     {
1243         List li = component.getChildren();
1244 
1245         for (int i = 0; i < li.size(); i++)
1246         {
1247             UIComponent oldChild = (UIComponent) li.get(i);
1248 
1249             if (oldChild.getId() != null
1250                     && oldChild.getId().equals(child.getId()))
1251             {
1252                 li.set(i, child);
1253                 return;
1254             }
1255         }
1256 
1257         component.getChildren().add(child);
1258     }
1259 
1260     public static String getClientId(FacesContext facesContext,
1261             UIComponent uiComponent, String forAttr)
1262     {
1263         UIComponent forComponent = uiComponent.findComponent(forAttr);
1264         if (forComponent == null)
1265         {
1266             final char separatorChar = UINamingContainer
1267                     .getSeparatorChar(facesContext);
1268             if (log.isLoggable(Level.INFO))
1269             {
1270                 log.info("Unable to find component '"
1271                         + forAttr
1272                         + "' (calling findComponent on component '"
1273                         + uiComponent.getClientId(facesContext)
1274                         + "')."
1275                         + " We'll try to return a guessed client-id anyways -"
1276                         + " this will be a problem if you put the referenced component"
1277                         + " into a different naming-container. If this is the case, " 
1278                         + "you can always use the full client-id.");
1279             }
1280             if (forAttr.length() > 0 && forAttr.charAt(0) == separatorChar)
1281             {
1282                 //absolute id path
1283                 return forAttr.substring(1);
1284             }
1285 
1286             //relative id path, we assume a component on the same level as the label component
1287             String labelClientId = uiComponent.getClientId(facesContext);
1288             int colon = labelClientId.lastIndexOf(separatorChar);
1289 
1290             return colon == -1 ? forAttr : labelClientId
1291                     .substring(0, colon + 1) + forAttr;
1292 
1293         }
1294 
1295         return forComponent.getClientId(facesContext);
1296 
1297     }
1298 
1299     public static List convertIdsToClientIds(String actionFor,
1300             FacesContext facesContext, UIComponent component)
1301     {
1302         List li = new ArrayList();
1303 
1304         String[] ids = actionFor.split(",");
1305 
1306         for (int i = 0; i < ids.length; i++)
1307         {
1308             String trimedId = ids[i].trim();
1309             if (trimedId.equals("none"))
1310             {
1311                 li.add(trimedId);
1312             }
1313             else
1314             {
1315                 li.add(RendererUtils.getClientId(facesContext, component,
1316                         trimedId));
1317             }
1318         }
1319         return li;
1320     }
1321 
1322     public static List convertPhasesToPhasesIds(String actionForPhase)
1323     {
1324         List li = new ArrayList();
1325 
1326         if (actionForPhase == null)
1327         {
1328             return li;
1329         }
1330 
1331         String[] ids = actionForPhase.split(",");
1332 
1333         for (int i = 0; i < ids.length; i++)
1334         {
1335             if (ids[i].equals("PROCESS_VALIDATIONS"))
1336             {
1337                 li.add(PhaseId.PROCESS_VALIDATIONS);
1338             }
1339             else if (ids[i].equals("UPDATE_MODEL_VALUES"))
1340             {
1341                 li.add(PhaseId.UPDATE_MODEL_VALUES);
1342             }
1343         }
1344         return li;
1345     }
1346 
1347     /**
1348      * Helper method which loads a resource file (such as css) by a given context path and a file name.
1349      * Useful to provide css files (or js files) inline.
1350      * 
1351      * @param ctx <code>FacesContext</code> object to calculate the context path of the web application.
1352      * @param file name of the resource file (e.g. <code>foo.css</code>).
1353      * @return the content of the resource file, or <code>null</code> if no such file is available.
1354      */
1355     public static String loadResourceFile(FacesContext ctx, String file)
1356     {
1357 
1358         ByteArrayOutputStream content = new ByteArrayOutputStream(10240);
1359 
1360         InputStream in = null;
1361         try
1362         {
1363             in = ctx.getExternalContext().getResourceAsStream(file);
1364             if (in == null)
1365             {
1366                 return null;
1367             }
1368 
1369             byte[] fileBuffer = new byte[10240];
1370             int read;
1371             while ((read = in.read(fileBuffer)) > -1)
1372             {
1373                 content.write(fileBuffer, 0, read);
1374             }
1375         }
1376         catch (FileNotFoundException e)
1377         {
1378             if (log.isLoggable(Level.WARNING))
1379             {
1380                 log.log(Level.WARNING, "no such file " + file, e);
1381             }
1382             content = null;
1383         }
1384         catch (IOException e)
1385         {
1386             if (log.isLoggable(Level.WARNING))
1387             {
1388                 log.log(Level.WARNING, "problems during processing resource "
1389                         + file, e);
1390             }
1391             content = null;
1392         }
1393         finally
1394         {
1395             try
1396             {
1397                 if (content != null)
1398                 {
1399                     content.close();
1400                 }
1401             }
1402             catch (IOException e)
1403             {
1404                 log.log(Level.WARNING, e.getLocalizedMessage(), e);
1405             }
1406             if (in != null)
1407             {
1408                 try
1409                 {
1410                     in.close();
1411                 }
1412                 catch (IOException e)
1413                 {
1414                     log.log(Level.WARNING, e.getLocalizedMessage(), e);
1415                 }
1416             }
1417         }
1418 
1419         return content.toString();
1420     }
1421 
1422     /**
1423      * check for partial validation or model update attributes being set
1424      * and initialize the request-map accordingly.
1425      * SubForms will work with this information.
1426      */
1427     public static void initPartialValidationAndModelUpdate(
1428             UIComponent component, FacesContext facesContext)
1429     {
1430         String actionFor = (String) component.getAttributes().get("actionFor");
1431 
1432         if (actionFor != null)
1433         {
1434             List li = convertIdsToClientIds(actionFor, facesContext, component);
1435 
1436             facesContext.getExternalContext().getRequestMap()
1437                     .put(ACTION_FOR_LIST, li);
1438 
1439             String actionForPhase = (String) component.getAttributes().get(
1440                     "actionForPhase");
1441 
1442             if (actionForPhase != null)
1443             {
1444                 List phaseList = convertPhasesToPhasesIds(actionForPhase);
1445 
1446                 facesContext.getExternalContext().getRequestMap()
1447                         .put(ACTION_FOR_PHASE_LIST, phaseList);
1448             }
1449         }
1450     }
1451 
1452     public static boolean isAdfOrTrinidadForm(UIComponent component)
1453     {
1454         if (component == null)
1455         {
1456             return false;
1457         }
1458         return ADF_FORM_COMPONENT_FAMILY.equals(component.getFamily())
1459                 || TRINIDAD_FORM_COMPONENT_FAMILY.equals(component.getFamily());
1460     }
1461 
1462     /**
1463      * Gets the ResponseStateManager for the renderKit Id provided
1464      * 
1465      * @deprecated use FacesContext.getRenderKit() or getRenderKitFactory().getRenderKit(
1466      *               context, renderKitId).getResponseStateManager()
1467      */
1468     @Deprecated
1469     public static ResponseStateManager getResponseStateManager(
1470             FacesContext facesContext, String renderKitId)
1471             throws FacesException
1472     {
1473         RenderKit renderKit = facesContext.getRenderKit();
1474 
1475         if (renderKit == null)
1476         {
1477             // look for the renderkit in the request
1478             Map attributesMap = facesContext.getAttributes();
1479             RenderKitFactory factory = (RenderKitFactory) attributesMap
1480                     .get(RENDER_KIT_IMPL);
1481 
1482             if (factory != null)
1483             {
1484                 renderKit = factory.getRenderKit(facesContext, renderKitId);
1485             }
1486             else
1487             {
1488                 factory = (RenderKitFactory) FactoryFinder
1489                         .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
1490 
1491                 if (factory == null)
1492                 {
1493                     throw new IllegalStateException("Factory is null");
1494                 }
1495 
1496                 attributesMap.put(RENDER_KIT_IMPL, factory);
1497 
1498                 renderKit = factory.getRenderKit(facesContext, renderKitId);
1499             }
1500         }
1501 
1502         if (renderKit == null)
1503         {
1504             throw new IllegalArgumentException(
1505                     "Could not find a RenderKit for \"" + renderKitId + "\"");
1506         }
1507 
1508         return renderKit.getResponseStateManager();
1509     }
1510 
1511     /**
1512       * Checks for name/library attributes on component and if they are avaliable,
1513       * creates {@link Resource} and returns it's path suitable for rendering.
1514       * If component doesn't have name/library gets value for attribute named <code>attributeName</code> 
1515       * returns it processed with {@link CoreRenderer#toResourceUri(FacesContext, Object)}
1516       *       
1517       * @param facesContext a {@link FacesContext}
1518       * @param component a {@link UIComponent}
1519       * @param attributeName name of attribute that represents "image", "icon", "source", ... 
1520       * 
1521       * @since 4.0.1
1522       */
1523     public static String getIconSrc(final FacesContext facesContext,
1524             final UIComponent component, final String attributeName)
1525     {
1526 
1527         // JSF 2.0: if "name" attribute is available, treat as a resource reference.
1528         final Map<String, Object> attributes = component.getAttributes();
1529         final String resourceName = (String) attributes.get(JSFAttr.NAME_ATTR);
1530         if (resourceName != null && (resourceName.length() > 0))
1531         {
1532 
1533             final ResourceHandler resourceHandler = facesContext
1534                     .getApplication().getResourceHandler();
1535             final Resource resource;
1536 
1537             final String libraryName = (String) component.getAttributes().get(
1538                     JSFAttr.LIBRARY_ATTR);
1539             if ((libraryName != null) && (libraryName.length() > 0))
1540             {
1541                 resource = resourceHandler.createResource(resourceName,
1542                         libraryName);
1543             }
1544             else
1545             {
1546                 resource = resourceHandler.createResource(resourceName);
1547             }
1548 
1549             if (resource == null)
1550             {
1551                 // If resourceName/libraryName are set but no resource created -> probably a typo,
1552                 // show a message
1553                 if (facesContext.isProjectStage(ProjectStage.Development))
1554                 {
1555                     String summary = "Unable to find resource: " + resourceName;
1556                     if (libraryName != null)
1557                     {
1558                         summary = summary + " from library: " + libraryName;
1559                     }
1560                     facesContext.addMessage(
1561                             component.getClientId(facesContext),
1562                             new FacesMessage(FacesMessage.SEVERITY_WARN,
1563                                     summary, summary));
1564                 }
1565 
1566                 return RES_NOT_FOUND;
1567             }
1568             else
1569             {
1570                 return resource.getRequestPath();
1571             }
1572         }
1573         else
1574         {
1575             String value = (String) component.getAttributes()
1576                     .get(attributeName);
1577             return toResourceUri(facesContext, value);
1578         }
1579     }
1580 
1581     /**
1582      * Coerces an object into a resource URI, calling the view-handler.
1583      */
1584     static public String toResourceUri(FacesContext facesContext, Object o)
1585     {
1586         if (o == null)
1587         {
1588             return null;
1589         }
1590 
1591         String uri = o.toString();
1592 
1593         // *** EL Coercion problem ***
1594         // If icon or image attribute was declared with #{resource[]} and that expression
1595         // evaluates to null (it means ResourceHandler.createResource returns null because 
1596         // requested resource does not exist)
1597         // EL implementation turns null into ""
1598         // see http://www.irian.at/blog/blogid/unifiedElCoercion/#unifiedElCoercion
1599         if (uri.length() == 0)
1600         {
1601             return null;
1602         }
1603 
1604         // With JSF 2.0 url for resources can be done with EL like #{resource['resourcename']}
1605         // and such EL after evalution contains context path for the current web application already,
1606         // -> we dont want call viewHandler.getResourceURL()
1607         if (uri.contains(ResourceHandler.RESOURCE_IDENTIFIER))
1608         {
1609             return uri;
1610         }
1611 
1612         // Treat two slashes as server-relative
1613         if (uri.startsWith("//"))
1614         {
1615             return uri.substring(1);
1616         }
1617         else
1618         {
1619             // If the specified path starts with a "/",
1620             // following method will prefix it with the context path for the current web application,
1621             // and return the result
1622             String resourceURL = facesContext.getApplication().getViewHandler()
1623                     .getResourceURL(facesContext, uri);
1624             return facesContext.getExternalContext().encodeResourceURL(
1625                     resourceURL);
1626         }
1627     }
1628 
1629     /**
1630      * Special converter for handling submitted values which don't need to be converted.
1631      *
1632      * @author mathias (latest modification by $Author: lu4242 $)
1633      * @version $Revision: 1341542 $ $Date: 2012-05-22 10:47:26 -0500 (Tue, 22 May 2012) $
1634      */
1635     private static class PassThroughAsStringConverter implements Converter
1636     {
1637         private final Converter converter;
1638 
1639         public PassThroughAsStringConverter(Converter converter)
1640         {
1641             this.converter = converter;
1642         }
1643 
1644         public Object getAsObject(FacesContext context, UIComponent component,
1645                 String value) throws ConverterException
1646         {
1647             return converter.getAsObject(context, component, value);
1648         }
1649 
1650         public String getAsString(FacesContext context, UIComponent component,
1651                 Object value) throws ConverterException
1652         {
1653             return (String) value;
1654         }
1655 
1656     }
1657 }