Source code: org/apache/myfaces/renderkit/RendererUtils.java
1 /*
2 * Copyright 2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.apache.myfaces.renderkit;
17
18 import java.io.IOException;
19 import java.io.Serializable;
20 import java.lang.reflect.Array;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Date;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30
31 import javax.faces.FacesException;
32 import javax.faces.component.EditableValueHolder;
33 import javax.faces.component.NamingContainer;
34 import javax.faces.component.UIComponent;
35 import javax.faces.component.UIComponentBase;
36 import javax.faces.component.UIForm;
37 import javax.faces.component.UIOutput;
38 import javax.faces.component.UISelectItem;
39 import javax.faces.component.UISelectItems;
40 import javax.faces.component.UISelectMany;
41 import javax.faces.component.UISelectOne;
42 import javax.faces.component.UIViewRoot;
43 import javax.faces.component.ValueHolder;
44 import javax.faces.component.html.HtmlInputText;
45 import javax.faces.context.FacesContext;
46 import javax.faces.convert.Converter;
47 import javax.faces.convert.ConverterException;
48 import javax.faces.el.PropertyNotFoundException;
49 import javax.faces.el.ValueBinding;
50 import javax.faces.model.SelectItem;
51
52 import org.apache.commons.logging.Log;
53 import org.apache.commons.logging.LogFactory;
54 import org.apache.myfaces.util.HashMapUtils;
55 import org.apache.myfaces.util.SelectItemsIterator;
56
57 /**
58 * @author Manfred Geiler (latest modification by $Author: mbr $)
59 * @version $Revision: 290413 $ $Date: 2005-09-20 06:26:13 -0400 (Tue, 20 Sep 2005) $
60 */
61 public class RendererUtils
62 {
63 private static final Log log = LogFactory.getLog(RendererUtils.class);
64
65 public static final String SELECT_ITEM_LIST_ATTR = RendererUtils.class.getName() + ".LIST";
66 public static final String EMPTY_STRING = new String();
67 public static final Object NOTHING = new Serializable() {};
68
69 public static String getPathToComponent(UIComponent component)
70 {
71 StringBuffer buf = new StringBuffer();
72
73 if(component == null)
74 {
75 buf.append("{Component-Path : ");
76 buf.append("[null]}");
77 return buf.toString();
78 }
79
80 getPathToComponent(component,buf);
81
82 buf.insert(0,"{Component-Path : ");
83 buf.append("}");
84
85 return buf.toString();
86 }
87
88 private static void getPathToComponent(UIComponent component, StringBuffer buf)
89 {
90 if(component == null)
91 return;
92
93 StringBuffer intBuf = new StringBuffer();
94
95 intBuf.append("[Class: ");
96 intBuf.append(component.getClass().getName());
97 if(component instanceof UIViewRoot)
98 {
99 intBuf.append(",ViewId: ");
100 intBuf.append(((UIViewRoot) component).getViewId());
101 }
102 else
103 {
104 intBuf.append(",Id: ");
105 intBuf.append(component.getId());
106 }
107 intBuf.append("]");
108
109 buf.insert(0,intBuf);
110
111 if(component!=null)
112 {
113 getPathToComponent(component.getParent(),buf);
114 }
115 }
116
117 public static String getConcatenatedId(FacesContext context, UIComponent container,
118 String clientId)
119 {
120 UIComponent child = container.findComponent(clientId);
121
122 if(child == null)
123 return clientId;
124
125 return getConcatenatedId(context, child);
126 }
127
128 public static String getConcatenatedId(FacesContext context, UIComponent component)
129 {
130 if (context == null) throw new NullPointerException("context");
131
132 StringBuffer idBuf = new StringBuffer();
133
134 idBuf.append(component.getId());
135
136 UIComponent parent = null;
137
138 while((parent = component.getParent())!=null)
139 {
140 if(parent instanceof NamingContainer)
141 {
142 idBuf.insert(0,NamingContainer.SEPARATOR_CHAR);
143 idBuf.insert(0,parent.getId());
144 }
145 }
146
147 return idBuf.toString();
148 }
149
150 public static Boolean getBooleanValue(UIComponent component)
151 {
152 Object value = getObjectValue(component);
153 if (value==null || value instanceof Boolean)
154 {
155 return (Boolean) value;
156 }
157 else
158 {
159 throw new IllegalArgumentException("Expected submitted value of type Boolean for Component : "+
160 getPathToComponent(component));
161 }
162 }
163
164 public static Date getDateValue(UIComponent component)
165 {
166 Object value = getObjectValue(component);
167 if (value==null || value instanceof Date)
168 {
169 return (Date) value;
170 }
171 else
172 {
173 throw new IllegalArgumentException("Expected submitted value of type Date for component : "
174 +getPathToComponent(component));
175 }
176 }
177
178 public static Object getObjectValue(UIComponent component)
179 {
180 if (!(component instanceof ValueHolder))
181 {
182 throw new IllegalArgumentException("Component : "+
183 getPathToComponent(component)+"is not a ValueHolder");
184 }
185
186 if (component instanceof EditableValueHolder)
187 {
188 Object value = ((EditableValueHolder)component).getSubmittedValue();
189 if(value != null && !NOTHING.equals(value))
190 {
191 return value;
192 }
193 }
194
195 return ((ValueHolder)component).getValue();
196 }
197
198 public static String getStringValue(FacesContext facesContext,
199 UIComponent component)
200 {
201 try
202 {
203 if (!(component instanceof ValueHolder))
204 {
205 throw new IllegalArgumentException("Component : "+getPathToComponent(component)+"is not a ValueHolder");
206 }
207
208 if (component instanceof EditableValueHolder)
209 {
210 Object submittedValue = ((EditableValueHolder)component).getSubmittedValue();
211 if (submittedValue != null)
212 {
213 if (submittedValue instanceof String)
214 {
215 return (String)submittedValue;
216 }
217 else
218 {
219 throw new IllegalArgumentException("Expected submitted value of type String for component : "
220 +getPathToComponent(component));
221 }
222 }
223 }
224
225 Object value = ((ValueHolder)component).getValue();
226
227 Converter converter = ((ValueHolder)component).getConverter();
228 if (converter == null && value != null)
229 {
230 if (value instanceof String)
231 {
232 return (String) value;
233 }
234
235 try
236 {
237 converter = facesContext.getApplication().createConverter(value.getClass());
238 }
239 catch (FacesException e)
240 {
241 log.error("No converter for class " + value.getClass().getName() + " found (component id=" + component.getId() + ").");
242 // converter stays null
243 }
244 }
245
246 if (converter == null)
247 {
248 if (value == null)
249 {
250 return "";
251 }
252 else
253 {
254 return value.toString();
255 }
256 }
257 else
258 {
259 return converter.getAsString(facesContext, component, value);
260 }
261 }
262 catch(PropertyNotFoundException ex)
263 {
264 log.error("Property not found - called by component : "+getPathToComponent(component));
265
266 throw ex;
267 }
268 }
269
270 /**
271 * See JSF Spec. 8.5 Table 8-1
272 * @param value
273 * @return boolean
274 */
275 public static boolean isDefaultAttributeValue(Object value)
276 {
277 if (value == null)
278 {
279 return true;
280 }
281 else if (value instanceof Boolean)
282 {
283 return ((Boolean)value).booleanValue() == false;
284 }
285 else if (value instanceof Number)
286 {
287 if (value instanceof Integer)
288 {
289 return ((Number)value).intValue() == Integer.MIN_VALUE;
290 }
291 else if (value instanceof Double)
292 {
293 return ((Number)value).doubleValue() == Double.MIN_VALUE;
294 }
295 else if (value instanceof Long)
296 {
297 return ((Number)value).longValue() == Long.MIN_VALUE;
298 }
299 else if (value instanceof Byte)
300 {
301 return ((Number)value).byteValue() == Byte.MIN_VALUE;
302 }
303 else if (value instanceof Float)
304 {
305 return ((Number)value).floatValue() == Float.MIN_VALUE;
306 }
307 else if (value instanceof Short)
308 {
309 return ((Number)value).shortValue() == Short.MIN_VALUE;
310 }
311 }
312 return false;
313 }
314
315 /**
316 * Find the proper Converter for the given UIOutput component.
317 * @return the Converter or null if no Converter specified or needed
318 * @throws FacesException if the Converter could not be created
319 */
320 public static Converter findUIOutputConverter(FacesContext facesContext,
321 UIOutput component)
322 throws FacesException
323 {
324 return _SharedRendererUtils.findUIOutputConverter(facesContext, component);
325 }
326
327
328 /**
329 * Find proper Converter for the entries in the associated List or Array of
330 * the given UISelectMany as specified in API Doc of UISelectMany.
331 * @return the Converter or null if no Converter specified or needed
332 * @throws FacesException if the Converter could not be created
333 */
334 public static Converter findUISelectManyConverter(FacesContext facesContext,
335 UISelectMany component)
336 {
337 Converter converter = component.getConverter();
338 if (converter != null) return converter;
339
340 //Try to find out by value binding
341 ValueBinding vb = component.getValueBinding("value");
342 if (vb == null) return null;
343
344 Class valueType = vb.getType(facesContext);
345 if (valueType == null) return null;
346
347 if (List.class.isAssignableFrom(valueType))
348 {
349 //According to API Doc of UISelectMany the assumed entry type for a List is String
350 //--> so basically no converter needed
351
352 // However, if the List contains something other than Strings, we can attempt
353 // to find a suitable converter. In JDK 1.4, we can try to find out what the List
354 // contains by looking at the SelectItem value of the first item. With generics in
355 // JDK 1.5, it would be much easier to determine the type.
356
357 List selectItems = RendererUtils.internalGetSelectItemList(component);
358
359 if (selectItems != null && selectItems.size() > 0)
360 {
361 SelectItem selectItem = (SelectItem) selectItems.get(0);
362 Class listComponentType = selectItem.getValue().getClass();
363
364 if (!(String.class.equals(listComponentType)))
365 {
366 try
367 {
368 return facesContext.getApplication().createConverter(listComponentType);
369 }
370 catch (FacesException e)
371 {
372 log.error("No Converter for type " + listComponentType.getName() + " found", e);
373 return null;
374 }
375 }
376 }
377
378 return null;
379 }
380
381 if (!valueType.isArray())
382 {
383 throw new IllegalArgumentException("ValueBinding for UISelectMany : "+getPathToComponent(component)+" must be of type List or Array");
384 }
385
386 Class arrayComponentType = valueType.getComponentType();
387 if (String.class.equals(arrayComponentType)) return null; //No converter needed for String type
388 if (Object.class.equals(arrayComponentType)) return null; //There is no converter for Object class
389
390 try
391 {
392 return facesContext.getApplication().createConverter(arrayComponentType);
393 }
394 catch (FacesException e)
395 {
396 log.error("No Converter for type " + arrayComponentType.getName() + " found", e);
397 return null;
398 }
399 }
400
401
402 public static void checkParamValidity(FacesContext facesContext, UIComponent uiComponent, Class compClass)
403 {
404 if(facesContext == null)
405 throw new NullPointerException("facesContext may not be null");
406 if(uiComponent == null)
407 throw new NullPointerException("uiComponent may not be null");
408
409 //if (compClass != null && !(compClass.isAssignableFrom(uiComponent.getClass())))
410 // why isAssignableFrom with additional getClass method call if isInstance does the same?
411 if (compClass != null && !(compClass.isInstance(uiComponent)))
412 {
413 throw new IllegalArgumentException("uiComponent : "+getPathToComponent(uiComponent)+
414 " is not instance of "+compClass.getName()+" as it should be");
415 }
416 }
417
418
419 public static void renderChildren(FacesContext facesContext, UIComponent component)
420 throws IOException
421 {
422 if (component.getChildCount() > 0)
423 {
424 for (Iterator it = component.getChildren().iterator(); it.hasNext(); )
425 {
426 UIComponent child = (UIComponent)it.next();
427 renderChild(facesContext, child);
428 }
429 }
430 }
431
432
433 public static void renderChild(FacesContext facesContext, UIComponent child)
434 throws IOException
435 {
436 if (!child.isRendered())
437 {
438 return;
439 }
440
441 child.encodeBegin(facesContext);
442 if (child.getRendersChildren())
443 {
444 child.encodeChildren(facesContext);
445 }
446 else
447 {
448 renderChildren(facesContext, child);
449 }
450 child.encodeEnd(facesContext);
451 }
452
453
454
455 /**
456 * @param uiSelectOne
457 * @return List of SelectItem Objects
458 */
459 public static List getSelectItemList(UISelectOne uiSelectOne)
460 {
461 return internalGetSelectItemList(uiSelectOne);
462 }
463
464 /**
465 * @param uiSelectMany
466 * @return List of SelectItem Objects
467 */
468 public static List getSelectItemList(UISelectMany uiSelectMany)
469 {
470 return internalGetSelectItemList(uiSelectMany);
471 }
472
473 private static List internalGetSelectItemList(UIComponent uiComponent)
474 {
475 /* TODO: Shall we cache the list in a component attribute?
476 ArrayList list = (ArrayList)uiComponent.getAttributes().get(SELECT_ITEM_LIST_ATTR);
477 if (list != null)
478 {
479 return list;
480 }
481 */
482
483 List list = new ArrayList();
484
485 for (Iterator iter = new SelectItemsIterator(uiComponent); iter.hasNext();)
486 {
487 list.add(iter.next());
488 }
489 return list;
490 }
491
492
493 /**
494 * Convenient utility method that returns the currently submitted values of
495 * a UISelectMany component as a Set, of which the contains method can then be
496 * easily used to determine if a select item is currently selected.
497 * Calling the contains method of this Set with the renderable (String converted) item value
498 * as argument returns true if this item is selected.
499 * @param uiSelectMany
500 * @return Set containing all currently selected values
501 */
502 public static Set getSubmittedValuesAsSet(FacesContext context, UIComponent component, Converter converter, UISelectMany uiSelectMany)
503 {
504 Object submittedValues = uiSelectMany.getSubmittedValue();
505 if (submittedValues == null)
506 {
507 return null;
508 }
509
510 return internalSubmittedOrSelectedValuesAsSet(context, component, converter, uiSelectMany, submittedValues);
511 }
512
513
514 /**
515 * Convenient utility method that returns the currently selected values of
516 * a UISelectMany component as a Set, of which the contains method can then be
517 * easily used to determine if a value is currently selected.
518 * Calling the contains method of this Set with the item value
519 * as argument returns true if this item is selected.
520 * @param uiSelectMany
521 * @return Set containing all currently selected values
522 */
523 public static Set getSelectedValuesAsSet(FacesContext context, UIComponent component, Converter converter, UISelectMany uiSelectMany)
524 {
525 Object selectedValues = uiSelectMany.getValue();
526
527 return internalSubmittedOrSelectedValuesAsSet(context, component, converter, uiSelectMany, selectedValues);
528 }
529
530
531 /**
532 * Convenient utility method that returns the currently given value as String,
533 * using the given converter.
534 * Especially usefull for dealing with primitive types.
535 */
536 public static String getConvertedStringValue(FacesContext context,
537 UIComponent component, Converter converter, Object value) {
538 if (converter == null) {
539 if (value == null) {
540 return "";
541 } else if (value instanceof String) {
542 return (String) value;
543 } else {
544 throw new IllegalArgumentException(
545 "Value is no String and component "
546 + component.getClientId(context)
547 + " does not have a Converter");
548 }
549 }
550
551 return converter.getAsString(context, component, value);
552 }
553
554
555 /**
556 * Convenient utility method that returns the currently given SelectItem value
557 * as String, using the given converter.
558 * Especially usefull for dealing with primitive types.
559 */
560 public static String getConvertedStringValue(FacesContext context,
561 UIComponent component, Converter converter, SelectItem selectItem) {
562 return getConvertedStringValue(context, component, converter, selectItem.getValue());
563 }
564
565
566 private static Set internalSubmittedOrSelectedValuesAsSet(FacesContext context,
567 UIComponent component, Converter converter, UISelectMany uiSelectMany,
568 Object values)
569 {
570 if (values == null || EMPTY_STRING.equals(values))
571 {
572 return Collections.EMPTY_SET;
573 }
574 else if (values instanceof Object[])
575 {
576 //Object array
577 Object[] ar = (Object[])values;
578 if (ar.length == 0)
579 {
580 return Collections.EMPTY_SET;
581 }
582
583 HashSet set = new HashSet(HashMapUtils.calcCapacity(ar.length));
584 for (int i = 0; i < ar.length; i++)
585 {
586 set.add( getConvertedStringValue(context, component, converter, ar[i]) );
587 }
588 return set;
589 }
590 else if (values.getClass().isArray())
591 {
592 //primitive array
593 int len = Array.getLength(values);
594 HashSet set = new HashSet(HashMapUtils.calcCapacity(len));
595 for (int i = 0; i < len; i++)
596 {
597 set.add( getConvertedStringValue(context, component, converter, Array.get(values,i)) );
598 }
599 return set;
600 }
601 else if (values instanceof List)
602 {
603 List lst = (List)values;
604 if (lst.size() == 0)
605 {
606 return Collections.EMPTY_SET;
607 }
608 else
609 {
610 HashSet set = new HashSet(HashMapUtils.calcCapacity(lst.size()));
611 for(Iterator i =lst.iterator(); i.hasNext(); )
612 set.add( getConvertedStringValue(context, component, converter, i.next()) );
613
614 return set;
615 }
616 }
617 else
618 {
619 throw new IllegalArgumentException("Value of UISelectMany component with path : " + getPathToComponent(uiSelectMany) + " is not of type Array or List");
620 }
621 }
622
623
624
625 public static Object getConvertedUIOutputValue(FacesContext facesContext,
626 UIOutput output,
627 Object submittedValue)
628 throws ConverterException
629 {
630 if (submittedValue!=null && !(submittedValue instanceof String))
631 {
632 if(RendererUtils.NOTHING.equals(submittedValue))
633 {
634 return null;
635 }
636 throw new IllegalArgumentException("Submitted value of type String for component : "+
637 getPathToComponent(output)+"expected");
638 }
639
640 Converter converter;
641 try
642 {
643 converter = findUIOutputConverter(facesContext, output);
644 }
645 catch (FacesException e)
646 {
647 throw new ConverterException(e);
648 }
649
650 if (converter == null)
651 {
652 //No conversion needed
653 return submittedValue;
654 }
655 else
656 {
657 //Conversion
658 return converter.getAsObject(facesContext, output, (String)submittedValue);
659 }
660 }
661
662
663 public static Object getConvertedUISelectManyValue(FacesContext facesContext,
664 UISelectMany selectMany,
665 Object submittedValue)
666 throws ConverterException
667 {
668 if (submittedValue == null)
669 {
670 return null;
671 }
672 else
673 {
674 if (!(submittedValue instanceof String[]))
675 {
676 throw new ConverterException("Submitted value of type String[] for component : "
677 + getPathToComponent(selectMany) + "expected");
678 }
679 }
680 return _SharedRendererUtils.getConvertedUISelectManyValue(facesContext,
681 selectMany,
682 (String[])submittedValue);
683 }
684
685
686 public static boolean getBooleanAttribute(UIComponent component,
687 String attrName,
688 boolean defaultValue)
689 {
690 Boolean b = (Boolean)component.getAttributes().get(attrName);
691 return b != null ? b.booleanValue() : defaultValue;
692 }
693
694 public static int getIntegerAttribute(UIComponent component,
695 String attrName,
696 int defaultValue)
697 {
698 Integer i = (Integer)component.getAttributes().get(attrName);
699 return i != null ? i.intValue() : defaultValue;
700 }
701
702 public static UIForm findParentForm(UIComponentBase comp)
703 {
704 UIComponent parent = comp.getParent();
705 while (parent != null)
706 {
707 if (parent instanceof UIForm)
708 {
709 return (UIForm)parent;
710 }
711 parent = parent.getParent();
712 }
713 return null;
714 }
715
716 public static void copyHtmlInputTextAttributes(HtmlInputText src, HtmlInputText dest)
717 {
718 dest.setId(src.getId());
719 dest.setImmediate(src.isImmediate());
720 dest.setTransient(src.isTransient());
721 dest.setAccesskey(src.getAccesskey());
722 dest.setAlt(src.getAlt());
723 dest.setConverter(src.getConverter());
724 dest.setDir(src.getDir());
725 dest.setDisabled(src.isDisabled());
726 dest.setLang(src.getLang());
727 dest.setLocalValueSet(src.isLocalValueSet());
728 dest.setMaxlength(src.getMaxlength());
729 dest.setOnblur(src.getOnblur());
730 dest.setOnchange(src.getOnchange());
731 dest.setOnclick(src.getOnclick());
732 dest.setOndblclick(src.getOndblclick());
733 dest.setOnfocus(src.getOnfocus());
734 dest.setOnkeydown(src.getOnkeydown());
735 dest.setOnkeypress(src.getOnkeypress());
736 dest.setOnkeyup(src.getOnkeyup());
737 dest.setOnmousedown(src.getOnmousedown());
738 dest.setOnmousemove(src.getOnmousemove());
739 dest.setOnmouseout(src.getOnmouseout());
740 dest.setOnmouseover(src.getOnmouseover());
741 dest.setOnmouseup(src.getOnmouseup());
742 dest.setOnselect(src.getOnselect());
743 dest.setReadonly(src.isReadonly());
744 dest.setRendered(src.isRendered());
745 dest.setRequired(src.isRequired());
746 dest.setSize(src.getSize());
747 dest.setStyle(src.getStyle());
748 dest.setStyleClass(src.getStyleClass());
749 dest.setTabindex(src.getTabindex());
750 dest.setTitle(src.getTitle());
751 dest.setValidator(src.getValidator());
752 }
753 }