1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5 *
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common Development
8 * and Distribution License("CDDL") (collectively, the "License"). You
9 * may not use this file except in compliance with the License. You can obtain
10 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
11 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
12 * language governing permissions and limitations under the License.
13 *
14 * When distributing the software, include this License Header Notice in each
15 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
16 * Sun designates this particular file as subject to the "Classpath" exception
17 * as provided by Sun in the GPL Version 2 section of the License file that
18 * accompanied this code. If applicable, add the following below the License
19 * Header, with the fields enclosed by brackets [] replaced by your own
20 * identifying information: "Portions Copyrighted [year]
21 * [name of copyright owner]"
22 *
23 * Contributor(s):
24 *
25 * If you wish your version of this file to be governed by only the CDDL or
26 * only the GPL Version 2, indicate your decision by adding "[Contributor]
27 * elects to include this software in this distribution under the [CDDL or GPL
28 * Version 2] license." If you don't indicate a single choice of license, a
29 * recipient has the option to distribute your version of this file under
30 * either the CDDL, the GPL Version 2 or to extend the choice of license to
31 * its licensees as provided above. However, if you add GPL Version 2 code
32 * and therefore, elected the GPL Version 2 license, then the option applies
33 * only if the new code is made subject to such option by the copyright
34 * holder.
35 */
36
37 package javax.faces.component;
38
39 import javax.el.ELException;
40 import javax.el.ValueExpression;
41 import javax.faces.FacesException;
42 import javax.faces.application.Application;
43 import javax.faces.component.behavior.Behavior;
44 import javax.faces.component.behavior.ClientBehavior;
45 import javax.faces.component.behavior.ClientBehaviorHolder;
46 import javax.faces.context.FacesContext;
47 import javax.faces.el.ValueBinding;
48 import javax.faces.event.AbortProcessingException;
49 import javax.faces.event.BehaviorEvent;
50 import javax.faces.event.FacesEvent;
51 import javax.faces.event.FacesListener;
52 import javax.faces.event.PostAddToViewEvent;
53 import javax.faces.event.PostValidateEvent;
54 import javax.faces.event.PreRemoveFromViewEvent;
55 import javax.faces.event.PreRenderComponentEvent;
56 import javax.faces.event.PreValidateEvent;
57 import javax.faces.event.SystemEvent;
58 import javax.faces.event.SystemEventListener;
59 import javax.faces.render.Renderer;
60 import java.beans.IntrospectionException;
61 import java.beans.Introspector;
62 import java.beans.PropertyDescriptor;
63 import java.io.IOException;
64 import java.io.ObjectInputStream;
65 import java.io.ObjectOutputStream;
66 import java.io.Serializable;
67 import java.lang.reflect.InvocationTargetException;
68 import java.lang.reflect.Method;
69 import java.lang.reflect.Array;
70 import java.util.AbstractCollection;
71 import java.util.AbstractMap;
72 import java.util.AbstractSet;
73 import java.util.ArrayList;
74 import java.util.Collection;
75 import java.util.Collections;
76 import java.util.HashMap;
77 import java.util.Iterator;
78 import java.util.List;
79 import java.util.ListIterator;
80 import java.util.Map;
81 import java.util.NoSuchElementException;
82 import java.util.Set;
83 import java.util.WeakHashMap;
84 import java.util.Map.Entry;
85 import java.util.logging.Level;
86 import java.util.logging.Logger;
87
88 /**
89 * <p><strong class="changed_modified_2_0">UIComponentBase</strong> is a
90 * convenience base class that implements the default concrete behavior
91 * of all methods defined by {@link UIComponent}.</p>
92 * <p/>
93 * <p>By default, this class defines <code>getRendersChildren()</code>
94 * to find the renderer for this component and call its
95 * <code>getRendersChildren()</code> method. The default implementation
96 * on the <code>Renderer</code> returns <code>false</code>. As of
97 * version 1.2 of the JavaServer Faces Specification, component authors
98 * are encouraged to return <code>true</code> from this method and rely
99 * on the implementation of {@link #encodeChildren} in this class and in
100 * the Renderer ({@link Renderer#encodeChildren}). Subclasses that wish
101 * to manage the rendering of their children should override this method
102 * to return <code>true</code> instead.</p>
103 */
104 public abstract class UIComponentBase extends UIComponent {
105
106
107 // -------------------------------------------------------------- Attributes
108
109 private static Logger LOGGER = Logger.getLogger("javax.faces.component",
110 "javax.faces.LogStrings");
111
112 private static final String ADDED = UIComponentBase.class.getName() + ".ADDED";
113
114
115 /**
116 * <p>Each entry is an map of <code>PropertyDescriptor</code>s describing
117 * the properties of a concrete {@link UIComponent} implementation, keyed
118 * by the corresponding <code>java.lang.Class</code>.</p>
119 * <p/>
120 * <p><strong>IMPLEMENTATION NOTE</strong> - This is implemented as a
121 * <code>WeakHashMap</code> so that, even if this class is embedded in a
122 * container's class loader that is a parent to webapp class loaders,
123 * references to the classes will eventually expire.</p>
124 */
125 @SuppressWarnings({"CollectionWithoutInitialCapacity"})
126 private static Map<Class<?>, Map<String, PropertyDescriptor>>
127 descriptors =
128 new WeakHashMap<Class<?>, Map<String, PropertyDescriptor>>();
129
130 /**
131 * Reference to the map of <code>PropertyDescriptor</code>s for this class
132 * in the <code>descriptors<code> <code>Map<code>.
133 */
134 private Map<String, PropertyDescriptor> pdMap = null;
135
136 /**
137 * <p>An EMPTY_OBJECT_ARRAY argument list to be passed to reflection methods.</p>
138 */
139 private static final Object EMPTY_OBJECT_ARRAY[] = new Object[0];
140
141 public UIComponentBase() {
142 populateDescriptorsMapIfNecessary();
143 }
144
145 private void populateDescriptorsMapIfNecessary() {
146 Class<?> clazz = this.getClass();
147 pdMap = descriptors.get(clazz);
148 if (null != pdMap) {
149 return;
150 }
151
152 // load the property descriptors for this class.
153 PropertyDescriptor pd[] = getPropertyDescriptors();
154 if (pd != null) {
155 pdMap = new HashMap<String, PropertyDescriptor>(pd.length, 1.0f);
156 for (PropertyDescriptor aPd : pd) {
157 pdMap.put(aPd.getName(), aPd);
158 }
159 if (LOGGER.isLoggable(Level.FINE)) {
160 LOGGER.log(Level.FINE, "fine.component.populating_descriptor_map",
161 new Object[]{clazz,
162 Thread.currentThread().getName()});
163 }
164
165 // Check again
166 Map<String, PropertyDescriptor> reCheckMap =
167 descriptors.get(clazz);
168 if (null != reCheckMap) {
169 return;
170 }
171 descriptors.put(clazz, pdMap);
172 }
173
174
175 }
176
177
178 /**
179 * <p>Return an array of <code>PropertyDescriptors</code> for this
180 * {@link UIComponent}'s implementation class. If no descriptors
181 * can be identified, a zero-length array will be returned.</p>
182 *
183 * @throws FacesException if an introspection exception occurs
184 */
185 private PropertyDescriptor[] getPropertyDescriptors() {
186 PropertyDescriptor[] pd;
187 try {
188 pd = Introspector.getBeanInfo(this.getClass()).
189 getPropertyDescriptors();
190 } catch (IntrospectionException e) {
191 throw new FacesException(e);
192 }
193 return (pd);
194 }
195
196
197 /**
198 * <p>The <code>Map</code> containing our attributes, keyed by
199 * attribute name.</p>
200 */
201 private AttributesMap attributes = null;
202
203
204 public Map<String, Object> getAttributes() {
205
206 if (attributes == null) {
207 attributes = new AttributesMap(this);
208 }
209 return (attributes);
210
211 }
212
213
214 // ---------------------------------------------------------------- Bindings
215
216
217 /**
218 * {@inheritDoc}
219 *
220 * @throws NullPointerException {@inheritDoc}
221 * @deprecated This has been replaced by {@link #getValueExpression}.
222 */
223 public ValueBinding getValueBinding(String name) {
224
225 if (name == null) {
226 throw new NullPointerException();
227 }
228 ValueBinding result = null;
229 ValueExpression ve;
230
231 if (null != (ve = getValueExpression(name))) {
232 // if the ValueExpression is an instance of our private
233 // wrapper class.
234 if (ve.getClass().equals(ValueExpressionValueBindingAdapter.class)) {
235 result = ((ValueExpressionValueBindingAdapter) ve).getWrapped();
236 } else {
237 // otherwise, this is a real ValueExpression. Wrap it
238 // in a ValueBinding.
239 result = new ValueBindingValueExpressionAdapter(ve);
240 }
241 }
242 return result;
243 }
244
245
246 /**
247 * {@inheritDoc}
248 *
249 * @throws IllegalArgumentException {@inheritDoc}
250 * @throws NullPointerException {@inheritDoc}
251 * @deprecated This has been replaced by {@link #setValueExpression}.
252 */
253 public void setValueBinding(String name, ValueBinding binding) {
254 if (name == null) {
255 throw new NullPointerException();
256 }
257 if (binding != null) {
258 ValueExpressionValueBindingAdapter adapter =
259 new ValueExpressionValueBindingAdapter(binding);
260 setValueExpression(name, adapter);
261 } else {
262 setValueExpression(name, null);
263 }
264
265 }
266
267
268 // -------------------------------------------------------------- Properties
269
270
271 /**
272 * <p>The assigned client identifier for this component.</p>
273 */
274 private String clientId = null;
275
276
277 /**
278 * @throws NullPointerException {@inheritDoc}
279 */
280 public String getClientId(FacesContext context) {
281
282 if (context == null) {
283 throw new NullPointerException();
284 }
285
286 // if the clientId is not yet set
287 if (this.clientId == null) {
288 UIComponent namingContainerAncestor =
289 this.getNamingContainerAncestor();
290 UIComponent parent = namingContainerAncestor;
291 String parentId = null;
292
293 // give the parent the opportunity to first
294 // grab a unique clientId
295 if (parent != null) {
296 parentId = parent.getContainerClientId(context);
297 }
298
299 // now resolve our own client id
300 this.clientId = getId();
301 if (this.clientId == null) {
302 String generatedId;
303 if (null != namingContainerAncestor &&
304 namingContainerAncestor instanceof UniqueIdVendor) {
305 generatedId = ((UniqueIdVendor)namingContainerAncestor).createUniqueId(context, null);
306 }
307 else {
308 generatedId = context.getViewRoot().createUniqueId();
309 }
310 setId(generatedId);
311 this.clientId = getId();
312 }
313 if (parentId != null) {
314 StringBuilder idBuilder =
315 new StringBuilder(parentId.length()
316 + 1
317 + this.clientId.length());
318 this.clientId = idBuilder.append(parentId)
319 .append(UINamingContainer.getSeparatorChar(context))
320 .append(this.clientId).toString();
321 }
322
323 // allow the renderer to convert the clientId
324 Renderer renderer = this.getRenderer(context);
325 if (renderer != null) {
326 this.clientId = renderer.convertClientId(context, this.clientId);
327 }
328 }
329 return this.clientId;
330 }
331
332 /**
333 * <p>The component identifier for this component.</p>
334 */
335 private String id = null;
336
337
338 public String getId() {
339
340 return (id);
341
342 }
343
344 private UIComponent getNamingContainerAncestor() {
345 UIComponent namingContainer = this.getParent();
346 while (namingContainer != null) {
347 if (namingContainer instanceof NamingContainer) {
348 return namingContainer;
349 }
350 namingContainer = namingContainer.getParent();
351 }
352 return null;
353 }
354
355
356 /**
357 * @throws IllegalArgumentException {@inheritDoc}
358 * @throws IllegalStateException {@inheritDoc}
359 */
360 public void setId(String id) {
361
362 // if the current ID is not null, and the passed
363 // argument is the same, no need to validate it
364 // as it has already been validated.
365 if (this.id == null || !(this.id.equals(id))) {
366 validateId(id);
367 this.id = id;
368 }
369
370 this.clientId = null; // Erase any cached value
371
372 }
373
374
375 /**
376 * <p>The parent component for this component.</p>
377 */
378 private UIComponent parent = null;
379
380
381 public UIComponent getParent() {
382 return (this.parent);
383 }
384
385
386 public void setParent(UIComponent parent) {
387
388
389 if (parent == null) {
390 if (this.parent != null) {
391 doPreRemoveProcessing(FacesContext.getCurrentInstance(), this);
392 this.parent = parent;
393 }
394 compositeParent = null;
395 } else {
396 this.parent = parent;
397 if (this.getAttributes().get(ADDED) == null) {
398 // add an attribute to this component here to indiciate that
399 // it's being processed. If we don't do this, and the component
400 // is re-parented, the events could fire again in certain cases
401 // and cause a stack overflow.
402 this.getAttributes().put(ADDED, Boolean.TRUE);
403 doPostAddProcessing(FacesContext.getCurrentInstance(), this);
404 // remove the attribute once we've returned from the event
405 // processing.
406 this.getAttributes().remove(ADDED);
407 }
408 }
409
410 }
411
412
413
414 public boolean isRendered() {
415
416 return Boolean.valueOf(getStateHelper().eval(PropertyKeys.rendered, Boolean.TRUE).toString());
417 }
418
419
420 public void setRendered(boolean rendered) {
421 getStateHelper().put(PropertyKeys.rendered, rendered);
422 }
423
424
425 public String getRendererType() {
426
427 return (String) getStateHelper().eval(PropertyKeys.rendererType);
428
429 }
430
431
432 public void setRendererType(String rendererType) {
433
434 getStateHelper().put(PropertyKeys.rendererType, rendererType);
435
436 }
437
438
439 public boolean getRendersChildren() {
440 boolean result = false;
441
442 Renderer renderer;
443 if (getRendererType() != null) {
444 if (null !=
445 (renderer = getRenderer(getFacesContext()))) {
446 result = renderer.getRendersChildren();
447 }
448 }
449 return result;
450
451 }
452
453 // ------------------------------------------------- Tree Management Methods
454
455
456 /*
457 * <p>The <code>List</code> containing our child components.</p>
458 */
459 private List<UIComponent> children = null;
460
461
462 public List<UIComponent> getChildren() {
463
464 if (children == null) {
465 children = new ChildrenList(this);
466 }
467 return (children);
468
469 }
470
471
472 // Do not allocate the children List to answer this question
473 public int getChildCount() {
474
475 if (children != null) {
476 return (children.size());
477 } else {
478 return (0);
479 }
480
481 }
482
483
484 /**
485 * <p>If the specified {@link UIComponent} has a non-null parent,
486 * remove it as a child or facet (as appropriate) of that parent.
487 * As a result, the <code>parent</code> property will always be
488 * <code>null</code> when this method returns.</p>
489 *
490 * @param component {@link UIComponent} to have any parent erased
491 */
492 private static void eraseParent(UIComponent component) {
493
494 UIComponent parent = component.getParent();
495 if (parent == null) {
496 return;
497 }
498 if (parent.getChildCount() > 0) {
499 List children = parent.getChildren();
500 int index = children.indexOf(component);
501 if (index >= 0) {
502 children.remove(index);
503 return;
504 }
505 }
506 if (parent.getFacetCount() > 0) {
507 Map facets = parent.getFacets();
508 Iterator entries = facets.entrySet().iterator();
509 while (entries.hasNext()) {
510 Map.Entry entry = (Map.Entry) entries.next();
511 //noinspection ObjectEquality
512 if (entry.getValue() == component) {
513 entries.remove();
514 return;
515 }
516 }
517 }
518
519 // Throw an exception for the "cannot happen" case
520 throw new IllegalStateException("Parent was not null, " +
521 "but this component not related");
522
523 }
524
525 /**
526 * <p>Throw <code>IllegalArgumentException</code> if the specified
527 * component identifier is non-<code>null</code> and not
528 * syntactically valid. </p>
529 *
530 * @param id The component identifier to test
531 */
532 private static void validateId(String id) {
533
534 if (id == null) {
535 return;
536 }
537 int n = id.length();
538 if (n < 1) {
539 throw new IllegalArgumentException("Empty id attribute is not allowed");
540 }
541 for (int i = 0; i < n; i++) {
542 char c = id.charAt(i);
543 if (i == 0) {
544 if (!Character.isLetter(c) && (c != '_')) {
545 throw new IllegalArgumentException(id);
546 }
547 } else {
548 if (!Character.isLetter(c) &&
549 !Character.isDigit(c) &&
550 (c != '-') && (c != '_')) {
551 throw new IllegalArgumentException(id);
552 }
553 }
554 }
555
556 }
557
558
559 /**
560 * @throws NullPointerException {@inheritDoc}
561 */
562 public UIComponent findComponent(String expr) {
563 if (expr == null) {
564 throw new NullPointerException();
565 }
566
567 FacesContext ctx = FacesContext.getCurrentInstance();
568 final char sepChar = UINamingContainer.getSeparatorChar(ctx);
569 final String SEPARATOR_STRING = String.valueOf(sepChar);
570
571 if (expr.length() == 0) {
572 // if an empty value is provided, fail fast.
573 throw new IllegalArgumentException("\"\"");
574 }
575
576 // Identify the base component from which we will perform our search
577 UIComponent base = this;
578 if (expr.charAt(0) == sepChar) {
579 // Absolute searches start at the root of the tree
580 while (base.getParent() != null) {
581 base = base.getParent();
582 }
583 // Treat remainder of the expression as relative
584 expr = expr.substring(1);
585 } else if (!(base instanceof NamingContainer)) {
586 // Relative expressions start at the closest NamingContainer or root
587 while (base.getParent() != null) {
588 if (base instanceof NamingContainer) {
589 break;
590 }
591 base = base.getParent();
592 }
593 }
594
595 // Evaluate the search expression (now guaranteed to be relative)
596 UIComponent result = null;
597 String[] segments = expr.split(SEPARATOR_STRING);
598 for (int i = 0, length = (segments.length - 1);
599 i < segments.length;
600 i++, length--) {
601 result = findComponent(base, segments[i], (i == 0));
602 // the first element of the expression may match base.id
603 // (vs. a child if of base)
604 if (i == 0 && result == null &&
605 segments[i].equals(base.getId())) {
606 result = base;
607 }
608 if (result != null && (!(result instanceof NamingContainer)) && length > 0) {
609 throw new IllegalArgumentException(segments[i]);
610 }
611 if (result == null) {
612 break;
613 }
614 base = result;
615 }
616
617 // Return the final result of our search
618 return (result);
619
620 }
621
622
623 /**
624 * <p>Return the {@link UIComponent} (if any) with the specified
625 * <code>id</code>, searching recursively starting at the specified
626 * <code>base</code>, and examining the base component itself, followed
627 * by examining all the base component's facets and children (unless
628 * the base component is a {@link NamingContainer}, in which case the
629 * recursive scan is skipped.</p>
630 *
631 * @param base Base {@link UIComponent} from which to search
632 * @param id Component identifier to be matched
633 */
634 private static UIComponent findComponent(UIComponent base,
635 String id,
636 boolean checkId) {
637 if (checkId && id.equals(base.getId())) {
638 return base;
639 }
640 // Search through our facets and children
641 UIComponent result = null;
642 for (Iterator i = base.getFacetsAndChildren(); i.hasNext();) {
643 UIComponent kid = (UIComponent) i.next();
644 if (!(kid instanceof NamingContainer)) {
645 if (checkId && id.equals(kid.getId())) {
646 result = kid;
647 break;
648 }
649 result = findComponent(kid, id, true);
650 if (result != null) {
651 break;
652 }
653 } else if (id.equals(kid.getId())) {
654 result = kid;
655 break;
656 }
657 }
658 return (result);
659
660 }
661
662 /**
663 * {@inheritDoc}
664 *
665 * @throws NullPointerException {@inheritDoc}
666 * @throws FacesException {@inheritDoc}
667 * @since 1.2
668 */
669 public boolean invokeOnComponent(FacesContext context, String clientId,
670 ContextCallback callback)
671 throws FacesException {
672 return super.invokeOnComponent(context, clientId, callback);
673 }
674
675
676 // ------------------------------------------------ Facet Management Methods
677
678
679 /*
680 * <p>The <code>Map</code> containing our related facet components.</p>
681 */
682 private Map<String, UIComponent> facets = null;
683
684
685 public Map<String, UIComponent> getFacets() {
686
687 if (facets == null) {
688 facets = new FacetsMap(this);
689 }
690 return (facets);
691
692 }
693
694 // Do not allocate the children List to answer this question
695 public int getFacetCount() {
696
697 if (facets != null) {
698 return (facets.size());
699 } else {
700 return (0);
701 }
702
703 }
704
705
706 // Do not allocate the facets Map to answer this question
707 public UIComponent getFacet(String name) {
708
709 if (facets != null) {
710 return (facets.get(name));
711 } else {
712 return (null);
713 }
714
715 }
716
717
718 public Iterator<UIComponent> getFacetsAndChildren() {
719
720 Iterator<UIComponent> result;
721 int childCount = this.getChildCount(),
722 facetCount = this.getFacetCount();
723 // If there are neither facets nor children
724 if (0 == childCount && 0 == facetCount) {
725 result = EMPTY_ITERATOR;
726 }
727 // If there are only facets and no children
728 else if (0 == childCount) {
729 Collection<UIComponent> unmodifiable =
730 Collections.unmodifiableCollection(getFacets().values());
731 result = unmodifiable.iterator();
732 }
733 // If there are only children and no facets
734 else if (0 == facetCount) {
735 List<UIComponent> unmodifiable =
736 Collections.unmodifiableList(getChildren());
737 result = unmodifiable.iterator();
738 }
739 // If there are both children and facets
740 else {
741 result = new FacetsAndChildrenIterator(this);
742 }
743 return result;
744 }
745
746
747 // -------------------------------------------- Lifecycle Processing Methods
748
749 /**
750 * @throws AbortProcessingException {@inheritDoc}
751 * @throws IllegalStateException {@inheritDoc}
752 * @throws NullPointerException {@inheritDoc}
753 */
754 public void broadcast(FacesEvent event)
755 throws AbortProcessingException {
756
757 if (event == null) {
758 throw new NullPointerException();
759 }
760 if (event instanceof BehaviorEvent) {
761 BehaviorEvent behaviorEvent = (BehaviorEvent) event;
762 Behavior behavior = behaviorEvent.getBehavior();
763 behavior.broadcast(behaviorEvent);
764 }
765
766 if (listeners == null) {
767 return;
768 }
769
770 for (FacesListener listener : listeners.asArray(FacesListener.class)) {
771 if (event.isAppropriateListener(listener)) {
772 event.processListener(listener);
773 }
774 }
775 }
776
777
778 /**
779 * @throws NullPointerException {@inheritDoc}
780 */
781 public void decode(FacesContext context) {
782
783 if (context == null) {
784 throw new NullPointerException();
785 }
786 String rendererType = getRendererType();
787 if (rendererType != null) {
788 Renderer renderer = this.getRenderer(context);
789 if (renderer != null) {
790 renderer.decode(context, this);
791 } else {
792 if (LOGGER.isLoggable(Level.FINE)) {
793 LOGGER.fine("Can't get Renderer for type " + rendererType);
794 }
795 }
796 }
797 }
798
799
800 /**
801 * @throws NullPointerException {@inheritDoc}
802 */
803 public void encodeBegin(FacesContext context) throws IOException {
804
805 if (context == null) {
806 throw new NullPointerException();
807 }
808
809 pushComponentToEL(context, null);
810
811 if (!isRendered()) {
812 return;
813 }
814
815 context.getApplication().publishEvent(context,
816 PreRenderComponentEvent.class,
817 this);
818
819 String rendererType = getRendererType();
820 if (rendererType != null) {
821 Renderer renderer = this.getRenderer(context);
822 if (renderer != null) {
823 renderer.encodeBegin(context, this);
824 } else {
825 if (LOGGER.isLoggable(Level.FINE)) {
826 LOGGER.fine("Can't get Renderer for type " + rendererType);
827 }
828 }
829 }
830
831 }
832
833 /**
834 * @throws NullPointerException {@inheritDoc}
835 */
836 public void encodeChildren(FacesContext context) throws IOException {
837
838 if (context == null) {
839 throw new NullPointerException();
840 }
841 if (!isRendered()) {
842 return;
843 }
844 String rendererType = getRendererType();
845 if (rendererType != null) {
846 Renderer renderer = this.getRenderer(context);
847 if (renderer != null) {
848 renderer.encodeChildren(context, this);
849 }
850 // We've already logged for this component
851 } else {
852 if (getChildCount() > 0) {
853 for (UIComponent child : getChildren()) {
854 child.encodeAll(context);
855 }
856 }
857 }
858 }
859
860
861 /**
862 * @throws IOException {@inheritDoc}
863 * @throws NullPointerException {@inheritDoc}
864 */
865 public void encodeEnd(FacesContext context) throws IOException {
866
867 if (context == null) {
868 throw new NullPointerException();
869 }
870 if (!isRendered()) {
871 popComponentFromEL(context);
872 return;
873 }
874 String rendererType = getRendererType();
875 if (rendererType != null) {
876 Renderer renderer = this.getRenderer(context);
877 if (renderer != null) {
878 renderer.encodeEnd(context, this);
879 } else {
880 // We've already logged for this component
881 }
882 }
883 popComponentFromEL(context);
884
885 }
886
887 // -------------------------------------------------- Event Listener Methods
888
889 private AttachedObjectListHolder<FacesListener> listeners;
890
891 /**
892 * <p>Add the specified {@link FacesListener} to the set of listeners
893 * registered to receive event notifications from this {@link UIComponent}.
894 * It is expected that {@link UIComponent} classes acting as event sources
895 * will have corresponding typesafe APIs for registering listeners of the
896 * required type, and the implementation of those registration methods
897 * will delegate to this method. For example:</p>
898 * <pre>
899 * public class FooEvent extends FacesEvent {
900 * ...
901 * protected boolean isAppropriateListener(FacesListener listener) {
902 * return (listener instanceof FooListener);
903 * }
904 * protected void processListener(FacesListener listener) {
905 * ((FooListener) listener).processFoo(this);
906 * }
907 * ...
908 * }
909 * <p/>
910 * public interface FooListener extends FacesListener {
911 * public void processFoo(FooEvent event);
912 * }
913 * <p/>
914 * public class FooComponent extends UIComponentBase {
915 * ...
916 * public void addFooListener(FooListener listener) {
917 * addFacesListener(listener);
918 * }
919 * public void removeFooListener(FooListener listener) {
920 * removeFacesListener(listener);
921 * }
922 * ...
923 * }
924 * </pre>
925 *
926 * @param listener The {@link FacesListener} to be registered
927 * @throws NullPointerException if <code>listener</code>
928 * is <code>null</code>
929 */
930 protected void addFacesListener(FacesListener listener) {
931
932 if (listener == null) {
933 throw new NullPointerException();
934 }
935
936 if (listeners == null) {
937 listeners = new AttachedObjectListHolder<FacesListener>();
938 }
939
940 listeners.add(listener);
941
942 }
943
944
945 /**
946 * @throws IllegalArgumentException {@inheritDoc}
947 * @throws NullPointerException {@inheritDoc}
948 */
949 protected FacesListener[] getFacesListeners(Class clazz) {
950
951 if (clazz == null) {
952 throw new NullPointerException();
953 }
954 if (!FacesListener.class.isAssignableFrom(clazz)) {
955 throw new IllegalArgumentException();
956 }
957
958 if (this.listeners == null) {
959 return (FacesListener[]) Array.newInstance(clazz, 0);
960 }
961 FacesListener[] listeners = this.listeners.asArray(FacesListener.class);
962 if (listeners.length == 0) {
963 return (FacesListener[]) Array.newInstance(clazz, 0);
964 }
965
966 List<FacesListener> results = new ArrayList<FacesListener>(listeners.length);
967 for (FacesListener listener : listeners) {
968 if (((Class<?>) clazz).isAssignableFrom(listener.getClass())) {
969 results.add(listener);
970 }
971 }
972
973 return (results.toArray
974 ((FacesListener[]) Array.newInstance(clazz,
975 results.size())));
976
977 }
978
979
980 /**
981 * <p>Remove the specified {@link FacesListener} from the set of listeners
982 * registered to receive event notifications from this {@link UIComponent}.
983 *
984 * @param listener The {@link FacesListener} to be deregistered
985 * @throws NullPointerException if <code>listener</code>
986 * is <code>null</code>
987 */
988 protected void removeFacesListener(FacesListener listener) {
989
990 if (listener == null) {
991 throw new NullPointerException();
992 }
993
994 if (listeners != null) {
995 listeners.remove(listener);
996 }
997
998 }
999
1000 /**
1001 * @throws IllegalStateException {@inheritDoc}
1002 * @throws NullPointerException {@inheritDoc}
1003 */
1004 public void queueEvent(FacesEvent event) {
1005
1006 if (event == null) {
1007 throw new NullPointerException();
1008 }
1009 UIComponent parent = getParent();
1010 if (parent == null) {
1011 throw new IllegalStateException();
1012 } else {
1013 parent.queueEvent(event);
1014 }
1015
1016 }
1017
1018
1019 // ------------------------------------------------ Lifecycle Phase Handlers
1020
1021
1022 /**
1023 * @throws NullPointerException {@inheritDoc}
1024 */
1025 public void processDecodes(FacesContext context) {
1026
1027 if (context == null) {
1028 throw new NullPointerException();
1029 }
1030
1031 // Skip processing if our rendered flag is false
1032 if (!isRendered()) {
1033 return;
1034 }
1035
1036 pushComponentToEL(context, null);
1037
1038 // Process all facets and children of this component
1039 Iterator kids = getFacetsAndChildren();
1040 while (kids.hasNext()) {
1041 UIComponent kid = (UIComponent) kids.next();
1042 kid.processDecodes(context);
1043 }
1044
1045 // Process this component itself
1046 try {
1047 decode(context);
1048 } catch (RuntimeException e) {
1049 context.renderResponse();
1050 throw e;
1051 } finally {
1052 popComponentFromEL(context);
1053 }
1054
1055 }
1056
1057
1058 /**
1059 * @throws NullPointerException {@inheritDoc}
1060 */
1061 public void processValidators(FacesContext context) {
1062
1063 if (context == null) {
1064 throw new NullPointerException();
1065 }
1066
1067 // Skip processing if our rendered flag is false
1068 if (!isRendered()) {
1069 return;
1070 }
1071
1072 pushComponentToEL(context, null);
1073
1074 Application app = context.getApplication();
1075 app.publishEvent(context, PreValidateEvent.class, this);
1076 // Process all the facets and children of this component
1077 Iterator kids = getFacetsAndChildren();
1078 while (kids.hasNext()) {
1079 UIComponent kid = (UIComponent) kids.next();
1080 kid.processValidators(context);
1081 }
1082 app.publishEvent(context, PostValidateEvent.class, this);
1083 popComponentFromEL(context);
1084
1085 }
1086
1087
1088 /**
1089 * @throws NullPointerException {@inheritDoc}
1090 */
1091 public void processUpdates(FacesContext context) {
1092
1093 if (context == null) {
1094 throw new NullPointerException();
1095 }
1096
1097 // Skip processing if our rendered flag is false
1098 if (!isRendered()) {
1099 return;
1100 }
1101
1102 pushComponentToEL(context, null);
1103
1104 // Process all facets and children of this component
1105 Iterator kids = getFacetsAndChildren();
1106 while (kids.hasNext()) {
1107 UIComponent kid = (UIComponent) kids.next();
1108 kid.processUpdates(context);
1109
1110 }
1111 popComponentFromEL(context);
1112
1113 }
1114
1115 private static final int MY_STATE = 0;
1116 private static final int CHILD_STATE = 1;
1117
1118 /**
1119 * @throws NullPointerException {@inheritDoc}
1120 */
1121 public Object processSaveState(FacesContext context) {
1122
1123 if (context == null) {
1124 throw new NullPointerException();
1125 }
1126 if (this.isTransient()) {
1127 return null;
1128 }
1129 Object[] stateStruct = new Object[2];
1130 Object[] childState = EMPTY_ARRAY;
1131
1132 pushComponentToEL(context, null);
1133
1134 // Process this component itself
1135 stateStruct[MY_STATE] = saveState(context);
1136
1137 // determine if we have any children to store
1138 int count = this.getChildCount() + this.getFacetCount();
1139 if (count > 0) {
1140
1141 // this arraylist will store state
1142 List<Object> stateList = new ArrayList<Object>(count);
1143
1144 // if we have children, add them to the stateList
1145 if (this.getChildCount() > 0) {
1146 Iterator kids = getChildren().iterator();
1147 UIComponent kid;
1148 while (kids.hasNext()) {
1149 kid = (UIComponent) kids.next();
1150 if (!kid.isTransient()) {
1151 stateList.add(kid.processSaveState(context));
1152 popComponentFromEL(context);
1153 }
1154 }
1155 }
1156
1157 pushComponentToEL(context, null);
1158
1159 // if we have facets, add them to the stateList
1160 if (this.getFacetCount() > 0) {
1161 Iterator myFacets = getFacets().entrySet().iterator();
1162 UIComponent facet;
1163 Object facetState;
1164 Object[] facetSaveState;
1165 Map.Entry entry;
1166 while (myFacets.hasNext()) {
1167 entry = (Map.Entry) myFacets.next();
1168 facet = (UIComponent) entry.getValue();
1169 if (!facet.isTransient()) {
1170 facetState = facet.processSaveState(context);
1171 popComponentFromEL(context);
1172 facetSaveState = new Object[2];
1173 facetSaveState[0] = entry.getKey();
1174 facetSaveState[1] = facetState;
1175 stateList.add(facetSaveState);
1176 }
1177 }
1178 }
1179
1180 // finally, capture the stateList and replace the original,
1181 // EMPTY_OBJECT_ARRAY Object array
1182 childState = stateList.toArray();
1183 }
1184
1185 stateStruct[CHILD_STATE] = childState;
1186 return stateStruct;
1187 }
1188
1189 /**
1190 * @throws NullPointerException {@inheritDoc}
1191 */
1192 public void processRestoreState(FacesContext context,
1193 Object state) {
1194 if (context == null) {
1195 throw new NullPointerException();
1196 }
1197
1198 Object[] stateStruct = (Object[]) state;
1199 Object[] childState = (Object[]) stateStruct[CHILD_STATE];
1200
1201 // Process this component itself
1202 restoreState(context, stateStruct[MY_STATE]);
1203
1204 int i = 0;
1205
1206 // Process all the children of this component
1207 if (this.getChildCount() > 0) {
1208 for (UIComponent kid : getChildren()) {
1209 if (kid.isTransient()) {
1210 continue;
1211 }
1212 Object currentState = childState[i++];
1213 if (currentState == null) {
1214 continue;
1215 }
1216 pushComponentToEL(context, null);
1217 kid.processRestoreState(context, currentState);
1218 popComponentFromEL(context);
1219 }
1220 }
1221
1222 // process all of the facets of this component
1223 if (this.getFacetCount() > 0) {
1224 int facetsSize = getFacets().size();
1225 int j = 0;
1226 Object[] facetSaveState;
1227 String facetName;
1228 UIComponent facet;
1229 Object facetState;
1230 while (j < facetsSize) {
1231 if (null != (facetSaveState = (Object[]) childState[i++])) {
1232 facetName = (String) facetSaveState[0];
1233 facetState = facetSaveState[1];
1234 facet = getFacets().get(facetName);
1235 pushComponentToEL(context, null);
1236 facet.processRestoreState(context, facetState);
1237 popComponentFromEL(context);
1238 }
1239 ++j;
1240 }
1241 }
1242 }
1243
1244 // ------------------------------------------------------- Protected Methods
1245
1246 protected FacesContext getFacesContext() {
1247
1248 // PENDING(edburns): we can't use the cache ivar because we
1249 // don't always know when to clear it. For example, in the
1250 // "save state in server" case, the UIComponent instances stick
1251 // around between requests, yielding stale facesContext
1252 // references. If there was some way to clear the facesContext
1253 // cache ivar for each node in the tree *after* the
1254 // render-response phase, then we could keep a cache ivar. As
1255 // it is now, we must always use the Thread Local Storage
1256 // solution.
1257
1258 return FacesContext.getCurrentInstance();
1259
1260 }
1261
1262
1263 protected Renderer getRenderer(FacesContext context) {
1264
1265 String rendererType = getRendererType();
1266 Renderer result = null;
1267 if (rendererType != null) {
1268 result = context.getRenderKit().getRenderer(getFamily(),
1269 rendererType);
1270 if (null == result) {
1271 if (LOGGER.isLoggable(Level.FINE)) {
1272 LOGGER.fine("Can't get Renderer for type " + rendererType);
1273 }
1274 }
1275 } else {
1276 if (LOGGER.isLoggable(Level.FINE)) {
1277 String id = this.getId();
1278 id = (null != id) ? id : this.getClass().getName();
1279 LOGGER.fine("No renderer-type for component " + id);
1280 }
1281 }
1282 return result;
1283 }
1284
1285
1286 // ---------------------------------------------- PartialStateHolder Methods
1287
1288 /**
1289 * <p class="changed_added_2_0">For each of the attached objects on
1290 * this instance that implement {@link PartialStateHolder}, call
1291 * {@link PartialStateHolder#markInitialState} on the attached object.</p>
1292 * @since 2.0
1293 */
1294 @Override
1295 public void markInitialState() {
1296 super.markInitialState();
1297
1298 if (listeners != null) {
1299 listeners.markInitialState();
1300 }
1301 if (listenersByEventClass != null) {
1302 for (List<SystemEventListener> listener : listenersByEventClass.values()) {
1303 if (listener instanceof PartialStateHolder) {
1304 ((PartialStateHolder) listener).markInitialState();
1305 }
1306 }
1307 }
1308 if (behaviors != null) {
1309 for (Entry<String, List<ClientBehavior>> entry : behaviors.entrySet()) {
1310 for (ClientBehavior behavior : entry.getValue()) {
1311 if (behavior instanceof PartialStateHolder) {
1312 ((PartialStateHolder) behavior).markInitialState();
1313 }
1314 }
1315 }
1316 }
1317 }
1318
1319
1320 /**
1321 * <p class="changed_added_2_0">For each of the attached objects on
1322 * this instance that implement {@link PartialStateHolder}, call
1323 * {@link PartialStateHolder#clearInitialState} on the attached object.</p>
1324 * @since 2.0
1325 */
1326 @Override
1327 public void clearInitialState() {
1328 super.clearInitialState();
1329 if (listeners != null) {
1330 listeners.clearInitialState();
1331 }
1332 if (listenersByEventClass != null) {
1333 for (List<SystemEventListener> listener : listenersByEventClass.values()) {
1334 if (listener instanceof PartialStateHolder) {
1335 ((PartialStateHolder) listener).clearInitialState();
1336 }
1337 }
1338 }
1339 if (behaviors != null) {
1340 for (Entry<String, List<ClientBehavior>> entry : behaviors.entrySet()) {
1341 for (ClientBehavior behavior : entry.getValue()) {
1342 if (behavior instanceof PartialStateHolder) {
1343 ((PartialStateHolder) behavior).clearInitialState();
1344 }
1345 }
1346 }
1347 }
1348 }
1349
1350 private Object[] values;
1351
1352 public Object saveState(FacesContext context) {
1353
1354 if (context == null) {
1355 throw new NullPointerException();
1356 }
1357 assert (!transientFlag);
1358 if (initialStateMarked()) {
1359 Object savedFacesListeners = ((listeners != null) ? listeners.saveState(context) : null);
1360 Object savedSysEventListeners = saveSystemEventListeners(context);
1361 Object savedBehaviors = saveBehaviorsState(context);
1362 Object savedBindings = null;
1363 if (bindings != null) {
1364 savedBindings = saveBindingsState(context);
1365 }
1366 Object savedHelper = null;
1367 if (stateHelper != null) {
1368 savedHelper = stateHelper.saveState(getFacesContext());
1369 }
1370 if (savedFacesListeners == null
1371 && savedSysEventListeners == null
1372 && savedBehaviors == null
1373 && savedBindings == null
1374 && savedHelper == null) {
1375 return null;
1376 } else {
1377 if (values == null || values.length != 5) {
1378 values = new Object[5];
1379 }
1380
1381 // since we're saving partial state, skip id and clientId
1382 // as this will be reconstructed from the template execution
1383 // when the view is restored
1384 values[0] = savedFacesListeners;
1385 values[1] = savedSysEventListeners;
1386 values[2] = savedBehaviors;
1387 values[3] = savedBindings;
1388 values[4] = savedHelper;
1389 return values;
1390 }
1391 } else {
1392 if (values == null || values.length != 6) {
1393 values = new Object[6];
1394 }
1395
1396 values[0] = ((listeners != null) ? listeners.saveState(context) : null);
1397 values[1] = saveSystemEventListeners(context);
1398 values[2] = saveBehaviorsState(context);
1399 if (bindings != null) {
1400 values[3] = saveBindingsState(context);
1401 }
1402 if (stateHelper != null) {
1403 values[4] = stateHelper.saveState(getFacesContext());
1404 }
1405 values[5] = id;
1406
1407 return (values);
1408 }
1409 }
1410
1411
1412 public void restoreState(FacesContext context, Object state) {
1413
1414 if (context == null) {
1415 throw new NullPointerException();
1416 }
1417
1418 if (state == null) {
1419 return;
1420 }
1421 values = (Object[]) state;
1422
1423 if (values[0] != null) {
1424 if (listeners == null) {
1425 listeners = new AttachedObjectListHolder<FacesListener>();
1426 }
1427 listeners.restoreState(context, values[0]);
1428 }
1429 if (values[1] != null) {
1430 Map m = restoreSystemEventListeners(context, values[1]);
1431 if (listenersByEventClass != null) {
1432 listenersByEventClass.putAll(m);
1433 } else {
1434 listenersByEventClass = m;
1435 }
1436 }
1437 if (values[2] != null) {
1438 behaviors = restoreBehaviorsState(context, values[2]);
1439 }
1440 if (values[3] != null) {
1441 bindings = restoreBindingsState(context, values[3]);
1442 }
1443 if(values[4] != null) {
1444 getStateHelper().restoreState(getFacesContext(), values[4]);
1445 }
1446 if (values.length == 6) {
1447 // this means we've saved full state and need to do a little more
1448 // work to finish the job
1449 if (values[5] != null) {
1450 id = (String) values[5];
1451 }
1452 }
1453
1454 }
1455
1456
1457 /**
1458 * <p>Flag indicating a desire to now participate in state saving.</p>
1459 */
1460 private boolean transientFlag = false;
1461
1462 public boolean isTransient() {
1463
1464 return (this.transientFlag);
1465
1466 }
1467
1468
1469 public void setTransient(boolean transientFlag) {
1470
1471 this.transientFlag = transientFlag;
1472
1473 }
1474
1475 // -------------------------------------- Helper methods for state saving
1476
1477 // --------- methods used by UIComponents to save their attached Objects.
1478
1479 /**
1480 * <p class="changed_modified_2_0">This method is called by {@link
1481 * UIComponent} subclasses that want to save one or more attached
1482 * objects. It is a convenience method that does the work of saving
1483 * attached objects that may or may not implement the {@link
1484 * StateHolder} interface. Using this method implies the use of
1485 * {@link #restoreAttachedState} to restore the attached
1486 * objects.</p>
1487 * <p/>
1488 * <p>This method supports saving attached objects of the following
1489 * type: <code>Object</code>s, <code>null</code> values, and <code
1490 * class="changed_modified_2_0">Collection</code>s of these objects.
1491 * If any contained objects are not <code
1492 * class="changed_modified_2_0">Collection</code>s and do not
1493 * implement {@link StateHolder}, they must have zero-argument
1494 * public constructors. The exact structure of the returned object
1495 * is undefined and opaque, but will be serializable. </p>
1496 *
1497 * @param context the {@link FacesContext} for this request.
1498 * @param attachedObject the object, which may be a
1499 * <code>List</code> instance, or an Object. The
1500 * <code>attachedObject</code> (or the elements that comprise
1501 * <code>attachedObject</code> may implement {@link StateHolder}.
1502 * @throws NullPointerException if the context argument is null.
1503 */
1504
1505 public static Object saveAttachedState(FacesContext context,
1506 Object attachedObject) {
1507 if (null == context) {
1508 throw new NullPointerException();
1509 }
1510 if (null == attachedObject) {
1511 return null;
1512 }
1513 Object result;
1514
1515 if (attachedObject instanceof Collection) {
1516 Collection attachedCollection = (Collection) attachedObject;
1517 List<StateHolderSaver> resultList = null;
1518 for (Object item : attachedCollection) {
1519 if (item != null) {
1520 if (item instanceof StateHolder && ((StateHolder) item).isTransient()) {
1521 continue;
1522 }
1523 if (resultList == null) {
1524 resultList = new ArrayList<StateHolderSaver>(attachedCollection.size() + 1);
1525 resultList.add(new StateHolderSaver(context, attachedCollection.getClass()));
1526 }
1527 resultList.add(new StateHolderSaver(context, item));
1528 }
1529 }
1530 result = resultList;
1531 } else {
1532 result = new StateHolderSaver(context, attachedObject);
1533 }
1534
1535 return result;
1536 }
1537
1538 /**
1539 * <p>This method is called by {@link UIComponent} subclasses that
1540 * need to restore the objects they saved using {@link
1541 * #saveAttachedState}. This method is tightly coupled with {@link
1542 * #saveAttachedState}.</p>
1543 * <p/>
1544 * <p>This method supports restoring all attached objects types
1545 * supported by {@link #saveAttachedState}.</p>
1546 *
1547 * @param context the {@link FacesContext} for this request
1548 * @param stateObj the opaque object returned from {@link
1549 * #saveAttachedState}
1550 * @throws NullPointerException if context is null.
1551 * @throws IllegalStateException if the object is not
1552 * previously returned by {@link #saveAttachedState}.
1553 */
1554
1555 public static Object restoreAttachedState(FacesContext context,
1556 Object stateObj)
1557 throws IllegalStateException {
1558 if (null == context) {
1559 throw new NullPointerException();
1560 }
1561 if (null == stateObj) {
1562 return null;
1563 }
1564 Object result;
1565
1566 if (stateObj instanceof List) {
1567 List<StateHolderSaver> stateList = (List<StateHolderSaver>) stateObj;
1568 Collection<Object> retCollection = null;
1569 StateHolderSaver collectionSaver = stateList.get(0);
1570 Class collectionClass = (Class) collectionSaver.restore(context);
1571 try {
1572 retCollection = (Collection<Object>) collectionClass.newInstance();
1573 }
1574 catch (Exception e) {
1575 if (LOGGER.isLoggable(Level.SEVERE)) {
1576 LOGGER.log(Level.SEVERE, e.toString(), e);
1577 }
1578 throw new IllegalStateException("Unknown object type");
1579 }
1580 for (int i = 1, len = stateList.size(); i < len; i++) {
1581 try {
1582 retCollection.add(stateList.get(i).restore(context));
1583 } catch (ClassCastException cce) {
1584 if (LOGGER.isLoggable(Level.SEVERE)) {
1585 LOGGER.log(Level.SEVERE, cce.toString(), cce);
1586 }
1587 throw new IllegalStateException("Unknown object type");
1588 }
1589 }
1590 result = retCollection;
1591 } else if (stateObj instanceof StateHolderSaver) {
1592 StateHolderSaver saver = (StateHolderSaver) stateObj;
1593 result = saver.restore(context);
1594 } else {
1595 throw new IllegalStateException("Unknown object type");
1596 }
1597 return result;
1598 }
1599
1600 private static Map<String, ValueExpression> restoreBindingsState(FacesContext context, Object state) {
1601
1602 if (state == null) {
1603 return (null);
1604 }
1605 Object values[] = (Object[]) state;
1606 String names[] = (String[]) values[0];
1607 Object states[] = (Object[]) values[1];
1608 Map<String, ValueExpression> bindings = new HashMap<String, ValueExpression>(names.length);
1609 for (int i = 0; i < names.length; i++) {
1610 bindings.put(names[i],
1611 (ValueExpression) restoreAttachedState(context, states[i]));
1612 }
1613 return (bindings);
1614
1615 }
1616
1617
1618 private Object saveBindingsState(FacesContext context) {
1619
1620 if (bindings == null) {
1621 return (null);
1622 }
1623
1624 Object values[] = new Object[2];
1625 values[0] = bindings.keySet().toArray(new String[bindings.size()]);
1626
1627 Object[] bindingValues = bindings.values().toArray();
1628 for (int i = 0; i < bindingValues.length; i++) {
1629 bindingValues[i] = saveAttachedState(context, bindingValues[i]);
1630 }
1631
1632 values[1] = bindingValues;
1633
1634 return (values);
1635
1636 }
1637
1638 private Object saveSystemEventListeners(FacesContext ctx) {
1639
1640 if (listenersByEventClass == null) {
1641 return null;
1642 }
1643
1644 int size = listenersByEventClass.size();
1645 Object listeners[][] = new Object[size][2];
1646 int idx = 0;
1647 boolean savedState = false;
1648 for (Entry<Class<? extends SystemEvent>, List<SystemEventListener>> e : listenersByEventClass.entrySet()) {
1649 Object[] target = listeners[idx++];
1650 target[0] = e.getKey();
1651 target[1] = saveAttachedState(ctx, e.getValue());
1652 if (target[1] == null) {
1653 target[0] = null;
1654 } else {
1655 savedState = true;
1656 }
1657 }
1658
1659 return ((savedState) ? listeners : null);
1660
1661 }
1662
1663
1664 private Map<Class<? extends SystemEvent>, List<SystemEventListener>> restoreSystemEventListeners(FacesContext ctx, Object state) {
1665
1666 if (state == null) {
1667 return null;
1668 }
1669
1670 Object[][] listeners = (Object[][]) state;
1671 Map<Class<? extends SystemEvent>, List<SystemEventListener>> m =
1672 new HashMap<Class<? extends SystemEvent>, List<SystemEventListener>>(listeners.length, 1.0f);
1673 for (int i = 0, len = listeners.length; i < len; i++) {
1674 Object[] source = listeners[i];
1675 m.put((Class<? extends SystemEvent>) source[0],
1676 (List<SystemEventListener>) restoreAttachedState(ctx, source[1]));
1677 }
1678
1679 return m;
1680 }
1681
1682
1683 Map<String, PropertyDescriptor> getDescriptorMap() {
1684 return pdMap;
1685 }
1686
1687
1688 private void doPostAddProcessing(FacesContext context, UIComponent added) {
1689
1690 if (parent.isInView()) {
1691 publishAfterViewEvents(context, context.getApplication(), added);
1692 }
1693
1694 }
1695
1696 private void doPreRemoveProcessing(FacesContext context,
1697 UIComponent toRemove) {
1698
1699 if (parent.isInView()) {
1700 disconnectFromView(context, context.getApplication(), toRemove);
1701 }
1702
1703 }
1704
1705 //------------------------------------------------------------- BehaviorHolder stub methods.
1706
1707 /**
1708 * behaviors associated with this component.
1709 */
1710 private BehaviorsMap behaviors;
1711
1712 /**
1713 * <p class="changed_added_2_0">This is a default implementation of
1714 * {@link javax.faces.component.behavior.ClientBehaviorHolder#addClientBehavior}.
1715 * <code>UIComponent</code> does not implement the
1716 * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface,
1717 * but provides default implementations for the methods defined by
1718 * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify
1719 * subclass implementations. Subclasses that wish to support the
1720 * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract must
1721 * declare that the subclass implements
1722 * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must provide
1723 * an implementation of
1724 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.</p>
1725 *
1726 * @param eventName the logical name of the client-side event to attach
1727 * the behavior to.
1728 * @param behavior the {@link javax.faces.component.behavior.Behavior}
1729 * instance to attach for the specified event name.
1730 *
1731 * @since 2.0
1732 */
1733 public void addClientBehavior(String eventName, ClientBehavior behavior) {
1734 assertClientBehaviorHolder();
1735 // First, make sure that the event is supported. We don't want
1736 // to bother attaching behaviors for unsupported events.
1737
1738 Collection<String> eventNames = getEventNames();
1739
1740 // getClientEventNames() is spec'ed to require a non-null Set.
1741 // If getClientEventNames() returns null, throw an exception
1742 // to indicate that the API in not being used properly.
1743 if (eventNames == null) {
1744 throw new IllegalStateException(
1745 "Attempting to add a Behavior to a component " +
1746 "that does not support any event types. " +
1747 "getEventTypes() must return a non-null Set.");
1748 }
1749
1750 if (eventNames.contains(eventName)) {
1751
1752 if (initialStateMarked()) {
1753 // a Behavior has been added dynamically. Update existing
1754 // Behaviors, if any, to save their full state.
1755 if (behaviors != null) {
1756 for (Entry<String, List<ClientBehavior>> entry : behaviors
1757 .entrySet()) {
1758 for (ClientBehavior b : entry.getValue()) {
1759 if (b instanceof PartialStateHolder) {
1760 ((PartialStateHolder) behavior).clearInitialState();
1761 }
1762 }
1763 }
1764 }
1765 }
1766 // We've got an event that we support, create our Map
1767 // if necessary
1768
1769 if (null == behaviors) {
1770 // Typically we only have a small number of behaviors for
1771 // any component - in most cases only 1. Using a very small
1772 // initial capacity so that we keep the footprint to a minimum.
1773 Map<String, List<ClientBehavior>> modifiableMap =
1774 new HashMap<String, List<ClientBehavior>>(5,1.0f);
1775 behaviors = new BehaviorsMap(modifiableMap);
1776 }
1777
1778 List<ClientBehavior> eventBehaviours = behaviors.get(eventName);
1779
1780 if (null == eventBehaviours) {
1781 // Again using small initial capacity - we typically
1782 // only have 1 Behavior per event type.
1783 eventBehaviours = new ArrayList<ClientBehavior>(3);
1784 behaviors.getModifiableMap().put(eventName, eventBehaviours);
1785 }
1786
1787 eventBehaviours.add(behavior);
1788 }
1789 }
1790
1791 /**
1792 * <p class="changed_added_2_0">This is a default implementation of
1793 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.
1794 * <code>UIComponent</code> does not implement the
1795 * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface,
1796 * but provides default implementations for the methods defined by
1797 * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify
1798 * subclass implementations. Subclasses that wish to support the
1799 * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract
1800 * must declare that the subclass implements
1801 * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must
1802 * override this method to return a non-Empty <code>Collection</code>
1803 * of the client event names that the component supports.</p>
1804 *
1805 * @since 2.0
1806 */
1807 public Collection<String> getEventNames() {
1808 assertClientBehaviorHolder();
1809 // Note: we intentionally return null here even though this
1810 // is not a valid value. The result is that addClientBehavior()
1811 // will fail with an IllegalStateException if getEventNames()
1812 // is not overridden. This should make it obvious to the
1813 // component author that something is wrong.
1814 return null;
1815 }
1816
1817 /**
1818 * <p class="changed_added_2_0">This is a default implementation of
1819 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getClientBehaviors}.
1820 * <code>UIComponent</code> does not implement the
1821 * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface,
1822 * but provides default implementations for the methods defined by
1823 * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify
1824 * subclass implementations. Subclasses that wish to support the
1825 * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract
1826 * must declare that the subclass implements
1827 * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must add
1828 * an implementation of
1829 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.</p>
1830 *
1831 * @since 2.0
1832 */
1833 public Map<String, List<ClientBehavior>> getClientBehaviors() {
1834 if (null == behaviors) {
1835 return Collections.emptyMap();
1836 }
1837
1838 return behaviors;
1839 }
1840
1841 /**
1842 * <p class="changed_added_2_0">This is a default implementation of
1843 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getDefaultEventName}.
1844 * <code>UIComponent</code> does not implement the
1845 * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface,
1846 * but provides default implementations for the methods defined by
1847 * {@link javax.faces.component.behavior.ClientBehaviorHolder} to simplify
1848 * subclass implementations. Subclasses that wish to support the
1849 * {@link javax.faces.component.behavior.ClientBehaviorHolder} contract
1850 * must declare that the subclass implements
1851 * {@link javax.faces.component.behavior.ClientBehaviorHolder}, and must
1852 * provide an implementation of
1853 * {@link javax.faces.component.behavior.ClientBehaviorHolder#getEventNames}.</p>
1854 */
1855 public String getDefaultEventName() {
1856 assertClientBehaviorHolder();
1857 // Our default implementation just returns null - no default
1858 // event name;
1859 return null;
1860 }
1861
1862 /**
1863 * {@link UIComponentBase} has stub methods from the {@link ClientBehaviorHolder} interface,
1864 * but these method should be used only with componets that really implement holder interface.
1865 * For an any other classes this method throws {@link IllegalStateException}
1866 * @throws IllegalStateException
1867 */
1868 private void assertClientBehaviorHolder() {
1869 if (!isClientBehaviorHolder()) {
1870 throw new IllegalStateException(
1871 "Attempting to use a Behavior feature with a component " +
1872 "that does not support any event types. " +
1873 "Component must implement BehaviourHolder interface.");
1874 }
1875 }
1876
1877 /**
1878 * @return true if component implements {@link ClientBehaviorHolder} interface.
1879 */
1880 private boolean isClientBehaviorHolder() {
1881 return ClientBehaviorHolder.class.isInstance(this);
1882 }
1883
1884 /**
1885 * Save state of the behaviors map.
1886 * @param context the {@link FacesContext} for this request.
1887 * @return map converted to the array of <code>Object</code> or null if no behaviors have been set.
1888 */
1889 private Object saveBehaviorsState(FacesContext context){
1890 Object state = null;
1891 if (null != behaviors && behaviors.size() >0){
1892 boolean stateWritten = false;
1893 Object[] attachedBehaviors = new Object[behaviors.size()];
1894 int i = 0;
1895 for (List<ClientBehavior> eventBehaviors : behaviors.values()) {
1896 // we need to take different action depending on whether
1897 // or not markInitialState() was called. If it's not called,
1898 // assume JSF 1.2 style state saving and call through to
1899 // saveAttachedState(), otherwise, call saveState() on the
1900 // behaviors directly.
1901 Object[] attachedEventBehaviors = new Object[eventBehaviors.size()];
1902 for (int j = 0; j < attachedEventBehaviors.length; j++) {
1903 attachedEventBehaviors[j] = ((initialStateMarked())
1904 ? saveBehavior(context, eventBehaviors.get(j))
1905 : saveAttachedState(context, eventBehaviors.get(j)));
1906 if (!stateWritten) {
1907 stateWritten = (attachedEventBehaviors[j] != null);
1908 }
1909 }
1910 attachedBehaviors[i++] = attachedEventBehaviors;
1911 }
1912 if (stateWritten) {
1913 state = new Object[]{behaviors.keySet().toArray(new String[behaviors.size()]),attachedBehaviors};
1914 }
1915 }
1916 return state;
1917 }
1918
1919 /**
1920 * @param context the {@link FacesContext} for this request.
1921 * @param state saved state of the {@link Behavior}'s attached to the component.
1922 * @return restored <code>Map</code> of the behaviors.
1923 */
1924 private BehaviorsMap restoreBehaviorsState(FacesContext context, Object state) {
1925
1926 if (null != state) {
1927 Object[] values = (Object[]) state;
1928 String[] names = (String[])values[0];
1929 Object[] attachedBehaviors = (Object[]) values[1];
1930 // we need to take different action depending on whether
1931 // or not markInitialState() was called. If it's not called,
1932 // assume JSF 1.2 style state saving and call through to
1933 // restoreAttachedState(), otherwise, call restoreState() on the
1934 // behaviors directly.
1935 if (!initialStateMarked()) {
1936 Map<String, List<ClientBehavior>> modifiableMap = new HashMap<String, List<ClientBehavior>>(
1937 names.length,
1938 1.0f);
1939 for (int i = 0; i < attachedBehaviors.length; i++) {
1940 Object[] attachedEventBehaviors = (Object[]) attachedBehaviors[i];
1941 ArrayList<ClientBehavior> eventBehaviors =
1942 new ArrayList<ClientBehavior>(attachedBehaviors.length);
1943 for (int j = 0; j < attachedEventBehaviors.length; j++) {
1944 eventBehaviors.add((ClientBehavior) restoreAttachedState(context,
1945 attachedEventBehaviors[j]));
1946 }
1947
1948 modifiableMap.put(names[i], eventBehaviors);
1949 }
1950
1951 return new BehaviorsMap(modifiableMap);
1952 } else {
1953 for (int i = 0, len = names.length; i < len; i++) {
1954 // assume the behaviors have already been populated by
1955 // execution of the template. Process the state in the
1956 // same order that the names were saved.
1957 List<ClientBehavior> existingBehaviors =
1958 behaviors.get(names[i]);
1959 restoreBehaviors(context, existingBehaviors, (Object[]) attachedBehaviors[i]);
1960 }
1961 return behaviors;
1962 }
1963 }
1964 return null;
1965 }
1966
1967 private Object saveBehavior(FacesContext ctx, ClientBehavior behavior) {
1968
1969 // if the Behavior isn't a StateHolder, do nothing as it will be
1970 // added to the BehaviorMap when the template is re-executed.
1971 return ((behavior instanceof StateHolder)
1972 ? ((StateHolder) behavior).saveState(ctx)
1973 : null);
1974
1975 }
1976
1977
1978 private void restoreBehaviors(FacesContext ctx, List<ClientBehavior> existingBehaviors, Object[] state) {
1979
1980 // this method assumes a one to one correspondence in both length and
1981 // order.
1982 for (int i = 0, len = state.length; i < len; i++) {
1983 ClientBehavior behavior = existingBehaviors.get(i);
1984 if (state[i] == null) {
1985 // nothing to do...move on
1986 continue;
1987 }
1988 // if the Behavior is a StateHolder, invoke restoreState
1989 // passing in the current state. If it's not, just ignore
1990 // it and move along.
1991 if (behavior instanceof StateHolder) {
1992 ((StateHolder) behavior).restoreState(ctx, state[i]);
1993 }
1994 }
1995 }
1996
1997
1998 private static void publishAfterViewEvents(FacesContext context,
1999 Application application,
2000 UIComponent component) {
2001
2002 component.setInView(true);
2003 try {
2004 component.pushComponentToEL(context, component);
2005 application.publishEvent(context, PostAddToViewEvent.class, component);
2006 if (component.getChildCount() > 0) {
2007 Collection<UIComponent> clist =
2008 new ArrayList<UIComponent>(component.getChildren());
2009 for (UIComponent c : clist) {
2010 publishAfterViewEvents(context, application, c);
2011 }
2012 }
2013
2014 if (component.getFacetCount() > 0) {
2015 Collection<UIComponent> clist =
2016 new ArrayList<UIComponent>(component.getFacets().values());
2017 for (UIComponent c : clist) {
2018 publishAfterViewEvents(context, application, c);
2019 }
2020 }
2021 } finally {
2022 component.popComponentFromEL(context);
2023 }
2024
2025 }
2026
2027
2028 private static void disconnectFromView(FacesContext context,
2029 Application application,
2030 UIComponent component) {
2031
2032 application.publishEvent(context,
2033 PreRemoveFromViewEvent.class,
2034 component);
2035 component.setInView(false);
2036 component.compositeParent = null;
2037 if (component.getChildCount() > 0) {
2038 List<UIComponent> children = component.getChildren();
2039 for (UIComponent c : children) {
2040 disconnectFromView(context, application, c);
2041 }
2042 }
2043 if (component.getFacetCount() > 0) {
2044 Map<String, UIComponent> facets = component.getFacets();
2045 for (UIComponent c : facets.values()) {
2046 disconnectFromView(context, application, c);
2047 }
2048 }
2049
2050 }
2051
2052
2053 // --------------------------------------------------------- Private Classes
2054
2055 // For state saving
2056 private final static Object[] EMPTY_ARRAY = new Object[0];
2057
2058 // Empty iterator for short circuiting operations
2059 private final static Iterator<UIComponent> EMPTY_ITERATOR = new Iterator<UIComponent>() {
2060
2061 public void remove() {
2062 throw new UnsupportedOperationException();
2063 }
2064
2065 public UIComponent next() {
2066 throw new NoSuchElementException("Empty Iterator");
2067 }
2068
2069 public boolean hasNext() {
2070 return false;
2071 }
2072 };
2073
2074 // Private implementation of Map that supports the functionality
2075 // required by UIComponent.getFacets()
2076 // HISTORY:
2077 // Versions 1.333 and older used inheritence to provide the
2078 // basic map functionality. This was wasteful since a
2079 // component could be completely configured via ValueExpressions
2080 // or (Bindings) which means an EMPTY_OBJECT_ARRAY Map would always be
2081 // present when it wasn't needed. By using composition,
2082 // we control if and when the Map is instantiated thereby
2083 // reducing uneeded object allocation. This change also
2084 // has a nice side effect in state saving since we no
2085 // longer need to duplicate the map, we just provide the
2086 // private 'attributes' map directly to the state saving process.
2087 private static class AttributesMap implements Map<String, Object>, Serializable {
2088
2089 // this KEY is special to the AttributesMap - this allows the implementation
2090 // to access the the List containing the attributes that have been set
2091 private static final String ATTRIBUTES_THAT_ARE_SET_KEY =
2092 UIComponentBase.class.getName() + ".attributesThatAreSet";
2093
2094 //private Map<String, Object> attributes;
2095 private transient Map<String, PropertyDescriptor> pdMap;
2096 private transient UIComponent component;
2097 private static final long serialVersionUID = -6773035086539772945L;
2098
2099 // -------------------------------------------------------- Constructors
2100
2101 private AttributesMap(UIComponent component) {
2102
2103 this.component = component;
2104 this.pdMap = ((UIComponentBase) component).getDescriptorMap();
2105
2106 }
2107
2108 public boolean containsKey(Object keyObj) {
2109 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(keyObj)) {
2110 return true;
2111 }
2112 String key = (String) keyObj;
2113 PropertyDescriptor pd =
2114 getPropertyDescriptor(key);
2115 if (pd == null) {
2116 Map<String,Object> attributes = (Map<String,Object>)
2117 component.getStateHelper().get(PropertyKeys.attributes);
2118 if (attributes != null) {
2119 return attributes.containsKey(key);
2120 } else {
2121 return (false);
2122 }
2123 } else {
2124 return (false);
2125 }
2126 }
2127
2128 public Object get(Object keyObj) {
2129 String key = (String) keyObj;
2130 Object result = null;
2131 if (key == null) {
2132 throw new NullPointerException();
2133 }
2134 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) {
2135 result = component.getStateHelper().get(UIComponent.PropertyKeysPrivate.attributesThatAreSet);
2136 }
2137 Map<String,Object> attributes = (Map<String,Object>)
2138 component.getStateHelper().get(PropertyKeys.attributes);
2139 if (null == result) {
2140 PropertyDescriptor pd =
2141 getPropertyDescriptor(key);
2142 if (pd != null) {
2143 try {
2144 Method readMethod = pd.getReadMethod();
2145 if (readMethod != null) {
2146 result = (readMethod.invoke(component,
2147 EMPTY_OBJECT_ARRAY));
2148 } else {
2149 throw new IllegalArgumentException(key);
2150 }
2151 } catch (IllegalAccessException e) {
2152 throw new FacesException(e);
2153 } catch (InvocationTargetException e) {
2154 throw new FacesException(e.getTargetException());
2155 }
2156 } else if (attributes != null) {
2157 if (attributes.containsKey(key)) {
2158 result = attributes.get(key);
2159 }
2160 }
2161 }
2162 if (null == result) {
2163 ValueExpression ve = component.getValueExpression(key);
2164 if (ve != null) {
2165 try {
2166 result = ve.getValue(component.getFacesContext().getELContext());
2167 } catch (ELException e) {
2168 throw new FacesException(e);
2169 }
2170 }
2171 }
2172
2173 return result;
2174 }
2175
2176 public Object put(String keyValue, Object value) {
2177 if (keyValue == null) {
2178 throw new NullPointerException();
2179 }
2180
2181 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(keyValue)) {
2182 if (component.attributesThatAreSet == null) {
2183 if (value instanceof List) {
2184 component.getStateHelper().put(UIComponent.PropertyKeysPrivate.attributesThatAreSet,
2185 value);
2186 }
2187 }
2188 return null;
2189 }
2190
2191 PropertyDescriptor pd =
2192 getPropertyDescriptor(keyValue);
2193 if (pd != null) {
2194 try {
2195 Object result = null;
2196 Method readMethod = pd.getReadMethod();
2197 if (readMethod != null) {
2198 result = readMethod.invoke
2199 (component, EMPTY_OBJECT_ARRAY);
2200 }
2201 Method writeMethod = pd.getWriteMethod();
2202 if (writeMethod != null) {
2203 writeMethod.invoke
2204 (component, value);
2205 } else {
2206 // TODO: i18n
2207 throw new IllegalArgumentException("Setter not found for property " + keyValue);
2208 }
2209 return (result);
2210 } catch (IllegalAccessException e) {
2211 throw new FacesException(e);
2212 } catch (InvocationTargetException e) {
2213 throw new FacesException
2214 (e.getTargetException());
2215 }
2216 } else {
2217 if (value == null) {
2218 throw new NullPointerException();
2219 }
2220
2221 List<String> sProperties =
2222 (List<String>) component.getStateHelper().get(PropertyKeysPrivate.attributesThatAreSet);
2223 if (sProperties == null) {
2224 component.getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, keyValue);
2225 } else if (!sProperties.contains(keyValue)) {
2226 component.getStateHelper().add(PropertyKeysPrivate.attributesThatAreSet, keyValue);
2227 }
2228 return putAttribute(keyValue, value);
2229 }
2230 }
2231
2232 public void putAll(Map<? extends String, ?> map) {
2233 if (map == null) {
2234 throw new NullPointerException();
2235 }
2236
2237 for (Map.Entry<? extends String, ?> entry : map.entrySet()) {
2238 this.put(entry.getKey(), entry.getValue());
2239 }
2240 }
2241
2242 public Object remove(Object keyObj) {
2243 String key = (String) keyObj;
2244 if (key == null) {
2245 throw new NullPointerException();
2246 }
2247 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) {
2248 return null;
2249 }
2250 PropertyDescriptor pd =
2251 getPropertyDescriptor(key);
2252 if (pd != null) {
2253 throw new IllegalArgumentException(key);
2254 } else {
2255 Map<String,Object> attributes = getAttributes();
2256 if (attributes != null) {
2257 component.getStateHelper().remove(UIComponent.PropertyKeysPrivate.attributesThatAreSet,
2258 key);
2259 return (component.getStateHelper().remove(PropertyKeys.attributes,
2260 key));
2261 } else {
2262 return null;
2263 }
2264 }
2265 }
2266
2267
2268 public int size() {
2269 Map attributes = getAttributes();
2270 return (attributes != null ? attributes.size() : 0);
2271 }
2272
2273 public boolean isEmpty() {
2274 Map attributes = getAttributes();
2275 return (attributes == null || attributes.isEmpty());
2276 }
2277
2278 public boolean containsValue(java.lang.Object value) {
2279 Map attributes= getAttributes();
2280 return (attributes != null && attributes.containsValue(value));
2281 }
2282
2283 public void clear() {
2284 component.getStateHelper().remove(PropertyKeys.attributes);
2285 component.getStateHelper().remove(PropertyKeysPrivate.attributesThatAreSet);
2286 }
2287
2288 public Set<String> keySet() {
2289 Map<String,Object> attributes = getAttributes();
2290 if (attributes != null)
2291 return Collections.unmodifiableSet(attributes.keySet());
2292 return Collections.emptySet();
2293 }
2294
2295 public Collection<Object> values() {
2296 Map<String,Object> attributes = getAttributes();
2297 if (attributes != null)
2298 return Collections.unmodifiableCollection(attributes.values());
2299 return Collections.emptyList();
2300 }
2301
2302 public Set<Entry<String, Object>> entrySet() {
2303 Map<String,Object> attributes = getAttributes();
2304 if (attributes != null)
2305 return Collections.unmodifiableSet(attributes.entrySet());
2306 return Collections.emptySet();
2307 }
2308
2309 public boolean equals(Object o) {
2310 if (o == this) {
2311 return true;
2312 }
2313
2314 if (!(o instanceof Map)) {
2315 return false;
2316 }
2317 Map t = (Map) o;
2318 if (t.size() != size()) {
2319 return false;
2320 }
2321
2322 try {
2323 for (Object e : entrySet()) {
2324 Entry entry = (Entry) e;
2325 Object key = entry.getKey();
2326 Object value = entry.getValue();
2327 if (value == null) {
2328 if (!(t.get(key) == null && t.containsKey(key))) {
2329 return false;
2330 }
2331 } else {
2332 if (!value.equals(t.get(key))) {
2333 return false;
2334 }
2335 }
2336 }
2337 } catch (ClassCastException unused) {
2338 return false;
2339 } catch (NullPointerException unused) {
2340 return false;
2341 }
2342
2343 return true;
2344 }
2345
2346
2347 public int hashCode() {
2348 int h = 0;
2349 for (Object o : entrySet()) {
2350 h += o.hashCode();
2351 }
2352 return h;
2353 }
2354
2355 private Map<String,Object> getAttributes() {
2356 return (Map<String,Object>) component.getStateHelper().get(
2357 PropertyKeys.attributes);
2358 }
2359
2360 private Object putAttribute(String key, Object value) {
2361 return component.getStateHelper().put(PropertyKeys.attributes,
2362 key,
2363 value);
2364 }
2365
2366
2367 /**
2368 * <p>Return the <code>PropertyDescriptor</code> for the specified
2369 * property name for this {@link UIComponent}'s implementation class,
2370 * if any; otherwise, return <code>null</code>.</p>
2371 *
2372 * @param name Name of the property to return a descriptor for
2373 * @throws FacesException if an introspection exception occurs
2374 */
2375 PropertyDescriptor getPropertyDescriptor(String name) {
2376 if (pdMap != null) {
2377 return (pdMap.get(name));
2378 }
2379 return (null);
2380 }
2381
2382 // ----------------------------------------------- Serialization Methods
2383
2384 // This is dependent on serialization occuring with in a
2385 // a Faces request, however, since UIComponentBase.{save,restore}State()
2386 // doesn't actually serialize the AttributesMap, these methods are here
2387 // purely to be good citizens.
2388
2389 private void writeObject(ObjectOutputStream out) throws IOException {
2390 out.writeObject(component.getClass());
2391 //noinspection NonSerializableObjectPassedToObjectStream
2392 out.writeObject(component.saveState(FacesContext.getCurrentInstance()));
2393 }
2394
2395 private void readObject(ObjectInputStream in)
2396 throws IOException, ClassNotFoundException {
2397 //noinspection unchecked
2398 Class clazz = (Class) in.readObject();
2399 try {
2400 component = (UIComponent) clazz.newInstance();
2401 } catch (Exception e) {
2402 throw new RuntimeException(e);
2403 }
2404 component.restoreState(FacesContext.getCurrentInstance(), in.readObject());
2405 }
2406 }
2407
2408
2409 // Private implementation of List that supports the functionality
2410 // required by UIComponent.getChildren()
2411 private static class ChildrenList extends ArrayList<UIComponent> {
2412
2413 private UIComponent component;
2414
2415 public ChildrenList(UIComponent component) {
2416 super(6);
2417 this.component = component;
2418 }
2419
2420 public void add(int index, UIComponent element) {
2421 if (element == null) {
2422 throw new NullPointerException();
2423 } else if ((index < 0) || (index > size())) {
2424 throw new IndexOutOfBoundsException();
2425 } else {
2426 eraseParent(element);
2427 super.add(index, element);
2428 element.setParent(component);
2429
2430 }
2431 }
2432
2433 public boolean add(UIComponent element) {
2434 if (element == null) {
2435 throw new NullPointerException();
2436 } else {
2437 eraseParent(element);
2438 boolean result = super.add(element);
2439 element.setParent(component);
2440 return result;
2441 }
2442 }
2443
2444 public boolean addAll(Collection<? extends UIComponent> collection) {
2445 Iterator<UIComponent> elements =
2446 (new ArrayList<UIComponent>(collection)).iterator();
2447 boolean changed = false;
2448 while (elements.hasNext()) {
2449 UIComponent element = elements.next();
2450 if (element == null) {
2451 throw new NullPointerException();
2452 } else {
2453 add(element);
2454 changed = true;
2455 }
2456 }
2457 return (changed);
2458 }
2459
2460 public boolean addAll(int index, Collection<? extends UIComponent> collection) {
2461 Iterator<UIComponent> elements =
2462 (new ArrayList<UIComponent>(collection)).iterator();
2463 boolean changed = false;
2464 while (elements.hasNext()) {
2465 UIComponent element = elements.next();
2466 if (element == null) {
2467 throw new NullPointerException();
2468 } else {
2469 add(index++, element);
2470 changed = true;
2471 }
2472 }
2473 return (changed);
2474 }
2475
2476 public void clear() {
2477 int n = size();
2478 if (n < 1) {
2479 return;
2480 }
2481 for (int i = 0; i < n; i++) {
2482 UIComponent child = get(i);
2483 child.setParent(null);
2484 }
2485 super.clear();
2486 }
2487
2488 public Iterator<UIComponent> iterator() {
2489 return (new ChildrenListIterator(this));
2490 }
2491
2492 public ListIterator<UIComponent> listIterator() {
2493 return (new ChildrenListIterator(this));
2494 }
2495
2496 public ListIterator<UIComponent> listIterator(int index) {
2497 return (new ChildrenListIterator(this, index));
2498 }
2499
2500 public UIComponent remove(int index) {
2501 UIComponent child = get(index);
2502 super.remove(index);
2503 child.setParent(null);
2504 return (child);
2505 }
2506
2507 public boolean remove(Object elementObj) {
2508 UIComponent element = (UIComponent) elementObj;
2509 if (element == null) {
2510 throw new NullPointerException();
2511 }
2512
2513 if (super.remove(element)) {
2514 element.setParent(null);
2515 return (true);
2516 } else {
2517 return (false);
2518 }
2519 }
2520
2521 public boolean removeAll(Collection<?> collection) {
2522 boolean result = false;
2523 for (Object elements : collection) {
2524 if (remove(elements)) {
2525 result = true;
2526 }
2527 }
2528 return (result);
2529 }
2530
2531 public boolean retainAll(Collection<?> collection) {
2532 boolean modified = false;
2533 Iterator<?> items = iterator();
2534 while (items.hasNext()) {
2535 if (!collection.contains(items.next())) {
2536 items.remove();
2537 modified = true;
2538 }
2539 }
2540 return (modified);
2541 }
2542
2543 public UIComponent set(int index, UIComponent element) {
2544 if (element == null) {
2545 throw new NullPointerException();
2546 } else if ((index < 0) || (index >= size())) {
2547 throw new IndexOutOfBoundsException();
2548 } else {
2549 eraseParent(element);
2550 UIComponent previous = get(index);
2551 super.set(index, element);
2552 previous.setParent(null);
2553 element.setParent(component);
2554 return (previous);
2555 }
2556 }
2557 }
2558
2559
2560 // Private implementation of ListIterator for ChildrenList
2561 private static class ChildrenListIterator implements ListIterator<UIComponent> {
2562
2563
2564 public ChildrenListIterator(ChildrenList list) {
2565 this.list = list;
2566 this.index = 0;
2567 }
2568
2569 public ChildrenListIterator(ChildrenList list, int index) {
2570 this.list = list;
2571 if ((index < 0) || (index > list.size())) {
2572 throw new IndexOutOfBoundsException(String.valueOf(index));
2573 } else {
2574 this.index = index;
2575 }
2576 }
2577
2578
2579 private ChildrenList list;
2580 private int index;
2581 private int last = -1; // Index last returned by next() or previous()
2582
2583 // Iterator methods
2584
2585 public boolean hasNext() {
2586 return (index < list.size());
2587 }
2588
2589 public UIComponent next() {
2590 try {
2591 UIComponent o = list.get(index);
2592 last = index++;
2593 return (o);
2594 } catch (IndexOutOfBoundsException e) {
2595 throw new NoSuchElementException(String.valueOf(index));
2596 }
2597 }
2598
2599 public void remove() {
2600 if (last == -1) {
2601 throw new IllegalStateException();
2602 }
2603 list.remove(last);
2604 if (last < index) {
2605 index--;
2606 }
2607 last = -1;
2608 }
2609
2610 // ListIterator methods
2611
2612 public void add(UIComponent o) {
2613 last = -1;
2614 list.add(index++, o);
2615 }
2616
2617 public boolean hasPrevious() {
2618 return (index > 1);
2619 }
2620
2621 public int nextIndex() {
2622 return (index);
2623 }
2624
2625 public UIComponent previous() {
2626 try {
2627 int current = index - 1;
2628 UIComponent o = list.get(current);
2629 last = current;
2630 index = current;
2631 return (o);
2632 } catch (IndexOutOfBoundsException e) {
2633 throw new NoSuchElementException();
2634 }
2635 }
2636
2637 public int previousIndex() {
2638 return (index - 1);
2639 }
2640
2641 public void set(UIComponent o) {
2642 if (last == -1) {
2643 throw new IllegalStateException();
2644 }
2645 list.set(last, o);
2646 }
2647
2648 }
2649
2650
2651 // Private implementation of Iterator for getFacetsAndChildren()
2652 private final static class FacetsAndChildrenIterator implements Iterator<UIComponent> {
2653
2654 private Iterator<UIComponent> iterator;
2655 private boolean childMode;
2656 private UIComponent c;
2657
2658 public FacetsAndChildrenIterator(UIComponent c) {
2659 this.c = c;
2660 this.childMode = false;
2661 }
2662
2663 private void update() {
2664 if (this.iterator == null) {
2665 // we must guarantee that 'iterator' is never null
2666 if (this.c.getFacetCount() != 0) {
2667 this.iterator = this.c.getFacets().values().iterator();
2668 this.childMode = false;
2669 } else if (this.c.getChildCount() != 0) {
2670 this.iterator = this.c.getChildren().iterator();
2671 this.childMode = true;
2672 } else {
2673 this.iterator = EMPTY_ITERATOR;
2674 this.childMode = true;
2675 }
2676 } else if (!this.childMode
2677 && !this.iterator.hasNext()
2678 && this.c.getChildCount() != 0) {
2679 this.iterator = this.c.getChildren().iterator();
2680 this.childMode = true;
2681 }
2682 }
2683
2684 public boolean hasNext() {
2685 this.update();
2686 return this.iterator.hasNext();
2687 }
2688
2689 public UIComponent next() {
2690 this.update();
2691 return this.iterator.next();
2692 }
2693
2694 public void remove() {
2695 throw new UnsupportedOperationException();
2696 }
2697
2698 }
2699
2700
2701 // Private implementation of Map that supports the functionality
2702 // required by UIComponent.getFacets()
2703 private static class FacetsMap extends HashMap<String, UIComponent> {
2704
2705 private UIComponent component;
2706
2707 public FacetsMap(UIComponent component) {
2708 super(3, 1.0f);
2709 this.component = component;
2710 }
2711
2712 public void clear() {
2713 Iterator<String> keys = keySet().iterator();
2714 while (keys.hasNext()) {
2715 keys.next();
2716 keys.remove();
2717 }
2718 super.clear();
2719 }
2720
2721 public Set<Map.Entry<String, UIComponent>> entrySet() {
2722 return (new FacetsMapEntrySet(this));
2723 }
2724
2725 public Set<String> keySet() {
2726 return (new FacetsMapKeySet(this));
2727 }
2728
2729 public UIComponent put(String key, UIComponent value) {
2730 if ((key == null) || (value == null)) {
2731 throw new NullPointerException();
2732 } else //noinspection ConstantConditions
2733 if (!(key instanceof String) ||
2734 !(value instanceof UIComponent)) {
2735 throw new ClassCastException();
2736 }
2737 UIComponent previous = super.get(key);
2738 if (previous != null) {
2739 previous.setParent(null);
2740 }
2741 eraseParent(value);
2742 UIComponent result = super.put(key, value);
2743 value.setParent(component);
2744
2745 return (result);
2746 }
2747
2748 public void putAll(Map<? extends String, ? extends UIComponent> map) {
2749 if (map == null) {
2750 throw new NullPointerException();
2751 }
2752 for (Map.Entry<? extends String, ? extends UIComponent> entry : map.entrySet()) {
2753 put(entry.getKey(), entry.getValue());
2754 }
2755 }
2756
2757 public UIComponent remove(Object key) {
2758 UIComponent previous = get(key);
2759 if (previous != null) {
2760 previous.setParent(null);
2761 }
2762 super.remove(key);
2763 return (previous);
2764 }
2765
2766 public Collection<UIComponent> values() {
2767 return (new FacetsMapValues(this));
2768 }
2769
2770 Iterator<String> keySetIterator() {
2771 return ((new ArrayList<String>(super.keySet())).iterator());
2772 }
2773
2774 }
2775
2776
2777 // Private implementation of Set for FacetsMap.getEntrySet()
2778 private static class FacetsMapEntrySet extends AbstractSet<Map.Entry<String, UIComponent>> {
2779
2780 public FacetsMapEntrySet(FacetsMap map) {
2781 this.map = map;
2782 }
2783
2784 private FacetsMap map = null;
2785
2786 public boolean add(Map.Entry<String, UIComponent> o) {
2787 throw new UnsupportedOperationException();
2788 }
2789
2790 public boolean addAll(Collection<? extends Map.Entry<String, UIComponent>> c) {
2791 throw new UnsupportedOperationException();
2792 }
2793
2794 public void clear() {
2795 map.clear();
2796 }
2797
2798 public boolean contains(Object o) {
2799 if (o == null) {
2800 throw new NullPointerException();
2801 }
2802 if (!(o instanceof Map.Entry)) {
2803 return (false);
2804 }
2805 Map.Entry e = (Map.Entry) o;
2806 Object k = e.getKey();
2807 Object v = e.getValue();
2808 if (!map.containsKey(k)) {
2809 return (false);
2810 }
2811 if (v == null) {
2812 return (map.get(k) == null);
2813 } else {
2814 return (v.equals(map.get(k)));
2815 }
2816 }
2817
2818 public boolean isEmpty() {
2819 return (map.isEmpty());
2820 }
2821
2822 public Iterator<Map.Entry<String, UIComponent>> iterator() {
2823 return (new FacetsMapEntrySetIterator(map));
2824 }
2825
2826 public boolean remove(Object o) {
2827 if (o == null) {
2828 throw new NullPointerException();
2829 }
2830 if (!(o instanceof Map.Entry)) {
2831 return (false);
2832 }
2833 Object k = ((Map.Entry) o).getKey();
2834 if (map.containsKey(k)) {
2835 map.remove(k);
2836 return (true);
2837 } else {
2838 return (false);
2839 }
2840 }
2841
2842 public boolean removeAll(Collection c) {
2843 boolean result = false;
2844 for (Object element : c) {
2845 if (remove(element)) {
2846 result = true;
2847 }
2848 }
2849 return (result);
2850 }
2851
2852 public boolean retainAll(Collection c) {
2853 boolean result = false;
2854 Iterator v = iterator();
2855 while (v.hasNext()) {
2856 if (!c.contains(v.next())) {
2857 v.remove();
2858 result = true;
2859 }
2860 }
2861 return (result);
2862 }
2863
2864 public int size() {
2865 return (map.size());
2866 }
2867
2868 }
2869
2870
2871 // Private implementation of Map.Entry for FacetsMapEntrySet
2872 private static class FacetsMapEntrySetEntry implements Map.Entry<String, UIComponent> {
2873
2874 public FacetsMapEntrySetEntry(FacetsMap map, String key) {
2875 this.map = map;
2876 this.key = key;
2877 }
2878
2879 private FacetsMap map;
2880 private String key;
2881
2882 public boolean equals(Object o) {
2883 if (o == null) {
2884 return (false);
2885 }
2886 if (!(o instanceof Map.Entry)) {
2887 return (false);
2888 }
2889 Map.Entry e = (Map.Entry) o;
2890 if (key == null) {
2891 if (e.getKey() != null) {
2892 return (false);
2893 }
2894 } else {
2895 if (!key.equals(e.getKey())) {
2896 return (false);
2897 }
2898 }
2899 UIComponent v = map.get(key);
2900 if (v == null) {
2901 if (e.getValue() != null) {
2902 return (false);
2903 }
2904 } else {
2905 if (!v.equals(e.getValue())) {
2906 return (false);
2907 }
2908 }
2909 return (true);
2910 }
2911
2912 public String getKey() {
2913 return (key);
2914 }
2915
2916 public UIComponent getValue() {
2917 return (map.get(key));
2918 }
2919
2920 public int hashCode() {
2921 Object value = map.get(key);
2922 return (((key == null) ? 0 : key.hashCode()) ^
2923 ((value == null) ? 0 : value.hashCode()));
2924 }
2925
2926 public UIComponent setValue(UIComponent value) {
2927 UIComponent previous = map.get(key);
2928 map.put(key, value);
2929 return (previous);
2930 }
2931
2932 }
2933
2934
2935 // Private implementation of Set for FacetsMap.getEntrySet().iterator()
2936 private static class FacetsMapEntrySetIterator implements Iterator<Map.Entry<String, UIComponent>> {
2937
2938 public FacetsMapEntrySetIterator(FacetsMap map) {
2939 this.map = map;
2940 this.iterator = map.keySetIterator();
2941 }
2942
2943 private FacetsMap map = null;
2944 private Iterator<String> iterator = null;
2945 private Map.Entry<String, UIComponent> last = null;
2946
2947 public boolean hasNext() {
2948 return (iterator.hasNext());
2949 }
2950
2951 public Map.Entry<String, UIComponent> next() {
2952 last = new FacetsMapEntrySetEntry(map, iterator.next());
2953 return (last);
2954 }
2955
2956 public void remove() {
2957 if (last == null) {
2958 throw new IllegalStateException();
2959 }
2960 map.remove(((Map.Entry) last).getKey());
2961 last = null;
2962 }
2963
2964 }
2965
2966
2967 // Private implementation of Set for FacetsMap.getKeySet()
2968 private static class FacetsMapKeySet extends AbstractSet<String> {
2969
2970 public FacetsMapKeySet(FacetsMap map) {
2971 this.map = map;
2972 }
2973
2974 private FacetsMap map = null;
2975
2976 public boolean add(String o) {
2977 throw new UnsupportedOperationException();
2978 }
2979
2980 public boolean addAll(Collection<? extends String> c) {
2981 throw new UnsupportedOperationException();
2982 }
2983
2984 public void clear() {
2985 map.clear();
2986 }
2987
2988 public boolean contains(Object o) {
2989 return (map.containsKey(o));
2990 }
2991
2992 public boolean containsAll(Collection c) {
2993 for (Object item : c) {
2994 if (!map.containsKey(item)) {
2995 return (false);
2996 }
2997 }
2998 return (true);
2999 }
3000
3001 public boolean isEmpty() {
3002 return (map.isEmpty());
3003 }
3004
3005 public Iterator<String> iterator() {
3006 return (new FacetsMapKeySetIterator(map));
3007 }
3008
3009 public boolean remove(Object o) {
3010 if (map.containsKey(o)) {
3011 map.remove(o);
3012 return (true);
3013 } else {
3014 return (false);
3015 }
3016 }
3017
3018 public boolean removeAll(Collection c) {
3019 boolean result = false;
3020 for (Object item : c) {
3021 if (map.containsKey(item)) {
3022 map.remove(item);
3023 result = true;
3024 }
3025 }
3026 return (result);
3027 }
3028
3029 public boolean retainAll(Collection c) {
3030 boolean result = false;
3031 Iterator v = iterator();
3032 while (v.hasNext()) {
3033 if (!c.contains(v.next())) {
3034 v.remove();
3035 result = true;
3036 }
3037 }
3038 return (result);
3039 }
3040
3041 public int size() {
3042 return (map.size());
3043 }
3044
3045 }
3046
3047
3048 // Private implementation of Set for FacetsMap.getKeySet().iterator()
3049 private static class FacetsMapKeySetIterator implements Iterator<String> {
3050
3051 public FacetsMapKeySetIterator(FacetsMap map) {
3052 this.map = map;
3053 this.iterator = map.keySetIterator();
3054 }
3055
3056 private FacetsMap map = null;
3057 private Iterator<String> iterator = null;
3058 private String last = null;
3059
3060 public boolean hasNext() {
3061 return (iterator.hasNext());
3062 }
3063
3064 public String next() {
3065 last = iterator.next();
3066 return (last);
3067 }
3068
3069 public void remove() {
3070 if (last == null) {
3071 throw new IllegalStateException();
3072 }
3073 map.remove(last);
3074 last = null;
3075 }
3076
3077 }
3078
3079
3080 // Private implementation of Collection for FacetsMap.values()
3081 private static class FacetsMapValues extends AbstractCollection<UIComponent> {
3082
3083 public FacetsMapValues(FacetsMap map) {
3084 this.map = map;
3085 }
3086
3087 private FacetsMap map;
3088
3089 public boolean add(UIComponent o) {
3090 throw new UnsupportedOperationException();
3091 }
3092
3093 public boolean addAll(Collection c) {
3094 throw new UnsupportedOperationException();
3095 }
3096
3097 public void clear() {
3098 map.clear();
3099 }
3100
3101 public boolean isEmpty() {
3102 return (map.isEmpty());
3103 }
3104
3105 public Iterator<UIComponent> iterator() {
3106 return (new FacetsMapValuesIterator(map));
3107 }
3108
3109 public int size() {
3110 return (map.size());
3111 }
3112
3113
3114 }
3115
3116
3117 // Private implementation of Iterator for FacetsMap.values().iterator()
3118 private static class FacetsMapValuesIterator implements Iterator<UIComponent> {
3119
3120 public FacetsMapValuesIterator(FacetsMap map) {
3121 this.map = map;
3122 this.iterator = map.keySetIterator();
3123 }
3124
3125 private FacetsMap map = null;
3126 private Iterator<String> iterator = null;
3127 private Object last = null;
3128
3129 public boolean hasNext() {
3130 return (iterator.hasNext());
3131 }
3132
3133 public UIComponent next() {
3134 last = iterator.next();
3135 return (map.get(last));
3136 }
3137
3138 public void remove() {
3139 if (last == null) {
3140 throw new IllegalStateException();
3141 }
3142 map.remove(last);
3143 last = null;
3144 }
3145
3146 }
3147
3148 // Private static member class that provide access to Behaviors.
3149 // Note that this Map must be unmodifiable to the external world,
3150 // but UIComponentBase itself needs to be able to write to the Map.
3151 // We solve these requirements wrapping the underlying modifiable
3152 // Map inside of a unmodifiable map and providing private access to
3153 // the underlying (modifable) Map
3154 private static class BehaviorsMap extends AbstractMap<String, List<ClientBehavior>>{
3155 private Map<String, List<ClientBehavior>> unmodifiableMap;
3156 private Map<String, List<ClientBehavior>> modifiableMap;
3157
3158 private BehaviorsMap(Map<String, List<ClientBehavior>> modifiableMap) {
3159 this.modifiableMap = modifiableMap;
3160 this.unmodifiableMap = Collections.unmodifiableMap(modifiableMap);
3161 }
3162
3163 public Set<Entry<String, List<ClientBehavior>>> entrySet() {
3164 return unmodifiableMap.entrySet();
3165 }
3166
3167 private Map<String, List<ClientBehavior>> getModifiableMap() {
3168 return modifiableMap;
3169 }
3170 }
3171
3172 }