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