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.trinidad.component;
20
21 import java.io.InputStream;
22 import java.io.IOException;
23 import java.net.URL;
24 import java.util.ArrayList;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.NoSuchElementException;
29 import java.util.Properties;
30
31 import javax.el.ELContext;
32 import javax.el.ELException;
33 import javax.el.MethodExpression;
34 import javax.el.ValueExpression;
35
36 import javax.faces.component.NamingContainer;
37 import javax.faces.component.UIComponent;
38 import javax.faces.context.FacesContext;
39 import javax.faces.el.EvaluationException;
40 import javax.faces.el.MethodBinding;
41 import javax.faces.el.ValueBinding;
42 import javax.faces.event.AbortProcessingException;
43 import javax.faces.event.FacesEvent;
44 import javax.faces.event.FacesListener;
45 import javax.faces.render.RenderKit;
46 import javax.faces.render.Renderer;
47
48 import org.apache.myfaces.trinidad.bean.FacesBean;
49 import org.apache.myfaces.trinidad.bean.FacesBeanFactory;
50 import org.apache.myfaces.trinidad.bean.PropertyKey;
51 import org.apache.myfaces.trinidad.bean.util.ValueMap;
52 import org.apache.myfaces.trinidad.change.AttributeComponentChange;
53 import org.apache.myfaces.trinidad.context.RequestContext;
54 import org.apache.myfaces.trinidad.event.AttributeChangeEvent;
55 import org.apache.myfaces.trinidad.event.AttributeChangeListener;
56 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
57 import org.apache.myfaces.trinidad.render.ExtendedRenderer;
58 import org.apache.myfaces.trinidad.render.LifecycleRenderer;
59
60
61 /**
62 * Base implementation of components for all of Trinidad. UIXComponentBase
63 * offers a number of features not supplied by the standard UIComponentBase
64 * class:
65 * <ul>
66 * <li>Use of FacesBean for better and easier state saving</li>
67 * <li>Support of the LifecycleRenderer class for greater Renderer
68 * control over the lifecycle</li>
69 * <li>Built-in support for both the "partialTriggers" attribute
70 * (declarative support for being a PPR target) and for triggering
71 * such components (for being a the source of a PPR-causing event).</li>
72 * </ul>
73 * <h3>FacesBean and UIXComponentBase</h3>
74 * <p>
75 * UIXComponentBase differs from UIXComponent most particularly
76 * in its use of FacesBeans to store all state. This offers
77 * a number of advantages:
78 * <ul>
79 * <li>Subclassers - if they use FacesBean for their state as well -
80 * do not need to write overrides of saveState() and restoreState().
81 * </li>
82 * <li>State is optimized by default</li>
83 * <li>Future optimizations - partly exposed today with
84 * markInitialState() - can offer major state saving improvements.
85 * </ul>
86 * </p>
87 */
88 // TODO Write Class Javadoc
89 // TODO Thorough review against UIComponentBase
90 abstract public class UIXComponentBase extends UIXComponent
91 {
92 // Created up top to ensure it's present while we're processing
93 // class initialization code.
94 static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(UIXComponentBase.class);
95
96 static public final FacesBean.Type TYPE = _createType();
97 static public final PropertyKey ID_KEY =
98 TYPE.registerKey("id", String.class, PropertyKey.CAP_NOT_BOUND);
99 static private final PropertyKey _GENERATED_ID_KEY =
100 TYPE.registerKey("_genId", String.class, PropertyKey.CAP_NOT_BOUND);
101 static public final PropertyKey RENDERED_KEY =
102 TYPE.registerKey("rendered", Boolean.class, Boolean.TRUE);
103 static public final PropertyKey BINDING_KEY =
104 TYPE.registerKey("binding");
105 static public final PropertyKey TRANSIENT_KEY =
106 TYPE.registerKey("transient", Boolean.class,
107 PropertyKey.CAP_NOT_BOUND |
108 PropertyKey.CAP_TRANSIENT);
109 static public final PropertyKey RENDERER_TYPE_KEY =
110 TYPE.registerKey("rendererType", String.class, PropertyKey.CAP_NOT_BOUND);
111 static private final PropertyKey _LISTENERS_KEY =
112 TYPE.registerKey("listeners", FacesListener[].class, PropertyKey.CAP_LIST);
113 static private final PropertyKey _ATTRIBUTE_CHANGE_LISTENER_KEY =
114 TYPE.registerKey("attributeChangeListener", MethodExpression.class);
115 // =-=AEW "parent", "rendersChildren", "childCount", "children",
116 // "facets", "facetsAndChildren", "family" all are technically
117 // bean properties, but they aren't exposed here...
118
119 static
120 {
121 // Register a couple of PropertyKeys against names that
122 // the RI's UIComponentTag implementation is shoving
123 // into all components. This is purely an optimization, but
124 // a very useful one.
125 TYPE.registerKey("javax.faces.webapp.COMPONENT_IDS",
126 List.class,
127 PropertyKey.CAP_NOT_BOUND);
128 TYPE.registerKey("javax.faces.webapp.FACET_NAMES",
129 List.class,
130 PropertyKey.CAP_NOT_BOUND);
131 TYPE.lock();
132 }
133
134 public UIXComponentBase()
135 {
136 }
137
138 public UIXComponentBase(String rendererType)
139 {
140 setRendererType(rendererType);
141 }
142
143 protected FacesBean createFacesBean(
144 String rendererType)
145 {
146 FacesBean bean = FacesBeanFactory.createFacesBean(getClass(),
147 rendererType);
148 UIXFacesBean uixBean = (UIXFacesBean) bean;
149 uixBean.init(this, getBeanType());
150 return uixBean;
151 }
152
153 protected PropertyKey getPropertyKey(String name)
154 {
155 PropertyKey key = getBeanType().findKey(name);
156 if (key == null)
157 key = PropertyKey.createPropertyKey(name);
158
159 return key;
160 }
161
162 protected FacesBean.Type getBeanType()
163 {
164 return TYPE;
165 }
166
167 @Override
168 public FacesBean getFacesBean()
169 {
170 if (_facesBean == null)
171 _init(null);
172
173 return _facesBean;
174 }
175
176 @Override
177 public String getContainerClientId(FacesContext context, UIComponent child)
178 {
179 return getContainerClientId(context);
180 }
181
182 @Override
183 public void addAttributeChangeListener(AttributeChangeListener acl)
184 {
185 addFacesListener(acl);
186 }
187
188 @Override
189 public void removeAttributeChangeListener(AttributeChangeListener acl)
190 {
191 removeFacesListener(acl);
192 }
193
194 @Override
195 public AttributeChangeListener[] getAttributeChangeListeners()
196 {
197 return (AttributeChangeListener[])
198 getFacesListeners(AttributeChangeListener.class);
199 }
200
201 @Override
202 public void setAttributeChangeListener(MethodExpression mb)
203 {
204 setProperty(_ATTRIBUTE_CHANGE_LISTENER_KEY, mb);
205 }
206
207 @Deprecated
208 public void setAttributeChangeListener(MethodBinding mb)
209 {
210 setAttributeChangeListener(adaptMethodBinding(mb));
211 }
212
213 @Override
214 public MethodExpression getAttributeChangeListener()
215 {
216 return (MethodExpression) getProperty(_ATTRIBUTE_CHANGE_LISTENER_KEY);
217 }
218
219
220 @Override
221 public ValueExpression getValueExpression(String name)
222 {
223 if (name == null)
224 throw new NullPointerException();
225
226 PropertyKey key = getPropertyKey(name);
227
228 // Support standard RI behavior where getValueBinding()
229 // doesn't complain about being asked for a ValueBinding -
230 // but continue supporting strict behavior at FacesBean layer.
231 if (!key.getSupportsBinding())
232 return null;
233
234 return getFacesBean().getValueExpression(key);
235 }
236
237 @Override
238 public void setValueExpression(String name,
239 ValueExpression expression)
240 {
241 if (name == null)
242 throw new NullPointerException();
243
244 if ((expression != null) && expression.isLiteralText())
245 {
246 ELContext context =
247 FacesContext.getCurrentInstance().getELContext();
248 getAttributes().put(name, expression.getValue(context));
249 }
250 else
251 {
252 PropertyKey key = getPropertyKey(name);
253 getFacesBean().setValueExpression(key, expression);
254 }
255 }
256
257
258
259 /**
260 */
261 @Override
262 public ValueBinding getValueBinding(String name)
263 {
264 if (name == null)
265 throw new NullPointerException();
266
267 PropertyKey key = getPropertyKey(name);
268
269 // Support standard RI behavior where getValueBinding()
270 // doesn't complain about being asked for a ValueBinding -
271 // but continue supporting strict behavior at FacesBean layer.
272 if (!key.getSupportsBinding())
273 return null;
274
275 return getFacesBean().getValueBinding(key);
276 }
277
278
279 @Override
280 public void setValueBinding(String name, ValueBinding binding)
281 {
282 if (name == null)
283 throw new NullPointerException();
284
285 PropertyKey key = getPropertyKey(name);
286 getFacesBean().setValueBinding(key, binding);
287 }
288
289
290 @Override
291 public Map<String, Object> getAttributes()
292 {
293 if (_attributes == null)
294 _init(null);
295
296 return _attributes;
297 }
298
299 // ------------------------------------------------------------- Properties
300
301
302
303 @Override
304 public String getClientId(FacesContext context)
305 {
306 // NOTE - client ids cannot be cached because the generated
307 // value has to be dynamically calculated in some cases (UIData)
308
309 String clientId = getId();
310 if (clientId == null)
311 {
312 clientId = (String) getProperty(_GENERATED_ID_KEY);
313 if (clientId == null)
314 {
315 clientId = context.getViewRoot().createUniqueId();
316 setProperty(_GENERATED_ID_KEY, clientId);
317 }
318 }
319
320 // Search for an ancestor that is a naming container
321 UIComponent containerComponent = getParent();
322 while (null != containerComponent)
323 {
324 if (containerComponent instanceof NamingContainer)
325 {
326 String contClientId;
327
328 // Pass additional context information to naming containers which extend UIXComponent:
329 if (containerComponent instanceof UIXComponent)
330 contClientId = ((UIXComponent)containerComponent).getContainerClientId(context, this);
331 else
332 contClientId = containerComponent.getContainerClientId(context);
333
334 StringBuilder bld = __getSharedStringBuilder();
335 bld.append(contClientId).append(NamingContainer.SEPARATOR_CHAR).append(clientId);
336 clientId = bld.toString();
337 break;
338 }
339
340 containerComponent = containerComponent.getParent();
341 }
342
343 Renderer renderer = getRenderer(context);
344 if (null != renderer)
345 clientId = renderer.convertClientId(context, clientId);
346
347 return clientId;
348 }
349
350
351 /**
352 * Gets the identifier for the component.
353 */
354 @Override
355 public String getId()
356 {
357 return (String) getProperty(ID_KEY);
358 }
359
360
361 /**
362 * Sets the identifier for the component. The identifier
363 * must follow a subset of the syntax allowed in HTML:
364 * <ul>
365 * <li>Must not be a zero-length String.</li>
366 * <li>First character must be an ASCII letter (A-Za-z) or an underscore ('_').</li>
367 * <li>Subsequent characters must be an ASCII letter or digit (A-Za-z0-9), an underscore ('_'), or a dash ('-').
368 * </ul>
369 */
370 @Override
371 public void setId(String id)
372 {
373 // =-=AEW Currently, setId() assumes that resetting to
374 // the same value *is not* short-circuited.
375 _validateId(id);
376 // If we're setting the ID to null, don't discard
377 // the _GENERATED_ID
378 if (id != null)
379 setProperty(_GENERATED_ID_KEY, null);
380
381 setProperty(ID_KEY, id);
382 }
383
384
385
386 @Override
387 abstract public String getFamily();
388
389
390 @Override
391 public UIComponent getParent()
392 {
393 return _parent;
394 }
395
396
397 /**
398 * <p>Set the parent <code>UIComponent</code> of this
399 * <code>UIComponent</code>.</p>
400 *
401 * @param parent The new parent, or <code>null</code> for the root node
402 * of a component tree
403 */
404 @Override
405 public void setParent(UIComponent parent)
406 {
407 _parent = parent;
408 }
409
410
411 @Override
412 public boolean isRendered()
413 {
414 return getBooleanProperty(RENDERED_KEY, true);
415 }
416
417
418 @Override
419 public void setRendered(boolean rendered)
420 {
421 setBooleanProperty(RENDERED_KEY, rendered);
422 }
423
424 public boolean isTransient()
425 {
426 return getBooleanProperty(TRANSIENT_KEY, false);
427 }
428
429 public void setTransient(boolean newTransient)
430 {
431 setBooleanProperty(TRANSIENT_KEY, newTransient);
432 }
433
434 @Override
435 public String getRendererType()
436 {
437 // Don't create the FacesBean just to get the renderer type;
438 // Generally, rendererType will be the first property
439 // set, which will trigger the code below in setRendererType()
440 // to run correctly.
441 if (_facesBean == null)
442 return null;
443
444 return (String) getProperty(RENDERER_TYPE_KEY);
445 }
446
447 @Override
448 public void setRendererType(String rendererType)
449 {
450 String oldRendererType = getRendererType();
451 if (oldRendererType == null)
452 {
453 if (rendererType == null)
454 return;
455 }
456 else if (oldRendererType.equals(rendererType))
457 {
458 return;
459 }
460
461 // prepare the faces bean
462 _init(rendererType);
463 setProperty(RENDERER_TYPE_KEY, rendererType);
464 }
465
466
467 @Override
468 public boolean getRendersChildren()
469 {
470 Renderer renderer = getRenderer(getFacesContext());
471 if (renderer == null)
472 return false;
473
474 return renderer.getRendersChildren();
475 }
476
477
478
479
480 // ------------------------------------------------ Tree Management Methods
481
482
483
484 @Override
485 public UIComponent findComponent(String id)
486 {
487 if (id == null)
488 throw new NullPointerException();
489
490 if ("".equals(id))
491 throw new IllegalArgumentException();
492
493 UIComponent from = this;
494 // If it starts with the separator character, it's
495 // an absolute path: start at the top
496 if (id.charAt(0) == NamingContainer.SEPARATOR_CHAR)
497 {
498 id = id.substring(1);
499 while (from.getParent() != null)
500 from = from.getParent();
501 }
502 // If it's a NamingContainer, start right here
503 else if (this instanceof NamingContainer)
504 {
505 ;
506 }
507 // Otherwise, go up to look for NamingContainer (or the top)
508 else
509 {
510 while (from.getParent() != null)
511 {
512 from = from.getParent();
513 if (from instanceof NamingContainer)
514 break;
515 }
516 }
517
518 // Evaluate each part of the expression
519 String searchId;
520 int separatorIndex = id.indexOf(NamingContainer.SEPARATOR_CHAR);
521 if (separatorIndex < 0)
522 searchId = id;
523 else
524 searchId = id.substring(0, separatorIndex);
525
526 if (searchId.equals(from.getId()))
527 {
528 // Don't need to look inside if we're already there
529 ;
530 }
531 else
532 {
533 from = _findInsideOf(from, searchId);
534 }
535
536 // Final ID: break, and return whatever we've found
537 if (separatorIndex < 0)
538 {
539 return from;
540 }
541 // Just an intermediate step. Make sure we're at a NamingContainer,
542 // and then ask it to find the rest of the expression.
543 else
544 {
545 if (from == null)
546 return null;
547
548 if (!(from instanceof NamingContainer))
549 throw new IllegalArgumentException();
550 return from.findComponent(id.substring(separatorIndex + 1));
551 }
552 }
553
554
555
556 /**
557 * <p>Create (if necessary) and return a List of the children associated
558 * with this component.</p>
559 */
560 @Override
561 public List<UIComponent> getChildren()
562 {
563 if (_children == null)
564 _children = new ChildArrayList(this);
565
566 return _children;
567 }
568
569 @Override
570 public int getChildCount()
571 {
572 if (_children == null)
573 return 0;
574 return getChildren().size();
575 }
576
577
578 /**
579 * <p>Create (if necessary) and return a Map of the facets associated
580 * with this component.</p>
581 */
582 @Override
583 public Map<String, UIComponent> getFacets()
584 {
585
586 if (_facets == null)
587 _facets = new FacetHashMap(this);
588
589 return _facets;
590 }
591
592
593 @Override
594 public UIComponent getFacet(String facetName)
595 {
596 if (facetName == null)
597 throw new NullPointerException();
598 if (_facets == null)
599 return null;
600 return getFacets().get(facetName);
601 }
602
603
604 /**
605 * Returns an Iterator over the names of all facets.
606 * Unlike getFacets().keySet().iterator(), this does
607 * not require instantiating a Map if there are
608 * no facets. (Note that this is not part of the
609 * UIComponent API.)
610 */
611 public Iterator<String> getFacetNames()
612 {
613 if (_facets == null)
614 return _EMPTY_STRING_ITERATOR;
615 return _facets.keySet().iterator();
616 }
617
618
619 // TODO: Optimize to a compound iterator
620 @Override
621 public Iterator<UIComponent> getFacetsAndChildren()
622 {
623 // =-=AEW Is this supposed to be an immutable Iterator?
624 if (_facets == null)
625 {
626 if (_children == null)
627 return _EMPTY_UICOMPONENT_ITERATOR;
628
629 return _children.iterator();
630 }
631 else
632 {
633 if (_children == null)
634 return _facets.values().iterator();
635 }
636
637 ArrayList<UIComponent> childrenAndFacets = new ArrayList<UIComponent>();
638 for(UIComponent facet : _facets.values())
639 {
640 childrenAndFacets.add(facet);
641 }
642
643 for(UIComponent child : _children)
644 {
645 childrenAndFacets.add(child);
646 }
647
648 if (childrenAndFacets.isEmpty())
649 return _EMPTY_UICOMPONENT_ITERATOR;
650
651 return childrenAndFacets.iterator();
652 }
653
654
655 // ------------------------------------------- Event processing methods
656
657 @Override
658 public void broadcast(FacesEvent event)
659 throws AbortProcessingException
660 {
661 if (event == null)
662 throw new NullPointerException();
663
664 if (_LOG.isFine())
665 _LOG.fine("Broadcasting event " + event + " to " + this);
666
667 UIComponent component = event.getComponent();
668 if (component != null)
669 {
670 RequestContext adfContext = RequestContext.getCurrentInstance();
671 if (adfContext != null)
672 adfContext.partialUpdateNotify(component);
673 }
674
675 Iterator<FacesListener> iter =
676 (Iterator<FacesListener>)getFacesBean().entries(_LISTENERS_KEY);
677
678 while (iter.hasNext())
679 {
680 FacesListener listener = iter.next();
681 if (event.isAppropriateListener(listener))
682 {
683 event.processListener(listener);
684 }
685 }
686
687 if (event instanceof AttributeChangeEvent)
688 {
689 broadcastToMethodExpression(event, getAttributeChangeListener());
690 }
691 }
692
693
694 // ------------------------------------------- Lifecycle Processing Methods
695
696
697 @Override
698 public void decode(FacesContext context)
699 {
700 if (context == null)
701 throw new NullPointerException();
702
703 // Find all the partialTriggers and save on the context
704 Map<String, Object> attrs = getAttributes();
705 Object triggers = attrs.get("partialTriggers");
706 if (triggers instanceof String[])
707 {
708 RequestContext adfContext = RequestContext.getCurrentInstance();
709 if (adfContext != null)
710 adfContext.addPartialTriggerListeners(this, (String[]) triggers);
711 }
712
713 __rendererDecode(context);
714 }
715
716 @Override
717 public void encodeBegin(FacesContext context) throws IOException
718 {
719 if (context == null)
720 throw new NullPointerException();
721
722 if (!isRendered())
723 return;
724
725 _cacheRenderer(context);
726 Renderer renderer = getRenderer(context);
727 // if there is a Renderer for this component
728 if (renderer != null)
729 {
730 renderer.encodeBegin(context, this);
731 }
732 }
733
734 @Override
735 public void encodeChildren(FacesContext context) throws IOException
736 {
737 if (context == null)
738 throw new NullPointerException();
739
740 if (!isRendered())
741 return;
742
743 Renderer renderer = getRenderer(context);
744 // if there is a Renderer for this component
745 if (renderer != null)
746 {
747 renderer.encodeChildren(context, this);
748 }
749 }
750
751 @Override
752 public void encodeEnd(FacesContext context) throws IOException
753 {
754 if (context == null)
755 throw new NullPointerException();
756
757 if (isRendered())
758 {
759 Renderer renderer = getRenderer(context);
760 // if there is a Renderer for this component
761 if (renderer != null)
762 {
763 renderer.encodeEnd(context, this);
764 }
765 }
766 }
767
768 /**
769 * Encodes a component and all of its children, whether
770 * getRendersChildren() is true or false. When rendersChildren
771 * is false, each child whose "rendered" property is true
772 * will be sequentially rendered; facets will be ignored.
773 */
774 @Override
775 public void encodeAll(FacesContext context) throws IOException
776 {
777 if (context == null)
778 throw new NullPointerException();
779
780 // This code ends up calling isRendered() once overall,
781 // plus up to three times more for encodeBegin(),
782 // encodeChildren(), and encodeEnd().
783 __encodeRecursive(context, this);
784 }
785
786 @Override
787 public void queueEvent(FacesEvent event)
788 {
789 if (event == null)
790 throw new NullPointerException();
791
792 UIComponent parent = getParent();
793 if (parent == null)
794 throw new IllegalStateException();
795
796 parent.queueEvent(event);
797 }
798
799 // ----------------------------------------------- Lifecycle Phase Handlers
800
801 @Override
802 public void processDecodes(FacesContext context)
803 {
804 if (context == null)
805 throw new NullPointerException();
806
807 if (!isRendered())
808 return;
809
810 // Process all facets and children of this component
811 decodeChildren(context);
812
813 // Process this component itself
814 decode(context);
815
816 }
817
818 @Override
819 public void processValidators(FacesContext context)
820 {
821 if (context == null)
822 throw new NullPointerException();
823
824 if (!isRendered())
825 return;
826
827 // Process all facets and children of this component
828 validateChildren(context);
829 }
830
831 @Override
832 public void processUpdates(FacesContext context)
833 {
834 if (context == null)
835 throw new NullPointerException();
836
837 if (!isRendered())
838 return;
839
840 // Process all facets and children of this component
841 updateChildren(context);
842 }
843
844 @Override
845 public Object processSaveState(FacesContext context)
846 {
847 if (context == null)
848 throw new NullPointerException();
849
850 if (_LOG.isFiner())
851 _LOG.finer("processSaveState() on " + this);
852
853 if (((_children == null) || _children.isEmpty()) &&
854 ((_facets == null) || _facets.isEmpty()))
855 {
856 return saveState(context);
857 }
858 else
859 {
860 TreeState state = new TreeState();
861 state.saveState(context, this);
862 if (state.isEmpty())
863 return null;
864
865 return state;
866 }
867 }
868
869 // TODO will have deep problems if UIComponent.saveState() ever
870 // returns a String.
871 // TODO crashes and burns if there are fewer children or missing
872 // facets from when state was saved.
873 @Override
874 public void processRestoreState(FacesContext context, Object state)
875 {
876 if (context == null)
877 throw new NullPointerException();
878
879 if (_LOG.isFiner())
880 _LOG.finer("processRestoreState() on " + this);
881
882 // If we saved a "TreeState", use it to restore everything
883 if (state instanceof TreeState)
884 {
885 ((TreeState) state).restoreState(context, this);
886 }
887 // Otherwise, we had no children or facets, and just use
888 // the "state" object
889 else
890 {
891 restoreState(context, state);
892 }
893 }
894
895 @Override
896 public void markInitialState()
897 {
898 // -= Simon Lessard =-
899 // FIXME: Set to true, but never read
900 //_initialStateMarked = true;
901 getFacesBean().markInitialState();
902 }
903
904 public Object saveState(FacesContext context)
905 {
906 return getFacesBean().saveState(context);
907 }
908
909 public void restoreState(FacesContext context, Object stateObj)
910 {
911 getFacesBean().restoreState(context, stateObj);
912 }
913
914
915 @Override
916 public String toString()
917 {
918 String className = getClass().getName();
919 int periodIndex = className.lastIndexOf('.');
920 if (periodIndex >= 0)
921 className = className.substring(periodIndex + 1);
922
923 return className + "[" + getFacesBean().toString() + ", id=" + getId() + "]";
924 }
925
926 /**
927 * <p>Return the {@link FacesContext} instance for the current request.</p>
928 */
929 @Override
930 protected FacesContext getFacesContext()
931 {
932 // If we ever have a way for a component to get notified
933 // when it's finished being used for a given request,
934 // we could cache this as an instance variable.
935 return FacesContext.getCurrentInstance();
936 }
937
938
939 /**
940 * Delegates to LifecycleRenderer, if present,
941 * otherwise calls decodeChildrenImpl.
942 *
943 * @param context the current FacesContext
944 */
945 final protected void decodeChildren(FacesContext context)
946 {
947 LifecycleRenderer renderer = getLifecycleRenderer(context);
948 // if there is a HierarchyRenderer for this component
949 if (renderer != null)
950 {
951 if (renderer.decodeChildren(context, this))
952 return;
953 }
954
955 decodeChildrenImpl(context);
956 }
957
958 /**
959 * Calls processDecodes on all facets and children of this
960 * component.
961 * @param context the current FacesContext
962 */
963 protected void decodeChildrenImpl(FacesContext context)
964 {
965 Iterator<UIComponent> kids = getFacetsAndChildren();
966 while (kids.hasNext())
967 {
968 UIComponent kid = kids.next();
969 kid.processDecodes(context);
970 }
971 }
972
973
974 /**
975 * Delegates to LifecycleRenderer, if present,
976 * otherwise calls validateChildrenImpl.
977 *
978 * @param context the current FacesContext
979 */
980 final protected void validateChildren(FacesContext context)
981 {
982 LifecycleRenderer renderer = getLifecycleRenderer(context);
983 // if there is a ExtendedRenderer for this component
984 if (renderer != null)
985 {
986 if (renderer.validateChildren(context, this))
987 return;
988 }
989
990 validateChildrenImpl(context);
991 }
992
993 /**
994 * Calls processValidators on all facets and children of this
995 * component.
996 * @param context the current FacesContext
997 */
998 protected void validateChildrenImpl(FacesContext context)
999 {
1000 // Process all the facets and children of this component
1001 Iterator<UIComponent> kids = getFacetsAndChildren();
1002 while (kids.hasNext())
1003 {
1004 UIComponent kid = kids.next();
1005 kid.processValidators(context);
1006 }
1007 }
1008
1009
1010 /**
1011 * Delegates to LifecycleRenderer, if present,
1012 * otherwise calls upateChildrenImpl.
1013 *
1014 * @param context the current FacesContext
1015 */
1016 final protected void updateChildren(FacesContext context)
1017 {
1018 LifecycleRenderer renderer = getLifecycleRenderer(context);
1019 // if there is a ExtendedRenderer for this component
1020 if (renderer != null)
1021 {
1022 if (renderer.updateChildren(context, this))
1023 return;
1024 }
1025
1026 updateChildrenImpl(context);
1027 }
1028
1029 protected void updateChildrenImpl(FacesContext context)
1030 {
1031 // Process all the facets and children of this component
1032 Iterator<UIComponent> kids = getFacetsAndChildren();
1033 while (kids.hasNext())
1034 {
1035 UIComponent kid = kids.next();
1036 kid.processUpdates(context);
1037 }
1038 }
1039
1040 @Override
1041 protected void addFacesListener(FacesListener listener)
1042 {
1043 if (listener == null)
1044 throw new NullPointerException();
1045
1046 getFacesBean().addEntry(_LISTENERS_KEY, listener);
1047 }
1048
1049 @Override
1050 protected void removeFacesListener(FacesListener listener)
1051 {
1052 if (listener == null)
1053 throw new NullPointerException();
1054
1055 getFacesBean().removeEntry(_LISTENERS_KEY, listener);
1056 }
1057
1058 @Override
1059 protected FacesListener[] getFacesListeners(Class clazz)
1060 {
1061 if (clazz == null)
1062 throw new NullPointerException();
1063
1064 if (!FacesListener.class.isAssignableFrom(clazz))
1065 throw new IllegalArgumentException();
1066
1067 return (FacesListener[])
1068 getFacesBean().getEntries(_LISTENERS_KEY, clazz);
1069 }
1070
1071 protected void addAttributeChange(
1072 String attributeName,
1073 Object attributeValue)
1074 {
1075 AttributeComponentChange aa =
1076 new AttributeComponentChange(attributeName, attributeValue);
1077 RequestContext adfContext = RequestContext.getCurrentInstance();
1078 adfContext.getChangeManager().addComponentChange(getFacesContext(), this, aa);
1079 }
1080
1081 void __rendererDecode(FacesContext context)
1082 {
1083 _cacheRenderer(context);
1084 Renderer renderer = getRenderer(context);
1085 // if there is a Renderer for this component
1086 if (renderer != null)
1087 {
1088 renderer.decode(context, this);
1089 }
1090 }
1091
1092 private void _cacheRenderer(FacesContext context)
1093 {
1094 Renderer renderer = _getRendererImpl(context);
1095 _cachedRenderer = renderer;
1096
1097 // cache the lifecycle renderer
1098 if (renderer instanceof LifecycleRenderer)
1099 {
1100 _cachedLifecycleRenderer = (LifecycleRenderer)renderer;
1101 }
1102 else
1103 {
1104 _cachedLifecycleRenderer = null;
1105 }
1106 }
1107
1108 private Renderer _getRendererImpl(FacesContext context)
1109 {
1110 String rendererType = getRendererType();
1111 if (rendererType != null)
1112 {
1113 RenderKit renderKit = context.getRenderKit();
1114 Renderer renderer = renderKit.getRenderer(getFamily(), rendererType);
1115 if (renderer == null)
1116 {
1117 _LOG.warning("CANNOT_FIND_RENDERER", new Object[]{this, rendererType});
1118 }
1119
1120 return renderer;
1121 }
1122
1123 return null;
1124 }
1125
1126 private LifecycleRenderer _getLifecycleRendererImpl(FacesContext context)
1127 {
1128 Renderer renderer = _getRendererImpl(context);
1129 if (renderer instanceof LifecycleRenderer)
1130 {
1131 return (LifecycleRenderer)renderer;
1132 }
1133
1134 return null;
1135 }
1136
1137 @Override
1138 protected Renderer getRenderer(FacesContext context)
1139 {
1140 Renderer renderer = _cachedRenderer;
1141 if (renderer != _UNDEFINED_RENDERER)
1142 return renderer;
1143
1144 return _getRendererImpl(context);
1145 }
1146
1147 protected LifecycleRenderer getLifecycleRenderer(FacesContext context)
1148 {
1149 LifecycleRenderer renderer = _cachedLifecycleRenderer;
1150 if (renderer != _UNDEFINED_LIFECYCLE_RENDERER)
1151 return renderer;
1152
1153 return _getLifecycleRendererImpl(context);
1154
1155 }
1156
1157 protected void setProperty(PropertyKey key, Object value)
1158 {
1159 getFacesBean().setProperty(key, value);
1160 }
1161
1162 protected Object getProperty(PropertyKey key)
1163 {
1164 return getFacesBean().getProperty(key);
1165 }
1166
1167 protected void setBooleanProperty(PropertyKey key, boolean value)
1168 {
1169 getFacesBean().setProperty(key, value ? Boolean.TRUE : Boolean.FALSE);
1170 }
1171
1172 protected boolean getBooleanProperty(PropertyKey key, boolean defaultValue)
1173 {
1174 Object o = getFacesBean().getProperty(key);
1175 if (defaultValue)
1176 return !Boolean.FALSE.equals(o);
1177 else
1178 return Boolean.TRUE.equals(o);
1179 }
1180
1181 protected void setIntProperty(PropertyKey key, int value)
1182 {
1183 getFacesBean().setProperty(key, Integer.valueOf(value));
1184 }
1185
1186 protected int getIntProperty(PropertyKey key, int defaultValue)
1187 {
1188 Number n = (Number) getFacesBean().getProperty(key);
1189 if (n == null)
1190 return defaultValue;
1191
1192 return n.intValue();
1193 }
1194
1195
1196 /**
1197 * Return the number of facets. This is more efficient than
1198 * calling getFacets().size();
1199 */
1200 @Override
1201 public int getFacetCount()
1202 {
1203 if (_facets == null)
1204 return 0;
1205
1206 return _facets.size();
1207 }
1208
1209
1210 /**
1211 * Broadcast an event to a MethodBinding.
1212 * This can be used to support MethodBindings such as the "actionListener"
1213 * binding on ActionSource components:
1214 * <tr:commandButton actionListener="#{mybean.myActionListener}">
1215 * @deprecated
1216 */
1217 protected final void broadcastToMethodBinding(
1218 FacesEvent event,
1219 MethodBinding method) throws AbortProcessingException
1220 {
1221 if (method != null)
1222 {
1223 try
1224 {
1225 FacesContext context = getFacesContext();
1226 method.invoke(context, new Object[] { event });
1227 }
1228 catch (EvaluationException ee)
1229 {
1230 Throwable t = ee.getCause();
1231 // Unwrap AbortProcessingExceptions
1232 if (t instanceof AbortProcessingException)
1233 throw ((AbortProcessingException) t);
1234 throw ee;
1235 }
1236 }
1237 }
1238
1239 /**
1240 * Given a MethodBinding, create a MethodExpression that
1241 * adapts it.
1242 */
1243 static public MethodExpression adaptMethodBinding(MethodBinding binding)
1244 {
1245 return new MethodBindingMethodExpression(binding);
1246 }
1247
1248 /**
1249 * Broadcast an event to a MethodExpression.
1250 * This can be used to support MethodBindings such as the "actionListener"
1251 * binding on ActionSource components:
1252 * <tr:commandButton actionListener="#{mybean.myActionListener}">
1253 */
1254 protected final void broadcastToMethodExpression(
1255 FacesEvent event,
1256 MethodExpression method) throws AbortProcessingException
1257 {
1258 if (method != null)
1259 {
1260 try
1261 {
1262 FacesContext context = getFacesContext();
1263 method.invoke(context.getELContext(), new Object[] { event });
1264 }
1265 catch (ELException ee)
1266 {
1267 Throwable t = ee.getCause();
1268 // Unwrap AbortProcessingExceptions
1269 if (t instanceof AbortProcessingException)
1270 throw ((AbortProcessingException) t);
1271 throw ee;
1272 }
1273 }
1274 }
1275
1276
1277 /**
1278 * <p>
1279 * This gets a single threadlocal shared stringbuilder instance, each time you call
1280 * __getSharedStringBuilder it sets the length of the stringBuilder instance to 0.
1281 * </p><p>
1282 * This allows you to use the same StringBuilder instance over and over.
1283 * You must call toString on the instance before calling __getSharedStringBuilder again.
1284 * </p>
1285 * Example that works
1286 * <pre><code>
1287 * StringBuilder sb1 = __getSharedStringBuilder();
1288 * sb1.append(a).append(b);
1289 * String c = sb1.toString();
1290 *
1291 * StringBuilder sb2 = __getSharedStringBuilder();
1292 * sb2.append(b).append(a);
1293 * String d = sb2.toString();
1294 * </code></pre>
1295 * <br><br>
1296 * Example that doesn't work, you must call toString on sb1 before
1297 * calling __getSharedStringBuilder again.
1298 * <pre><code>
1299 * StringBuilder sb1 = __getSharedStringBuilder();
1300 * StringBuilder sb2 = __getSharedStringBuilder();
1301 *
1302 * sb1.append(a).append(b);
1303 * String c = sb1.toString();
1304 *
1305 * sb2.append(b).append(a);
1306 * String d = sb2.toString();
1307 * </code></pre>
1308 *
1309 */
1310 static StringBuilder __getSharedStringBuilder()
1311 {
1312 StringBuilder sb = _STRING_BUILDER.get();
1313
1314 if (sb == null)
1315 {
1316 sb = new StringBuilder();
1317 _STRING_BUILDER.set(sb);
1318 }
1319
1320 // clear out the stringBuilder by setting the length to 0
1321 sb.setLength(0);
1322
1323 return sb;
1324 }
1325
1326 /**
1327 * render a component. this is called by renderers whose
1328 * getRendersChildren() return true.
1329 */
1330 void __encodeRecursive(FacesContext context, UIComponent component)
1331 throws IOException
1332 {
1333 if (component.isRendered())
1334 {
1335 component.encodeBegin(context);
1336 if (component.getRendersChildren())
1337 {
1338 component.encodeChildren(context);
1339 }
1340 else
1341 {
1342 if (component.getChildCount() > 0)
1343 {
1344 for(UIComponent child : component.getChildren())
1345 {
1346 __encodeRecursive(context, child);
1347 }
1348 }
1349 }
1350
1351 component.encodeEnd(context);
1352 }
1353 }
1354
1355
1356 static private UIComponent _findInsideOf(
1357 UIComponent from,
1358 String id)
1359 {
1360 Iterator<UIComponent> kids = from.getFacetsAndChildren();
1361 while (kids.hasNext())
1362 {
1363 UIComponent kid = kids.next();
1364 if (id.equals(kid.getId()))
1365 return kid;
1366
1367 if (!(kid instanceof NamingContainer))
1368 {
1369 UIComponent returned = _findInsideOf(kid, id);
1370 if (returned != null)
1371 return returned;
1372 }
1373 }
1374
1375 return null;
1376 }
1377
1378 /**
1379 * <p>Verify that the specified component id is safe to add to the tree.
1380 * </p>
1381 *
1382 * @param id The proposed component id to check for validity
1383 *
1384 * @exception IllegalArgumentException if <code>id</code>
1385 * is <code>null</code> or contains invalid characters
1386 */
1387 private void _validateId(String id)
1388 {
1389 if (id == null)
1390 return;
1391
1392
1393 int n = id.length();
1394 if (0 == n ||
1395 NamingContainer.SEPARATOR_CHAR == id.charAt(0))
1396 _throwBadId(id);
1397
1398 for (int i = 0; i < n; i++)
1399 {
1400 char c = id.charAt(i);
1401 if (i == 0)
1402 {
1403 if (!Character.isLetter(c) && (c != '_'))
1404 _throwBadId(id);
1405 }
1406 else
1407 {
1408 if (!(Character.isLetter(c) ||
1409 Character.isDigit(c) ||
1410 (c == '-') || (c == '_')))
1411 {
1412 _throwBadId(id);
1413 }
1414 }
1415 }
1416 }
1417
1418 private void _throwBadId(String id)
1419 {
1420 throw new IllegalArgumentException(_LOG.getMessage(
1421 "ILLEGAL_ID", id));
1422 }
1423
1424 private void _init(
1425 String rendererType)
1426 {
1427 FacesBean oldBean = _facesBean;
1428 _facesBean = createFacesBean(rendererType);
1429 if (oldBean != null)
1430 _facesBean.addAll(oldBean);
1431
1432 _attributes = new ValueMap(_facesBean);
1433 }
1434
1435 private FacesBean _facesBean;
1436 private List<UIComponent> _children;
1437 private Map<String, Object> _attributes;
1438 private Map<String, UIComponent> _facets;
1439 private UIComponent _parent;
1440
1441 // Cached instance of the Renderer for this component.
1442 // The instance will be re-retrieved in encodeBegin()
1443 private transient Renderer _cachedRenderer = _UNDEFINED_RENDERER;
1444 private transient LifecycleRenderer _cachedLifecycleRenderer =
1445 _UNDEFINED_LIFECYCLE_RENDERER;
1446
1447 // -= Simon Lessard =-
1448 // FIXME: _initialStateMarked is never read
1449 // So commented out, is that ok? If so, this attribute should be deleted
1450 //private transient boolean _initialStateMarked;
1451
1452 private static final Iterator<String> _EMPTY_STRING_ITERATOR =
1453 new EmptyIterator<String>();
1454
1455 private static final Iterator<UIComponent> _EMPTY_UICOMPONENT_ITERATOR =
1456 new EmptyIterator<UIComponent>();
1457
1458
1459 static private final ThreadLocal<StringBuilder> _STRING_BUILDER =
1460 new ThreadLocal<StringBuilder>();
1461
1462 static private FacesBean.Type _createType()
1463 {
1464 try
1465 {
1466 ClassLoader cl = _getClassLoader();
1467 URL url = cl.getResource("META-INF/faces-bean-type.properties");
1468 if (url != null)
1469 {
1470 Properties properties = new Properties();
1471 InputStream is = url.openStream();
1472 try
1473 {
1474 properties.load(is);
1475 String className = (String)
1476 properties.get(UIXComponentBase.class.getName());
1477 return (FacesBean.Type) cl.loadClass(className).newInstance();
1478 }
1479 finally
1480 {
1481 is.close();
1482 }
1483 }
1484 }
1485 catch (Exception e)
1486 {
1487 _LOG.severe("CANNOT_LOAD_TYPE_PROPERTIES", e);
1488 }
1489
1490 // For testing purposes, return a valid Type
1491 return new FacesBean.Type();
1492 }
1493
1494 static private ClassLoader _getClassLoader()
1495 {
1496 ClassLoader loader = Thread.currentThread().getContextClassLoader();
1497 if (loader == null)
1498 loader = FacesBeanFactory.class.getClassLoader();
1499 return loader;
1500 }
1501
1502 static private class RendererImpl extends Renderer
1503 {
1504 }
1505
1506 static private class ExtendedRendererImpl extends ExtendedRenderer
1507 {
1508 }
1509
1510 private static class EmptyIterator<T> implements Iterator<T>
1511 {
1512 public boolean hasNext()
1513 {
1514 return false;
1515 }
1516
1517 public T next()
1518 {
1519 throw new NoSuchElementException();
1520 }
1521
1522 public void remove()
1523 {
1524 throw new UnsupportedOperationException();
1525 }
1526
1527 }
1528
1529 static private final LifecycleRenderer _UNDEFINED_LIFECYCLE_RENDERER =
1530 new ExtendedRendererImpl();
1531 static private final Renderer _UNDEFINED_RENDERER = new RendererImpl();
1532 }