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
40 import javax.el.ELException;
41 import javax.el.ValueExpression;
42 import javax.faces.FacesException;
43 import javax.faces.context.FacesContext;
44 import javax.faces.el.ValueBinding;
45 import javax.faces.event.AbortProcessingException;
46 import javax.faces.event.FacesEvent;
47 import javax.faces.event.FacesListener;
48 import javax.faces.render.Renderer;
49
50 import java.beans.IntrospectionException;
51 import java.beans.Introspector;
52 import java.beans.PropertyDescriptor;
53 import java.io.IOException;
54 import java.io.Serializable;
55 import java.io.ObjectOutputStream;
56 import java.io.ObjectInputStream;
57 import java.lang.reflect.InvocationTargetException;
58 import java.lang.reflect.Method;
59 import java.util.AbstractCollection;
60 import java.util.AbstractSet;
61 import java.util.ArrayList;
62 import java.util.Collection;
63 import java.util.Collections;
64 import java.util.HashMap;
65 import java.util.Iterator;
66 import java.util.List;
67 import java.util.ListIterator;
68 import java.util.Map;
69 import java.util.NoSuchElementException;
70 import java.util.Set;
71 import java.util.WeakHashMap;
72 import java.util.logging.Level;
73 import java.util.logging.Logger;
74
75 /**
76 * <p><strong>UIComponentBase</strong> is a convenience base class that
77 * implements the default concrete behavior of all methods defined by
78 * {@link UIComponent}.</p>
79 *
80 * <p>By default, this class defines <code>getRendersChildren()</code>
81 * to find the renderer for this component and call its
82 * <code>getRendersChildren()</code> method. The default implementation
83 * on the <code>Renderer</code> returns <code>false</code>. As of
84 * version 1.2 of the JavaServer Faces Specification, component authors
85 * are encouraged to return <code>true</code> from this method and rely
86 * on the implementation of {@link #encodeChildren} in this class and in
87 * the Renderer ({@link Renderer#encodeChildren}). Subclasses that wish
88 * to manage the rendering of their children should override this method
89 * to return <code>true</code> instead.</p>
90 */
91
92 public abstract class UIComponentBase extends UIComponent {
93
94
95 // -------------------------------------------------------------- Attributes
96
97 private static Logger log = Logger.getLogger("javax.faces.component",
98 "javax.faces.LogStrings");
99
100
101 /**
102 * <p>Each entry is an map of <code>PropertyDescriptor</code>s describing
103 * the properties of a concrete {@link UIComponent} implementation, keyed
104 * by the corresponding <code>java.lang.Class</code>.</p>
105 *
106 * <p><strong>IMPLEMENTATION NOTE</strong> - This is implemented as a
107 * <code>WeakHashMap</code> so that, even if this class is embedded in a
108 * container's class loader that is a parent to webapp class loaders,
109 * references to the classes will eventually expire.</p>
110 */
111 @SuppressWarnings({"CollectionWithoutInitialCapacity"})
112 private static Map<Class<?>, Map<String, PropertyDescriptor>>
113 descriptors =
114 new WeakHashMap<Class<?>, Map<String, PropertyDescriptor>>();
115
116 /**
117 * Reference to the map of <code>PropertyDescriptor</code>s for this class
118 * in the <code>descriptors<code> <code>Map<code>.
119 */
120 private Map<String,PropertyDescriptor> pdMap = null;
121
122 /**
123 * <p>An EMPTY_OBJECT_ARRAY argument list to be passed to reflection methods.</p>
124 */
125 private static final Object EMPTY_OBJECT_ARRAY[] = new Object[0];
126
127 public UIComponentBase() {
128 populateDescriptorsMapIfNecessary();
129 }
130
131 private void populateDescriptorsMapIfNecessary() {
132 Class<?> clazz = this.getClass();
133 pdMap = descriptors.get(clazz);
134 if (null != pdMap) {
135 return;
136 }
137
138 // load the property descriptors for this class.
139 PropertyDescriptor pd[] = getPropertyDescriptors();
140 if (pd != null) {
141 pdMap = new HashMap<String, PropertyDescriptor>(pd.length, 1.0f);
142 for (PropertyDescriptor aPd : pd) {
143 pdMap.put(aPd.getName(), aPd);
144 }
145 if (log.isLoggable(Level.FINE)) {
146 log.log(Level.FINE, "fine.component.populating_descriptor_map",
147 new Object[]{clazz,
148 Thread.currentThread().getName()});
149 }
150
151 // Check again
152 Map<String, PropertyDescriptor> reCheckMap =
153 descriptors.get(clazz);
154 if (null != reCheckMap) {
155 return;
156 }
157 descriptors.put(clazz, pdMap);
158 }
159
160
161 }
162
163
164 /**
165 * <p>Return an array of <code>PropertyDescriptors</code> for this
166 * {@link UIComponent}'s implementation class. If no descriptors
167 * can be identified, a zero-length array will be returned.</p>
168 *
169 * @throws FacesException if an introspection exception occurs
170 */
171 private PropertyDescriptor[] getPropertyDescriptors() {
172 PropertyDescriptor[] pd;
173 try {
174 pd = Introspector.getBeanInfo(this.getClass()).
175 getPropertyDescriptors();
176 } catch (IntrospectionException e) {
177 throw new FacesException(e);
178 }
179 return (pd);
180 }
181
182
183 /**
184 * <p>The <code>Map</code> containing our attributes, keyed by
185 * attribute name.</p>
186 */
187 private AttributesMap attributes = null;
188
189
190 public Map<String, Object> getAttributes() {
191
192 if (attributes == null) {
193 attributes = new AttributesMap(this);
194 }
195 return (attributes);
196
197 }
198
199
200 // ---------------------------------------------------------------- Bindings
201
202
203 /**
204 * {@inheritDoc}
205 * @throws NullPointerException {@inheritDoc}
206 * @deprecated This has been replaced by {@link #getValueExpression}.
207 */
208 public ValueBinding getValueBinding(String name) {
209
210 if (name == null) {
211 throw new NullPointerException();
212 }
213 ValueBinding result = null;
214 ValueExpression ve;
215
216 if (null != (ve = getValueExpression(name))) {
217 // if the ValueExpression is an instance of our private
218 // wrapper class.
219 if (ve.getClass().equals(ValueExpressionValueBindingAdapter.class)) {
220 result = ((ValueExpressionValueBindingAdapter)ve).getWrapped();
221 }
222 else {
223 // otherwise, this is a real ValueExpression. Wrap it
224 // in a ValueBinding.
225 result = new ValueBindingValueExpressionAdapter(ve);
226 }
227 }
228 return result;
229 }
230
231
232 /**
233 * {@inheritDoc}
234 * @throws IllegalArgumentException {@inheritDoc}
235 * @throws NullPointerException {@inheritDoc}
236 * @deprecated This has been replaced by {@link #setValueExpression}.
237 */
238 public void setValueBinding(String name, ValueBinding binding) {
239 if (name == null) {
240 throw new NullPointerException();
241 }
242 if (binding != null) {
243 ValueExpressionValueBindingAdapter adapter =
244 new ValueExpressionValueBindingAdapter(binding);
245 setValueExpression(name, adapter);
246 } else {
247 setValueExpression(name, null);
248 }
249
250 }
251
252
253 // -------------------------------------------------------------- Properties
254
255
256 /**
257 * <p>The assigned client identifier for this component.</p>
258 */
259 private String clientId = null;
260
261
262 /**
263 * @throws NullPointerException {@inheritDoc}
264 */
265 public String getClientId(FacesContext context) {
266
267 if (context == null) {
268 throw new NullPointerException();
269 }
270
271 // if the clientId is not yet set
272 if (this.clientId == null) {
273 UIComponent parent = this.getNamingContainer();
274 String parentId = null;
275
276 // give the parent the opportunity to first
277 // grab a unique clientId
278 if (parent != null) {
279 parentId = parent.getContainerClientId(context);
280 }
281
282 // now resolve our own client id
283 this.clientId = getId();
284 if (this.clientId == null) {
285 setId(context.getViewRoot().createUniqueId());
286 this.clientId = getId();
287 }
288 if (parentId != null) {
289 StringBuilder idBuilder =
290 new StringBuilder(parentId.length()
291 + 1
292 + this.clientId.length());
293 this.clientId = idBuilder.append(parentId)
294 .append(NamingContainer.SEPARATOR_CHAR)
295 .append(this.clientId).toString();
296 }
297
298 // allow the renderer to convert the clientId
299 Renderer renderer = this.getRenderer(context);
300 if (renderer != null) {
301 this.clientId = renderer.convertClientId(context, this.clientId);
302 }
303 }
304 return this.clientId;
305 }
306
307 /**
308 * <p>Private utilitity method for finding this
309 * <code>UIComponent</code>'s parent <code>NamingContainer</code>.
310 * This method may return <code>null</code> if there is not a
311 * parent <code>NamingContainer</code></p>
312 *
313 * @return the parent <code>NamingContainer</code>
314 */
315 private UIComponent getNamingContainer() {
316 UIComponent namingContainer = this.getParent();
317 while (namingContainer != null) {
318 if (namingContainer instanceof NamingContainer) {
319 return namingContainer;
320 }
321 namingContainer = namingContainer.getParent();
322 }
323 return null;
324 }
325
326
327
328 /**
329 * <p>The component identifier for this component.</p>
330 */
331 private String id = null;
332
333
334 public String getId() {
335
336 return (id);
337
338 }
339
340
341 /**
342 * @throws IllegalArgumentException {@inheritDoc}
343 * @throws IllegalStateException {@inheritDoc}
344 */
345 public void setId(String id) {
346
347 // if the current ID is not null, and the passed
348 // argument is the same, no need to validate it
349 // as it has already been validated.
350 if (this.id == null || !(this.id.equals(id))) {
351 validateId(id);
352 this.id = id;
353 }
354
355 this.clientId = null; // Erase any cached value
356
357 }
358
359
360 /**
361 * <p>The parent component for this component.</p>
362 */
363 private UIComponent parent = null;
364
365
366 public UIComponent getParent() {
367 return (this.parent);
368 }
369
370
371 public void setParent(UIComponent parent) {
372 this.parent = parent;
373 }
374
375
376 /**
377 * <p>The "should this component be rendered" flag.</p>
378 */
379 private boolean rendered = true;
380 private boolean renderedSet = false;
381
382 public boolean isRendered() {
383
384 if (renderedSet) {
385 return (rendered);
386 }
387 ValueExpression ve = getValueExpression("rendered");
388 if (ve != null) {
389 try {
390 return (!Boolean.FALSE.equals(ve.getValue(getFacesContext().getELContext())));
391 }
392 catch (ELException e) {
393 throw new FacesException(e);
394 }
395 } else {
396 return (this.rendered);
397 }
398
399 }
400
401
402 public void setRendered(boolean rendered) {
403
404 this.rendered = rendered;
405 this.renderedSet = true;
406
407 }
408
409
410 /**
411 * <p>The renderer type for this component.</p>
412 */
413 private String rendererType = null;
414
415
416 public String getRendererType() {
417
418 if (this.rendererType != null) {
419 return (this.rendererType);
420 }
421 ValueExpression ve = getValueExpression("rendererType");
422 if (ve != null) {
423 try {
424 return ((String) ve.getValue(getFacesContext().getELContext()));
425 }
426 catch (ELException e) {
427 throw new FacesException(e);
428 }
429 } else {
430 return (null);
431 }
432
433 }
434
435
436 public void setRendererType(String rendererType) {
437
438 this.rendererType = rendererType;
439
440 }
441
442
443 public boolean getRendersChildren() {
444 boolean result = false;
445
446 Renderer renderer;
447 if (getRendererType() != null) {
448 if (null !=
449 (renderer = getRenderer(getFacesContext()))) {
450 result = renderer.getRendersChildren();
451 }
452 }
453 return result;
454
455 }
456
457 // ------------------------------------------------- Tree Management Methods
458
459
460 /*
461 * <p>The <code>List</code> containing our child components.</p>
462 */
463 private List<UIComponent> children = null;
464
465
466 public List<UIComponent> getChildren() {
467
468 if (children == null) {
469 children = new ChildrenList(this);
470 }
471 return (children);
472
473 }
474
475
476 // Do not allocate the children List to answer this question
477 public int getChildCount() {
478
479 if (children != null) {
480 return (children.size());
481 } else {
482 return (0);
483 }
484
485 }
486
487
488 /**
489 * <p>If the specified {@link UIComponent} has a non-null parent,
490 * remove it as a child or facet (as appropriate) of that parent.
491 * As a result, the <code>parent</code> property will always be
492 * <code>null</code> when this method returns.</p>
493 *
494 * @param component {@link UIComponent} to have any parent erased
495 */
496 private static void eraseParent(UIComponent component) {
497
498 UIComponent parent = component.getParent();
499 if (parent == null) {
500 return;
501 }
502 if (parent.getChildCount() > 0) {
503 List children = parent.getChildren();
504 int index = children.indexOf(component);
505 if (index >= 0) {
506 children.remove(index);
507 return;
508 }
509 }
510 if (parent.getFacetCount() > 0) {
511 Map facets = parent.getFacets();
512 Iterator entries = facets.entrySet().iterator();
513 while (entries.hasNext()) {
514 Map.Entry entry = (Map.Entry) entries.next();
515 //noinspection ObjectEquality
516 if (entry.getValue() == component) {
517 entries.remove();
518 return;
519 }
520 }
521 }
522
523 // Throw an exception for the "cannot happen" case
524 throw new IllegalStateException("Parent was not null, " +
525 "but this component not related");
526
527 }
528
529 /**
530 * <p>Throw <code>IllegalArgumentException</code> if the specified
531 * component identifier is non-<code>null</code> and not
532 * syntactically valid. </p>
533 *
534 * @param id The component identifier to test
535 */
536 private static void validateId(String id) {
537
538 if (id == null) {
539 return;
540 }
541 int n = id.length();
542 if (n < 1) {
543 throw new IllegalArgumentException();
544 }
545 for (int i = 0; i < n; i++) {
546 char c = id.charAt(i);
547 if (i == 0) {
548 if (!Character.isLetter(c) && (c != '_')) {
549 throw new IllegalArgumentException(id);
550 }
551 } else {
552 if (!Character.isLetter(c) &&
553 !Character.isDigit(c) &&
554 (c != '-') && (c != '_')) {
555 throw new IllegalArgumentException(id);
556 }
557 }
558 }
559
560 }
561
562
563 private static final String SEPARATOR_STRING =
564 String.valueOf(NamingContainer.SEPARATOR_CHAR);
565
566 /**
567 * @throws NullPointerException {@inheritDoc}
568 */
569 public UIComponent findComponent(String expr) {
570 if (expr == null) {
571 throw new NullPointerException();
572 }
573
574 if (expr.length() == 0) {
575 // if an empty value is provided, fail fast.
576 throw new IllegalArgumentException("\"\"");
577 }
578
579 // Identify the base component from which we will perform our search
580 UIComponent base = this;
581 if (expr.charAt(0) == NamingContainer.SEPARATOR_CHAR) {
582 // Absolute searches start at the root of the tree
583 while (base.getParent() != null) {
584 base = base.getParent();
585 }
586 // Treat remainder of the expression as relative
587 expr = expr.substring(1);
588 } else {
589 // Relative expressions start at the closest NamingContainer or root
590 while (base.getParent() != null) {
591 if (base instanceof NamingContainer) {
592 break;
593 }
594 base = base.getParent();
595 }
596 }
597
598 // Evaluate the search expression (now guaranteed to be relative)
599 UIComponent result = null;
600 String[] segments = expr.split(SEPARATOR_STRING);
601 for (int i = 0, length = (segments.length - 1);
602 i < segments.length;
603 i++, length--) {
604 result = findComponent(base, segments[i], (i == 0));
605 // the first element of the expression may match base.id
606 // (vs. a child if of base)
607 if (i == 0 && result == null &&
608 segments[i].equals(base.getId())) {
609 result = base;
610 }
611 if (result != null && (!(result instanceof NamingContainer)) && length > 0) {
612 throw new IllegalArgumentException(segments[i]);
613 }
614 if (result == null) {
615 break;
616 }
617 base = result;
618 }
619
620 // Return the final result of our search
621 return (result);
622
623 }
624
625
626 /**
627 * <p>Return the {@link UIComponent} (if any) with the specified
628 * <code>id</code>, searching recursively starting at the specified
629 * <code>base</code>, and examining the base component itself, followed
630 * by examining all the base component's facets and children (unless
631 * the base component is a {@link NamingContainer}, in which case the
632 * recursive scan is skipped.</p>
633 *
634 * @param base Base {@link UIComponent} from which to search
635 * @param id Component identifier to be matched
636 */
637 private static UIComponent findComponent(UIComponent base,
638 String id,
639 boolean checkId) {
640 if (checkId && id.equals(base.getId())) {
641 return base;
642 }
643 // Search through our facets and children
644 UIComponent result = null;
645 for (Iterator i = base.getFacetsAndChildren(); i.hasNext(); ) {
646 UIComponent kid = (UIComponent) i.next();
647 if (!(kid instanceof NamingContainer)) {
648 if (checkId && id.equals(kid.getId())) {
649 result = kid;
650 break;
651 }
652 result = findComponent(kid, id, true);
653 if (result != null) {
654 break;
655 }
656 } else if (id.equals(kid.getId())) {
657 result = kid;
658 break;
659 }
660 }
661 return (result);
662
663 }
664
665 /**
666 * {@inheritDoc}
667 * @since 1.2
668 * @throws NullPointerException {@inheritDoc}
669 * @throws FacesException {@inheritDoc}
670 *
671 */
672 public boolean invokeOnComponent(FacesContext context, String clientId,
673 ContextCallback callback)
674 throws FacesException {
675 return super.invokeOnComponent(context, clientId, callback);
676 }
677
678
679 // ------------------------------------------------ Facet Management Methods
680
681
682 /*
683 * <p>The <code>Map</code> containing our related facet components.</p>
684 */
685 private Map<String, UIComponent> facets = null;
686
687
688 public Map<String, UIComponent> getFacets() {
689
690 if (facets == null) {
691 facets = new FacetsMap(this);
692 }
693 return (facets);
694
695 }
696
697 // Do not allocate the children List to answer this question
698 public int getFacetCount() {
699
700 if (facets != null) {
701 return (facets.size());
702 } else {
703 return (0);
704 }
705
706 }
707
708
709 // Do not allocate the facets Map to answer this question
710 public UIComponent getFacet(String name) {
711
712 if (facets != null) {
713 return (facets.get(name));
714 } else {
715 return (null);
716 }
717
718 }
719
720
721 public Iterator<UIComponent> getFacetsAndChildren() {
722
723 Iterator<UIComponent> result;
724 int childCount = this.getChildCount(),
725 facetCount = this.getFacetCount();
726 // If there are neither facets nor children
727 if (0 == childCount && 0 == facetCount) {
728 result = EMPTY_ITERATOR;
729 }
730 // If there are only facets and no children
731 else if (0 == childCount) {
732 Collection<UIComponent> unmodifiable =
733 Collections.unmodifiableCollection(getFacets().values());
734 result = unmodifiable.iterator();
735 }
736 // If there are only children and no facets
737 else if (0 == facetCount) {
738 List<UIComponent> unmodifiable =
739 Collections.unmodifiableList(getChildren());
740 result = unmodifiable.iterator();
741 }
742 // If there are both children and facets
743 else {
744 result = new FacetsAndChildrenIterator(this);
745 }
746 return result;
747 }
748
749
750 // -------------------------------------------- Lifecycle Processing Methods
751
752 /**
753 * @throws AbortProcessingException {@inheritDoc}
754 * @throws IllegalStateException {@inheritDoc}
755 * @throws NullPointerException {@inheritDoc}
756 */
757 public void broadcast(FacesEvent event)
758 throws AbortProcessingException {
759
760 if (event == null) {
761 throw new NullPointerException();
762 }
763 if (listeners == null) {
764 return;
765 }
766
767 Iterator<FacesListener> iter = listeners.iterator();
768 while (iter.hasNext()) {
769 FacesListener listener = iter.next();
770 if (event.isAppropriateListener(listener)) {
771 event.processListener(listener);
772 }
773 }
774 }
775
776
777 /**
778 * @throws NullPointerException {@inheritDoc}
779 */
780 public void decode(FacesContext context) {
781
782 if (context == null) {
783 throw new NullPointerException();
784 }
785 String rendererType = getRendererType();
786 if (rendererType != null) {
787 Renderer renderer = this.getRenderer(context);
788 if (renderer != null) {
789 renderer.decode(context, this);
790 }else {
791 // TODO: i18n
792 log.fine("Can't get Renderer for type " + rendererType);
793 }
794 }
795 }
796
797
798 /**
799 * @throws NullPointerException {@inheritDoc}
800 */
801 public void encodeBegin(FacesContext context) throws IOException {
802
803 if (context == null) {
804 throw new NullPointerException();
805 }
806 if (!isRendered()) {
807 return;
808 }
809 String rendererType = getRendererType();
810 if (rendererType != null) {
811 Renderer renderer = this.getRenderer(context);
812 if (renderer != null) {
813 renderer.encodeBegin(context, this);
814 } else {
815 // TODO: i18n
816 log.fine("Can't get Renderer for type " + rendererType);
817 }
818 }
819
820 }
821
822 /**
823 * @throws NullPointerException {@inheritDoc}
824 */
825 public void encodeChildren(FacesContext context) throws IOException {
826
827 if (context == null) {
828 throw new NullPointerException();
829 }
830 if (!isRendered()) {
831 return;
832 }
833 String rendererType = getRendererType();
834 if (rendererType != null) {
835 Renderer renderer = this.getRenderer(context);
836 if (renderer != null) {
837 renderer.encodeChildren(context, this);
838 } else {
839 // We've already logged for this component
840 }
841 }
842 }
843
844
845 /**
846 * @throws IOException {@inheritDoc}
847 * @throws NullPointerException {@inheritDoc}
848 */
849 public void encodeEnd(FacesContext context) throws IOException {
850
851 if (context == null) {
852 throw new NullPointerException();
853 }
854 if (!isRendered()) {
855 return;
856 }
857 String rendererType = getRendererType();
858 if (rendererType != null) {
859 Renderer renderer = this.getRenderer(context);
860 if (renderer != null) {
861 renderer.encodeEnd(context, this);
862 } else {
863 // We've already logged for this component
864 }
865 }
866
867 }
868
869 // -------------------------------------------------- Event Listener Methods
870
871
872 /**
873 * <p>Our {@link javax.faces.event.FacesListener}s. This data
874 * structure is lazily instantiated as necessary.</p>
875 */
876 private List<FacesListener> listeners;
877
878
879 /**
880 * <p>Add the specified {@link FacesListener} to the set of listeners
881 * registered to receive event notifications from this {@link UIComponent}.
882 * It is expected that {@link UIComponent} classes acting as event sources
883 * will have corresponding typesafe APIs for registering listeners of the
884 * required type, and the implementation of those registration methods
885 * will delegate to this method. For example:</p>
886 * <pre>
887 * public class FooEvent extends FacesEvent {
888 * ...
889 * protected boolean isAppropriateListener(FacesListener listener) {
890 * return (listener instanceof FooListener);
891 * }
892 * protected void processListener(FacesListener listener) {
893 * ((FooListener) listener).processFoo(this);
894 * }
895 * ...
896 * }
897 *
898 * public interface FooListener extends FacesListener {
899 * public void processFoo(FooEvent event);
900 * }
901 *
902 * public class FooComponent extends UIComponentBase {
903 * ...
904 * public void addFooListener(FooListener listener) {
905 * addFacesListener(listener);
906 * }
907 * public void removeFooListener(FooListener listener) {
908 * removeFacesListener(listener);
909 * }
910 * ...
911 * }
912 * </pre>
913 *
914 * @param listener The {@link FacesListener} to be registered
915 *
916 * @throws NullPointerException if <code>listener</code>
917 * is <code>null</code>
918 */
919 protected void addFacesListener(FacesListener listener) {
920
921 if (listener == null) {
922 throw new NullPointerException();
923 }
924 if (listeners == null) {
925 //noinspection CollectionWithoutInitialCapacity
926 listeners = new ArrayList<FacesListener>();
927 }
928 listeners.add(listener);
929
930 }
931
932
933 /**
934 * @throws IllegalArgumentException {@inheritDoc}
935 * @throws NullPointerException {@inheritDoc}
936 */
937 protected FacesListener[] getFacesListeners(Class clazz) {
938 if (clazz == null) {
939 throw new NullPointerException();
940 }
941 if (!FacesListener.class.isAssignableFrom(clazz)) {
942 throw new IllegalArgumentException();
943 }
944 if (listeners == null) {
945 return ((FacesListener[])
946 java.lang.reflect.Array.newInstance(clazz, 0));
947 }
948
949 //noinspection CollectionWithoutInitialCapacity
950 List<FacesListener> results = new ArrayList<FacesListener>();
951 Iterator<FacesListener> items = listeners.iterator();
952 while (items.hasNext()) {
953 FacesListener item = items.next();
954 if (((Class<?>)clazz).isAssignableFrom(item.getClass())) {
955 results.add(item);
956 }
957 }
958
959 return (results.toArray
960 ((FacesListener []) java.lang.reflect.Array.newInstance(clazz,
961 results.size())));
962
963 }
964
965
966 /**
967 * <p>Remove the specified {@link FacesListener} from the set of listeners
968 * registered to receive event notifications from this {@link UIComponent}.
969 *
970 * @param listener The {@link FacesListener} to be deregistered
971 *
972 * @throws NullPointerException if <code>listener</code>
973 * is <code>null</code>
974 */
975 protected void removeFacesListener(FacesListener listener) {
976
977 if (listener == null) {
978 throw new NullPointerException();
979 }
980 if (listeners == null) {
981 return;
982 }
983 listeners.remove(listener);
984 }
985
986 /**
987 * @throws IllegalStateException {@inheritDoc}
988 * @throws NullPointerException {@inheritDoc}
989 */
990 public void queueEvent(FacesEvent event) {
991
992 if (event == null) {
993 throw new NullPointerException();
994 }
995 UIComponent parent = getParent();
996 if (parent == null) {
997 throw new IllegalStateException();
998 } else {
999 parent.queueEvent(event);
1000 }
1001
1002 }
1003
1004
1005 // ------------------------------------------------ Lifecycle Phase Handlers
1006
1007
1008 /**
1009 * @throws NullPointerException {@inheritDoc}
1010 */
1011 public void processDecodes(FacesContext context) {
1012
1013 if (context == null) {
1014 throw new NullPointerException();
1015 }
1016
1017 // Skip processing if our rendered flag is false
1018 if (!isRendered()) {
1019 return;
1020 }
1021
1022 // Process all facets and children of this component
1023 Iterator kids = getFacetsAndChildren();
1024 while (kids.hasNext()) {
1025 UIComponent kid = (UIComponent) kids.next();
1026 kid.processDecodes(context);
1027 }
1028
1029 // Process this component itself
1030 try {
1031 decode(context);
1032 } catch (RuntimeException e) {
1033 context.renderResponse();
1034 throw e;
1035 }
1036
1037 }
1038
1039
1040 /**
1041 * @throws NullPointerException {@inheritDoc}
1042 */
1043 public void processValidators(FacesContext context) {
1044
1045 if (context == null) {
1046 throw new NullPointerException();
1047 }
1048
1049 // Skip processing if our rendered flag is false
1050 if (!isRendered()) {
1051 return;
1052 }
1053
1054 // Process all the facets and children of this component
1055 Iterator kids = getFacetsAndChildren();
1056 while (kids.hasNext()) {
1057 UIComponent kid = (UIComponent) kids.next();
1058 kid.processValidators(context);
1059 }
1060 }
1061
1062
1063 /**
1064 * @throws NullPointerException {@inheritDoc}
1065 */
1066 public void processUpdates(FacesContext context) {
1067
1068 if (context == null) {
1069 throw new NullPointerException();
1070 }
1071
1072 // Skip processing if our rendered flag is false
1073 if (!isRendered()) {
1074 return;
1075 }
1076
1077 // Process all facets and children of this component
1078 Iterator kids = getFacetsAndChildren();
1079 while (kids.hasNext()) {
1080 UIComponent kid = (UIComponent) kids.next();
1081 kid.processUpdates(context);
1082 }
1083 }
1084
1085 private static final int MY_STATE = 0;
1086 private static final int CHILD_STATE = 1;
1087
1088 /**
1089 * @throws NullPointerException {@inheritDoc}
1090 */
1091 public Object processSaveState(FacesContext context) {
1092
1093 if (context == null) {
1094 throw new NullPointerException();
1095 }
1096 if (this.isTransient()) {
1097 return null;
1098 }
1099 Object [] stateStruct = new Object[2];
1100 Object [] childState = EMPTY_ARRAY;
1101
1102 // Process this component itself
1103 stateStruct[MY_STATE] = saveState(context);
1104
1105 // determine if we have any children to store
1106 int count = this.getChildCount() + this.getFacetCount();
1107 if (count > 0) {
1108
1109 // this arraylist will store state
1110 List<Object> stateList = new ArrayList<Object>(count);
1111
1112 // if we have children, add them to the stateList
1113 if (this.getChildCount() > 0) {
1114 Iterator kids = getChildren().iterator();
1115 UIComponent kid;
1116 while (kids.hasNext()) {
1117 kid = (UIComponent) kids.next();
1118 if (!kid.isTransient()) {
1119 stateList.add(kid.processSaveState(context));
1120 }
1121 }
1122 }
1123
1124 // if we have facets, add them to the stateList
1125 if (this.getFacetCount() > 0) {
1126 Iterator myFacets = getFacets().entrySet().iterator();
1127 UIComponent facet;
1128 Object facetState;
1129 Object[] facetSaveState;
1130 Map.Entry entry;
1131 while (myFacets.hasNext()) {
1132 entry = (Map.Entry) myFacets.next();
1133 facet = (UIComponent) entry.getValue();
1134 if (!facet.isTransient()) {
1135 facetState = facet.processSaveState(context);
1136 facetSaveState = new Object[2];
1137 facetSaveState[0] = entry.getKey();
1138 facetSaveState[1] = facetState;
1139 stateList.add(facetSaveState);
1140 }
1141 }
1142 }
1143
1144 // finally, capture the stateList and replace the original,
1145 // EMPTY_OBJECT_ARRAY Object array
1146 childState = stateList.toArray();
1147 }
1148
1149 stateStruct[CHILD_STATE] = childState;
1150 return stateStruct;
1151 }
1152
1153 /**
1154 * @throws NullPointerException {@inheritDoc}
1155 */
1156 public void processRestoreState(FacesContext context,
1157 Object state) {
1158 if (context == null) {
1159 throw new NullPointerException();
1160 }
1161
1162 Object [] stateStruct = (Object []) state;
1163 Object [] childState = (Object []) stateStruct[CHILD_STATE];
1164
1165 // Process this component itself
1166 restoreState(context, stateStruct[MY_STATE]);
1167
1168 int i = 0;
1169
1170 // Process all the children of this component
1171 if (this.getChildCount() > 0) {
1172 Iterator kids = getChildren().iterator();
1173 while (kids.hasNext()) {
1174 UIComponent kid = (UIComponent) kids.next();
1175 if (kid.isTransient()) {
1176 continue;
1177 }
1178 Object currentState = childState[i++];
1179 if (currentState == null) {
1180 continue;
1181 }
1182 kid.processRestoreState(context, currentState);
1183 }
1184 }
1185
1186 // process all of the facets of this component
1187 if (this.getFacetCount() > 0) {
1188 int facetsSize = getFacets().size();
1189 int j = 0;
1190 Object[] facetSaveState;
1191 String facetName;
1192 UIComponent facet;
1193 Object facetState;
1194 while (j < facetsSize) {
1195 if (null != (facetSaveState = (Object[])childState[i++])) {
1196 facetName = (String) facetSaveState[0];
1197 facetState = facetSaveState[1];
1198 facet = getFacets().get(facetName);
1199 facet.processRestoreState(context, facetState);
1200 }
1201 ++j;
1202 }
1203 }
1204 }
1205
1206 // ------------------------------------------------------- Protected Methods
1207
1208
1209 protected FacesContext getFacesContext() {
1210
1211 // PENDING(edburns): we can't use the cache ivar because we
1212 // don't always know when to clear it. For example, in the
1213 // "save state in server" case, the UIComponent instances stick
1214 // around between requests, yielding stale facesContext
1215 // references. If there was some way to clear the facesContext
1216 // cache ivar for each node in the tree *after* the
1217 // render-response phase, then we could keep a cache ivar. As
1218 // it is now, we must always use the Thread Local Storage
1219 // solution.
1220
1221 return FacesContext.getCurrentInstance();
1222
1223 }
1224
1225
1226 protected Renderer getRenderer(FacesContext context) {
1227
1228 String rendererType = getRendererType();
1229 Renderer result = null;
1230 if (rendererType != null) {
1231 result = context.getRenderKit().getRenderer(getFamily(),
1232 rendererType);
1233 if (null == result) {
1234 if (log.isLoggable(Level.FINE)) {
1235 // PENDING(edburns): I18N
1236 log.fine("Can't get Renderer for type " + rendererType);
1237 }
1238 }
1239 } else {
1240 if (log.isLoggable(Level.FINE)) {
1241 String id = this.getId();
1242 id = (null != id) ? id : this.getClass().getName();
1243 // PENDING(edburns): I18N
1244 log.fine("No renderer-type for component " + id);
1245 }
1246 }
1247 return result;
1248 }
1249
1250
1251 // ----------------------------------------------------- StateHolder Methods
1252 private Object[] values;
1253
1254 public Object saveState(FacesContext context) {
1255
1256 if (values == null) {
1257 values = new Object[9];
1258 }
1259
1260 if (attributes != null) {
1261 Map backing = attributes.getBackingAttributes();
1262 if (backing != null && !backing.isEmpty()) {
1263 values[0] = backing;
1264 }
1265 }
1266 values[1] = saveBindingsState(context);
1267 values[2] = clientId;
1268 values[3] = id;
1269 values[4] = rendered ? Boolean.TRUE : Boolean.FALSE;
1270 values[5] = renderedSet ? Boolean.TRUE : Boolean.FALSE;
1271 values[6] = rendererType;
1272 values[7] = saveAttachedState(context, listeners);
1273 values[8] = attributesThatAreSet;
1274 assert(!transientFlag);
1275
1276 return (values);
1277 }
1278
1279
1280 public void restoreState(FacesContext context, Object state) {
1281
1282 values = (Object[]) state;
1283 // we need to get the map that knows how to handle attribute/property
1284 // transparency before we restore its values.
1285 if (values[0] != null) {
1286 attributes = new AttributesMap(this,
1287 (HashMap) TypedCollections.dynamicallyCastMap((Map) values[0],
1288 String.class,
1289 Object.class));
1290 }
1291 bindings = restoreBindingsState(context, values[1]);
1292 clientId = (String) values[2];
1293 id = (String) values[3];
1294 rendered = ((Boolean) values[4]).booleanValue();
1295 renderedSet = ((Boolean) values[5]).booleanValue();
1296 rendererType = (String) values[6];
1297 List<FacesListener> restoredListeners;
1298 if (null != (restoredListeners = TypedCollections.dynamicallyCastList((List)
1299 restoreAttachedState(context, values[7]), FacesListener.class))) {
1300 // if there were some listeners registered prior to this
1301 // method being invoked, merge them with the list to be
1302 // restored.
1303 if (null != listeners) {
1304 listeners.addAll(restoredListeners);
1305 }
1306 else {
1307 listeners = restoredListeners;
1308 }
1309 }
1310 attributesThatAreSet = (List<String>) values[8];
1311 }
1312
1313
1314 /**
1315 * <p>Flag indicating a desire to now participate in state saving.</p>
1316 */
1317 private boolean transientFlag = false;
1318
1319
1320 public boolean isTransient() {
1321
1322 return (this.transientFlag);
1323
1324 }
1325
1326
1327 public void setTransient(boolean transientFlag) {
1328
1329 this.transientFlag = transientFlag;
1330
1331 }
1332
1333 // -------------------------------------- Helper methods for state saving
1334
1335 // --------- methods used by UIComponents to save their attached Objects.
1336
1337 /**
1338 *
1339 * <p>This method is called by {@link UIComponent} subclasses that
1340 * want to save one or more attached objects. It is a convenience
1341 * method that does the work of saving attached objects that may or
1342 * may not implement the {@link StateHolder} interface. Using this
1343 * method implies the use of {@link #restoreAttachedState} to restore
1344 * the attached objects.</p>
1345 *
1346 * <p>This method supports saving attached objects of the following
1347 * type: <code>Object</code>s,
1348 * <code>null</code> values, and <code>Lists</code> of these
1349 * objects. If any contained objects are not <code>Lists</code>
1350 * and do not implement {@link StateHolder}, they must have
1351 * zero-argument public constructors. The exact structure of the
1352 * returned object is undefined and opaque, but will be serializable.
1353 * </p>
1354 *
1355 * @param context the {@link FacesContext} for this request.
1356 *
1357 * @param attachedObject the object, which may be a
1358 * <code>List</code> instance, or an Object. The
1359 * <code>attachedObject</code> (or the elements that comprise
1360 * <code>attachedObject</code> may implement {@link StateHolder}.
1361 *
1362 * @throws NullPointerException if the context argument is null.
1363 *
1364 */
1365
1366 public static Object saveAttachedState(FacesContext context,
1367 Object attachedObject) {
1368 if (null == context) {
1369 throw new NullPointerException();
1370 }
1371 if (null == attachedObject) {
1372 return null;
1373 }
1374 Object result;
1375
1376 if (attachedObject instanceof List) {
1377 List attachedList = (List) attachedObject;
1378 List<StateHolderSaver> resultList = new ArrayList<StateHolderSaver>(attachedList.size());
1379 Iterator listIter = attachedList.iterator();
1380 Object cur;
1381 while (listIter.hasNext()) {
1382 if (null != (cur = listIter.next())) {
1383 resultList.add(new StateHolderSaver(context, cur));
1384 }
1385 }
1386 result = resultList;
1387 }
1388 else {
1389 result = new StateHolderSaver(context, attachedObject);
1390 }
1391
1392 return result;
1393 }
1394
1395 /**
1396 *
1397 * <p>This method is called by {@link UIComponent} subclasses that
1398 * need to restore the objects they saved using {@link
1399 * #saveAttachedState}. This method is tightly coupled with {@link
1400 * #saveAttachedState}.</p>
1401 *
1402 * <p>This method supports restoring all attached objects types
1403 * supported by {@link #saveAttachedState}.</p>
1404 *
1405 * @param context the {@link FacesContext} for this request
1406 *
1407 * @param stateObj the opaque object returned from {@link
1408 * #saveAttachedState}
1409 *
1410 * @throws NullPointerException if context is null.
1411 *
1412 * @throws IllegalStateException if the object is not
1413 * previously returned by {@link #saveAttachedState}.
1414 *
1415 */
1416
1417 public static Object restoreAttachedState(FacesContext context,
1418 Object stateObj)
1419 throws IllegalStateException {
1420 if (null == context) {
1421 throw new NullPointerException();
1422 }
1423 if (null == stateObj) {
1424 return null;
1425 }
1426 Object result;
1427
1428 if (stateObj instanceof List) {
1429 List stateList = (List) stateObj;
1430 List<Object> retList = new ArrayList<Object>(stateList.size());
1431 for (Object item : stateList) {
1432 try {
1433 retList.add(((StateHolderSaver) item).restore(context));
1434 } catch (ClassCastException cce) {
1435 throw new IllegalStateException("Unknown object type");
1436 }
1437 }
1438 result = retList;
1439 } else if (stateObj instanceof StateHolderSaver) {
1440 StateHolderSaver saver = (StateHolderSaver) stateObj;
1441 result = saver.restore(context);
1442 } else {
1443 throw new IllegalStateException("Unknown object type");
1444 }
1445 return result;
1446 }
1447
1448 private static Map<String,ValueExpression> restoreBindingsState(FacesContext context, Object state) {
1449
1450 if (state == null) {
1451 return (null);
1452 }
1453 Object values[] = (Object[]) state;
1454 String names[] = (String[]) values[0];
1455 Object states[] = (Object[]) values[1];
1456 Map<String,ValueExpression> bindings = new HashMap<String,ValueExpression>(names.length);
1457 for (int i = 0; i < names.length; i++) {
1458 bindings.put(names[i],
1459 (ValueExpression) restoreAttachedState(context, states[i]));
1460 }
1461 return (bindings);
1462
1463 }
1464
1465
1466 private Object saveBindingsState(FacesContext context) {
1467
1468 if (bindings == null) {
1469 return (null);
1470 }
1471
1472 Object values[] = new Object[2];
1473 values[0] = bindings.keySet().toArray(new String[bindings.size()]);
1474
1475 Object[] bindingValues = bindings.values().toArray();
1476 for (int i = 0; i < bindingValues.length; i++) {
1477 bindingValues[i] = saveAttachedState(context, bindingValues[i]);
1478 }
1479
1480 values[1] = bindingValues;
1481
1482 return (values);
1483
1484 }
1485
1486
1487 Map<String,PropertyDescriptor> getDescriptorMap() {
1488 return pdMap;
1489 }
1490
1491
1492 // --------------------------------------------------------- Private Classes
1493
1494 // For state saving
1495 private final static Object[] EMPTY_ARRAY = new Object[0];
1496
1497 // Empty iterator for short circuiting operations
1498 private final static Iterator<UIComponent> EMPTY_ITERATOR = new Iterator<UIComponent>() {
1499
1500 public void remove() {
1501 throw new UnsupportedOperationException();
1502 }
1503
1504 public UIComponent next() {
1505 throw new NoSuchElementException("Empty Iterator");
1506 }
1507
1508 public boolean hasNext() {
1509 return false;
1510 }
1511 };
1512
1513 // Private implementation of Map that supports the functionality
1514 // required by UIComponent.getFacets()
1515 // HISTORY:
1516 // Versions 1.333 and older used inheritence to provide the
1517 // basic map functionality. This was wasteful since a
1518 // component could be completely configured via ValueExpressions
1519 // or (Bindings) which means an EMPTY_OBJECT_ARRAY Map would always be
1520 // present when it wasn't needed. By using composition,
1521 // we control if and when the Map is instantiated thereby
1522 // reducing uneeded object allocation. This change also
1523 // has a nice side effect in state saving since we no
1524 // longer need to duplicate the map, we just provide the
1525 // private 'attributes' map directly to the state saving process.
1526 private static class AttributesMap implements Map<String, Object>, Serializable {
1527
1528 // this KEY is special to the AttributesMap - this allows the implementation
1529 // to access the the List containing the attributes that have been set
1530 private static final String ATTRIBUTES_THAT_ARE_SET_KEY =
1531 UIComponentBase.class.getName() + ".attributesThatAreSet";
1532
1533 private HashMap<String, Object> attributes;
1534 private transient Map<String,PropertyDescriptor> pdMap;
1535 private transient UIComponent component;
1536 private static final long serialVersionUID = -6773035086539772945L;
1537
1538 // -------------------------------------------------------- Constructors
1539
1540 private AttributesMap(UIComponent component) {
1541
1542 this.component = component;
1543 this.pdMap = ((UIComponentBase) component).getDescriptorMap();
1544
1545 }
1546
1547 private AttributesMap(UIComponent component,
1548 HashMap<String,Object> attributes) {
1549 this(component);
1550 this.attributes = attributes;
1551 }
1552
1553 public boolean containsKey(Object keyObj) {
1554 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(keyObj)) {
1555 return true;
1556 }
1557 String key = (String) keyObj;
1558 PropertyDescriptor pd =
1559 getPropertyDescriptor(key);
1560 if (pd == null) {
1561 if (attributes != null) {
1562 return attributes.containsKey(key);
1563 } else {
1564 return (false);
1565 }
1566 } else {
1567 return (false);
1568 }
1569 }
1570
1571 public Object get(Object keyObj) {
1572 String key = (String) keyObj;
1573 if (key == null) {
1574 throw new NullPointerException();
1575 }
1576 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) {
1577 return component.getAttributesThatAreSet(false);
1578 }
1579 PropertyDescriptor pd =
1580 getPropertyDescriptor(key);
1581 if (pd != null) {
1582 try {
1583 Method readMethod = pd.getReadMethod();
1584 if (readMethod != null) {
1585 return (readMethod.invoke
1586 (component, EMPTY_OBJECT_ARRAY));
1587 } else {
1588 throw new IllegalArgumentException(key);
1589 }
1590 } catch (IllegalAccessException e) {
1591 throw new FacesException(e);
1592 } catch (InvocationTargetException e) {
1593 throw new FacesException
1594 (e.getTargetException());
1595 }
1596 } else if (attributes != null) {
1597 if (attributes.containsKey(key)) {
1598 return (attributes.get(key));
1599 }
1600 }
1601 ValueExpression ve = component.getValueExpression(key);
1602 if (ve != null) {
1603 try {
1604 return ve.getValue(component.getFacesContext().getELContext());
1605 }
1606 catch (ELException e) {
1607 throw new FacesException(e);
1608 }
1609 }
1610 return (null);
1611 }
1612
1613 public Object put(String keyValue, Object value) {
1614 if (keyValue == null) {
1615 throw new NullPointerException();
1616 }
1617
1618 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(keyValue)) {
1619 if (component.attributesThatAreSet == null) {
1620 if (value instanceof List) {
1621 //noinspection unchecked
1622 component.attributesThatAreSet = (List<String>) value;
1623 return component.attributesThatAreSet;
1624 }
1625 }
1626 return null;
1627 }
1628
1629 PropertyDescriptor pd =
1630 getPropertyDescriptor(keyValue);
1631 if (pd != null) {
1632 try {
1633 Object result = null;
1634 Method readMethod = pd.getReadMethod();
1635 if (readMethod != null) {
1636 result = readMethod.invoke
1637 (component, EMPTY_OBJECT_ARRAY);
1638 }
1639 Method writeMethod = pd.getWriteMethod();
1640 if (writeMethod != null) {
1641 writeMethod.invoke
1642 (component, value);
1643 } else {
1644 // TODO: i18n
1645 throw new IllegalArgumentException("Setter not found for property " + keyValue);
1646 }
1647 return (result);
1648 } catch (IllegalAccessException e) {
1649 throw new FacesException(e);
1650 } catch (InvocationTargetException e) {
1651 throw new FacesException
1652 (e.getTargetException());
1653 }
1654 } else {
1655 if (value == null) {
1656 throw new NullPointerException();
1657 }
1658 if (attributes == null) {
1659 initMap();
1660 }
1661 List<String> sProperties = component.getAttributesThatAreSet(true);
1662 if (sProperties != null && !sProperties.contains(keyValue)) {
1663 sProperties.add(keyValue);
1664 }
1665 return (attributes.put(keyValue, value));
1666 }
1667 }
1668
1669 public void putAll(Map<? extends String, ?> map) {
1670 if (map == null) {
1671 throw new NullPointerException();
1672 }
1673
1674 for (Map.Entry<? extends String, ?> entry : map.entrySet()) {
1675 this.put(entry.getKey(), entry.getValue());
1676 }
1677 }
1678
1679 public Object remove(Object keyObj) {
1680 String key = (String) keyObj;
1681 if (key == null) {
1682 throw new NullPointerException();
1683 }
1684 if (ATTRIBUTES_THAT_ARE_SET_KEY.equals(key)) {
1685 return null;
1686 }
1687 PropertyDescriptor pd =
1688 getPropertyDescriptor(key);
1689 if (pd != null) {
1690 throw new IllegalArgumentException(key);
1691 } else {
1692 if (attributes != null) {
1693 List<String> sProperties = component.getAttributesThatAreSet(false);
1694 if (sProperties != null) {
1695 sProperties.remove(key);
1696 }
1697 return (attributes.remove(key));
1698 } else {
1699 return null;
1700 }
1701 }
1702 }
1703
1704
1705 public int size() {
1706 return (attributes != null ? attributes.size() : 0);
1707 }
1708
1709 public boolean isEmpty() {
1710 return (attributes == null || attributes.isEmpty());
1711 }
1712
1713 public boolean containsValue(java.lang.Object value) {
1714 return (attributes != null && attributes.containsValue(value));
1715 }
1716
1717 public void clear() {
1718 if (attributes != null) {
1719 attributes.clear();
1720 }
1721 }
1722
1723 public Set<String> keySet() {
1724 if (attributes != null)
1725 return attributes.keySet();
1726 return Collections.emptySet();
1727 }
1728
1729 public Collection<Object> values() {
1730 if (attributes != null)
1731 return attributes.values();
1732 return Collections.emptyList();
1733 }
1734
1735 public Set<Entry<String,Object>> entrySet() {
1736 if (attributes != null)
1737 return attributes.entrySet();
1738 return Collections.emptySet();
1739 }
1740
1741 Map getBackingAttributes() {
1742 return attributes;
1743 }
1744
1745 public boolean equals(Object o) {
1746 if (o == this) {
1747 return true;
1748 }
1749
1750 if (!(o instanceof Map)) {
1751 return false;
1752 }
1753 Map t = (Map) o;
1754 if (t.size() != size()) {
1755 return false;
1756 }
1757
1758 try {
1759 for (Object e : entrySet()) {
1760 Entry entry = (Entry) e;
1761 Object key = entry.getKey();
1762 Object value = entry.getValue();
1763 if (value == null) {
1764 if (!(t.get(key) == null && t.containsKey(key))) {
1765 return false;
1766 }
1767 } else {
1768 if (!value.equals(t.get(key))) {
1769 return false;
1770 }
1771 }
1772 }
1773 } catch (ClassCastException unused) {
1774 return false;
1775 } catch (NullPointerException unused) {
1776 return false;
1777 }
1778
1779 return true;
1780 }
1781
1782
1783 public int hashCode() {
1784 int h = 0;
1785 for (Object o : entrySet()) {
1786 h += o.hashCode();
1787 }
1788 return h;
1789 }
1790
1791 private void initMap() {
1792 attributes = new HashMap<String,Object>(8);
1793 }
1794
1795 /**
1796 * <p>Return the <code>PropertyDescriptor</code> for the specified
1797 * property name for this {@link UIComponent}'s implementation class,
1798 * if any; otherwise, return <code>null</code>.</p>
1799 *
1800 * @param name Name of the property to return a descriptor for
1801 * @throws FacesException if an introspection exception occurs
1802 */
1803 PropertyDescriptor getPropertyDescriptor(String name) {
1804 if (pdMap != null) {
1805 return (pdMap.get(name));
1806 }
1807 return (null);
1808 }
1809
1810 // ----------------------------------------------- Serialization Methods
1811
1812 // This is dependent on serialization occuring with in a
1813 // a Faces request, however, since UIComponentBase.{save,restore}State()
1814 // don't actually serialize the AttributesMap, these methods are here
1815 // purely to be good citizens.
1816 private void writeObject(ObjectOutputStream out) throws IOException {
1817 if (attributes == null) {
1818 out.writeObject(new HashMap(1, 1.0f));
1819 } else {
1820 out.writeObject(attributes);
1821 }
1822 out.writeObject(component.getClass());
1823 //noinspection NonSerializableObjectPassedToObjectStream
1824 out.writeObject(component.saveState(FacesContext.getCurrentInstance()));
1825 }
1826
1827 private void readObject(ObjectInputStream in)
1828 throws IOException, ClassNotFoundException {
1829 attributes = null;
1830 pdMap = null;
1831 component = null;
1832 attributes = (HashMap) in.readObject();
1833 Class clazz = (Class) in.readObject();
1834 try {
1835 component = (UIComponent) clazz.newInstance();
1836 } catch (Exception e) {
1837 throw new RuntimeException(e);
1838 }
1839 component.restoreState(FacesContext.getCurrentInstance(), in.readObject());
1840 }
1841 }
1842
1843
1844 // Private implementation of List that supports the functionality
1845 // required by UIComponent.getChildren()
1846 private static class ChildrenList extends ArrayList<UIComponent> {
1847
1848 private UIComponent component;
1849
1850 public ChildrenList(UIComponent component) {
1851 super(6);
1852 this.component = component;
1853 }
1854
1855 public void add(int index, UIComponent element) {
1856 if (element == null) {
1857 throw new NullPointerException();
1858 } else if ((index < 0) || (index > size())) {
1859 throw new IndexOutOfBoundsException();
1860 } else {
1861 eraseParent(element);
1862 element.setParent(component);
1863 super.add(index, element);
1864 }
1865 }
1866
1867 public boolean add(UIComponent element) {
1868 if (element == null) {
1869 throw new NullPointerException();
1870 } else {
1871 eraseParent(element);
1872 element.setParent(component);
1873 return (super.add(element));
1874 }
1875 }
1876
1877 public boolean addAll(Collection<? extends UIComponent> collection) {
1878 Iterator<UIComponent> elements =
1879 (new ArrayList<UIComponent>(collection)).iterator();
1880 boolean changed = false;
1881 while (elements.hasNext()) {
1882 UIComponent element = elements.next();
1883 if (element == null) {
1884 throw new NullPointerException();
1885 } else {
1886 add(element);
1887 changed = true;
1888 }
1889 }
1890 return (changed);
1891 }
1892
1893 public boolean addAll(int index, Collection<? extends UIComponent> collection) {
1894 Iterator<UIComponent> elements =
1895 (new ArrayList<UIComponent>(collection)).iterator();
1896 boolean changed = false;
1897 while (elements.hasNext()) {
1898 UIComponent element = elements.next();
1899 if (element == null) {
1900 throw new NullPointerException();
1901 } else {
1902 add(index++, element);
1903 changed = true;
1904 }
1905 }
1906 return (changed);
1907 }
1908
1909 public void clear() {
1910 int n = size();
1911 if (n < 1) {
1912 return;
1913 }
1914 for (int i = 0; i < n; i++) {
1915 UIComponent child = get(i);
1916 child.setParent(null);
1917 }
1918 super.clear();
1919 }
1920
1921 public Iterator<UIComponent> iterator() {
1922 return (new ChildrenListIterator(this));
1923 }
1924
1925 public ListIterator<UIComponent> listIterator() {
1926 return (new ChildrenListIterator(this));
1927 }
1928
1929 public ListIterator<UIComponent> listIterator(int index) {
1930 return (new ChildrenListIterator(this, index));
1931 }
1932
1933 public UIComponent remove(int index) {
1934 UIComponent child = get(index);
1935 super.remove(index);
1936 child.setParent(null);
1937 return (child);
1938 }
1939
1940 public boolean remove(Object elementObj) {
1941 UIComponent element = (UIComponent) elementObj;
1942 if (element == null) {
1943 throw new NullPointerException();
1944 }
1945
1946 if (super.remove(element)) {
1947 element.setParent(null);
1948 return (true);
1949 } else {
1950 return (false);
1951 }
1952 }
1953
1954 public boolean removeAll(Collection<?> collection) {
1955 boolean result = false;
1956 Iterator<?> elements = collection.iterator();
1957 while (elements.hasNext()) {
1958 if (remove(elements.next())) {
1959 result = true;
1960 }
1961 }
1962 return (result);
1963 }
1964
1965 public boolean retainAll(Collection<?> collection) {
1966 boolean modified = false;
1967 Iterator<?> items = iterator();
1968 while (items.hasNext()) {
1969 if (!collection.contains(items.next())) {
1970 items.remove();
1971 modified = true;
1972 }
1973 }
1974 return (modified);
1975 }
1976
1977 public UIComponent set(int index, UIComponent element) {
1978 if (element == null) {
1979 throw new NullPointerException();
1980 } else if ((index < 0) || (index >= size())) {
1981 throw new IndexOutOfBoundsException();
1982 } else {
1983 eraseParent(element);
1984 UIComponent previous = get(index);
1985 previous.setParent(null);
1986 element.setParent(component);
1987 super.set(index, element);
1988 return (previous);
1989 }
1990 }
1991
1992 }
1993
1994
1995 // Private implementation of ListIterator for ChildrenList
1996 private static class ChildrenListIterator implements ListIterator<UIComponent> {
1997
1998
1999 public ChildrenListIterator(ChildrenList list) {
2000 this.list = list;
2001 this.index = 0;
2002 }
2003
2004 public ChildrenListIterator(ChildrenList list, int index) {
2005 this.list = list;
2006 if ((index < 0) || (index >= list.size())) {
2007 throw new IndexOutOfBoundsException(String.valueOf(index));
2008 } else {
2009 this.index = index;
2010 }
2011 }
2012
2013
2014 private ChildrenList list;
2015 private int index;
2016 private int last = -1; // Index last returned by next() or previous()
2017
2018 // Iterator methods
2019
2020 public boolean hasNext() {
2021 return (index < list.size());
2022 }
2023
2024 public UIComponent next() {
2025 try {
2026 UIComponent o = list.get(index);
2027 last = index++;
2028 return (o);
2029 } catch (IndexOutOfBoundsException e) {
2030 throw new NoSuchElementException(String.valueOf(index));
2031 }
2032 }
2033
2034 public void remove() {
2035 if (last == -1) {
2036 throw new IllegalStateException();
2037 }
2038 list.remove(last);
2039 if (last < index) {
2040 index--;
2041 }
2042 last = -1;
2043 }
2044
2045 // ListIterator methods
2046
2047 public void add(UIComponent o) {
2048 last = -1;
2049 list.add(index++, o);
2050 }
2051
2052 public boolean hasPrevious() {
2053 return (index > 1);
2054 }
2055
2056 public int nextIndex() {
2057 return (index);
2058 }
2059
2060 public UIComponent previous() {
2061 try {
2062 int current = index - 1;
2063 UIComponent o = list.get(current);
2064 last = current;
2065 index = current;
2066 return (o);
2067 } catch (IndexOutOfBoundsException e) {
2068 throw new NoSuchElementException();
2069 }
2070 }
2071
2072 public int previousIndex() {
2073 return (index - 1);
2074 }
2075
2076 public void set(UIComponent o) {
2077 if (last == -1) {
2078 throw new IllegalStateException();
2079 }
2080 list.set(last, o);
2081 }
2082
2083 }
2084
2085
2086 // Private implementation of Iterator for getFacetsAndChildren()
2087 private final static class FacetsAndChildrenIterator implements Iterator<UIComponent> {
2088
2089 private Iterator<UIComponent> iterator;
2090 private boolean childMode;
2091 private UIComponent c;
2092
2093 public FacetsAndChildrenIterator(UIComponent c) {
2094 this.c = c;
2095 this.childMode = false;
2096 }
2097
2098 private void update() {
2099 if (this.iterator == null) {
2100 // we must guarantee that 'iterator' is never null
2101 if (this.c.getFacetCount() != 0) {
2102 this.iterator = this.c.getFacets().values().iterator();
2103 this.childMode = false;
2104 } else if (this.c.getChildCount() != 0) {
2105 this.iterator = this.c.getChildren().iterator();
2106 this.childMode = true;
2107 } else {
2108 this.iterator = EMPTY_ITERATOR;
2109 this.childMode = true;
2110 }
2111 } else if (!this.childMode
2112 && !this.iterator.hasNext()
2113 && this.c.getChildCount() != 0) {
2114 this.iterator = this.c.getChildren().iterator();
2115 this.childMode = true;
2116 }
2117 }
2118
2119 public boolean hasNext() {
2120 this.update();
2121 return this.iterator.hasNext();
2122 }
2123
2124 public UIComponent next() {
2125 this.update();
2126 return this.iterator.next();
2127 }
2128
2129 public void remove() {
2130 throw new UnsupportedOperationException();
2131 }
2132
2133 }
2134
2135
2136 // Private implementation of Map that supports the functionality
2137 // required by UIComponent.getFacets()
2138 private static class FacetsMap extends HashMap<String, UIComponent> {
2139
2140 UIComponent component;
2141
2142 public FacetsMap(UIComponent component) {
2143 super(3, 1.0f);
2144 this.component = component;
2145 }
2146
2147 public void clear() {
2148 Iterator<String> keys = keySet().iterator();
2149 while (keys.hasNext()) {
2150 keys.next();
2151 keys.remove();
2152 }
2153 super.clear();
2154 }
2155
2156 public Set<Map.Entry<String, UIComponent>> entrySet() {
2157 return (new FacetsMapEntrySet(this));
2158 }
2159
2160 public Set<String> keySet() {
2161 return (new FacetsMapKeySet(this));
2162 }
2163
2164 public UIComponent put(String key, UIComponent value) {
2165 if ((key == null) || (value == null)) {
2166 throw new NullPointerException();
2167 } else //noinspection ConstantConditions
2168 if (!(key instanceof String) ||
2169 !(value instanceof UIComponent)) {
2170 throw new ClassCastException();
2171 }
2172 UIComponent previous = super.get(key);
2173 if (previous != null) {
2174 previous.setParent(null);
2175 }
2176 eraseParent(value);
2177 value.setParent(component);
2178 return (super.put(key, value));
2179 }
2180
2181 public void putAll(Map<? extends String, ? extends UIComponent> map) {
2182 if (map == null) {
2183 throw new NullPointerException();
2184 }
2185 for (Map.Entry<? extends String, ? extends UIComponent> entry : map.entrySet()) {
2186 put(entry.getKey(), entry.getValue());
2187 }
2188 }
2189
2190 public UIComponent remove(Object key) {
2191 UIComponent previous = get(key);
2192 if (previous != null) {
2193 previous.setParent(null);
2194 }
2195 super.remove(key);
2196 return (previous);
2197 }
2198
2199 public Collection<UIComponent> values() {
2200 return (new FacetsMapValues(this));
2201 }
2202
2203 Iterator<String> keySetIterator() {
2204 return ((new ArrayList<String>(super.keySet())).iterator());
2205 }
2206
2207 }
2208
2209
2210 // Private implementation of Set for FacetsMap.getEntrySet()
2211 private static class FacetsMapEntrySet extends AbstractSet<Map.Entry<String, UIComponent>> {
2212
2213 public FacetsMapEntrySet(FacetsMap map) {
2214 this.map = map;
2215 }
2216
2217 private FacetsMap map = null;
2218
2219 public boolean add(Map.Entry<String, UIComponent> o) {
2220 throw new UnsupportedOperationException();
2221 }
2222
2223 public boolean addAll(Collection<? extends Map.Entry<String,UIComponent>> c) {
2224 throw new UnsupportedOperationException();
2225 }
2226
2227 public void clear() {
2228 map.clear();
2229 }
2230
2231 public boolean contains(Object o) {
2232 if (o == null) {
2233 throw new NullPointerException();
2234 }
2235 if (!(o instanceof Map.Entry)) {
2236 return (false);
2237 }
2238 Map.Entry e = (Map.Entry) o;
2239 Object k = e.getKey();
2240 Object v = e.getValue();
2241 if (!map.containsKey(k)) {
2242 return (false);
2243 }
2244 if (v == null) {
2245 return (map.get(k) == null);
2246 } else {
2247 return (v.equals(map.get(k)));
2248 }
2249 }
2250
2251 public boolean isEmpty() {
2252 return (map.isEmpty());
2253 }
2254
2255 public Iterator<Map.Entry<String, UIComponent>> iterator() {
2256 return (new FacetsMapEntrySetIterator(map));
2257 }
2258
2259 public boolean remove(Object o) {
2260 if (o == null) {
2261 throw new NullPointerException();
2262 }
2263 if (!(o instanceof Map.Entry)) {
2264 return (false);
2265 }
2266 Object k = ((Map.Entry) o).getKey();
2267 if (map.containsKey(k)) {
2268 map.remove(k);
2269 return (true);
2270 } else {
2271 return (false);
2272 }
2273 }
2274
2275 public boolean removeAll(Collection c) {
2276 boolean result = false;
2277 Iterator v = c.iterator();
2278 while (v.hasNext()) {
2279 if (remove(v.next())) {
2280 result = true;
2281 }
2282 }
2283 return (result);
2284 }
2285
2286 public boolean retainAll(Collection c) {
2287 boolean result = false;
2288 Iterator v = iterator();
2289 while (v.hasNext()) {
2290 if (!c.contains(v.next())) {
2291 v.remove();
2292 result = true;
2293 }
2294 }
2295 return (result);
2296 }
2297
2298 public int size() {
2299 return (map.size());
2300 }
2301
2302 }
2303
2304
2305 // Private implementation of Map.Entry for FacetsMapEntrySet
2306 private static class FacetsMapEntrySetEntry implements Map.Entry<String, UIComponent> {
2307
2308 public FacetsMapEntrySetEntry(FacetsMap map, String key) {
2309 this.map = map;
2310 this.key = key;
2311 }
2312
2313 private FacetsMap map;
2314 private String key;
2315
2316 public boolean equals(Object o) {
2317 if (o == null) {
2318 return (false);
2319 }
2320 if (!(o instanceof Map.Entry)) {
2321 return (false);
2322 }
2323 Map.Entry e = (Map.Entry) o;
2324 if (key == null) {
2325 if (e.getKey() != null) {
2326 return (false);
2327 }
2328 } else {
2329 if (!key.equals(e.getKey())) {
2330 return (false);
2331 }
2332 }
2333 UIComponent v = map.get(key);
2334 if (v == null) {
2335 if (e.getValue() != null) {
2336 return (false);
2337 }
2338 } else {
2339 if (!v.equals(e.getValue())) {
2340 return (false);
2341 }
2342 }
2343 return (true);
2344 }
2345
2346 public String getKey() {
2347 return (key);
2348 }
2349
2350 public UIComponent getValue() {
2351 return (map.get(key));
2352 }
2353
2354 public int hashCode() {
2355 Object value = map.get(key);
2356 return (((key == null) ? 0 : key.hashCode()) ^
2357 ((value == null) ? 0 : value.hashCode()));
2358 }
2359
2360 public UIComponent setValue(UIComponent value) {
2361 UIComponent previous = map.get(key);
2362 map.put(key, value);
2363 return (previous);
2364 }
2365
2366 }
2367
2368
2369 // Private implementation of Set for FacetsMap.getEntrySet().iterator()
2370 private static class FacetsMapEntrySetIterator implements Iterator<Map.Entry<String, UIComponent>> {
2371
2372 public FacetsMapEntrySetIterator(FacetsMap map) {
2373 this.map = map;
2374 this.iterator = map.keySetIterator();
2375 }
2376
2377 private FacetsMap map = null;
2378 private Iterator<String> iterator = null;
2379 private Map.Entry<String, UIComponent> last = null;
2380
2381 public boolean hasNext() {
2382 return (iterator.hasNext());
2383 }
2384
2385 public Map.Entry<String, UIComponent> next() {
2386 last = new FacetsMapEntrySetEntry(map, iterator.next());
2387 return (last);
2388 }
2389
2390 public void remove() {
2391 if (last == null) {
2392 throw new IllegalStateException();
2393 }
2394 map.remove(((Map.Entry) last).getKey());
2395 last = null;
2396 }
2397
2398 }
2399
2400
2401 // Private implementation of Set for FacetsMap.getKeySet()
2402 private static class FacetsMapKeySet extends AbstractSet<String> {
2403
2404 public FacetsMapKeySet(FacetsMap map) {
2405 this.map = map;
2406 }
2407
2408 private FacetsMap map = null;
2409
2410 public boolean add(String o) {
2411 throw new UnsupportedOperationException();
2412 }
2413
2414 public boolean addAll(Collection<? extends String> c) {
2415 throw new UnsupportedOperationException();
2416 }
2417
2418 public void clear() {
2419 map.clear();
2420 }
2421
2422 public boolean contains(Object o) {
2423 return (map.containsKey(o));
2424 }
2425
2426 public boolean containsAll(Collection c) {
2427 Iterator v = c.iterator();
2428 while (v.hasNext()) {
2429 if (!map.containsKey(v.next())) {
2430 return (false);
2431 }
2432 }
2433 return (true);
2434 }
2435
2436 public boolean isEmpty() {
2437 return (map.isEmpty());
2438 }
2439
2440 public Iterator<String> iterator() {
2441 return (new FacetsMapKeySetIterator(map));
2442 }
2443
2444 public boolean remove(Object o) {
2445 if (map.containsKey(o)) {
2446 map.remove(o);
2447 return (true);
2448 } else {
2449 return (false);
2450 }
2451 }
2452
2453 public boolean removeAll(Collection c) {
2454 boolean result = false;
2455 Iterator v = c.iterator();
2456 while (v.hasNext()) {
2457 Object o = v.next();
2458 if (map.containsKey(o)) {
2459 map.remove(o);
2460 result = true;
2461 }
2462 }
2463 return (result);
2464 }
2465
2466 public boolean retainAll(Collection c) {
2467 boolean result = false;
2468 Iterator v = iterator();
2469 while (v.hasNext()) {
2470 if (!c.contains(v.next())) {
2471 v.remove();
2472 result = true;
2473 }
2474 }
2475 return (result);
2476 }
2477
2478 public int size() {
2479 return (map.size());
2480 }
2481
2482 }
2483
2484
2485 // Private implementation of Set for FacetsMap.getKeySet().iterator()
2486 private static class FacetsMapKeySetIterator implements Iterator<String> {
2487
2488 public FacetsMapKeySetIterator(FacetsMap map) {
2489 this.map = map;
2490 this.iterator = map.keySetIterator();
2491 }
2492
2493 private FacetsMap map = null;
2494 private Iterator<String> iterator = null;
2495 private String last = null;
2496
2497 public boolean hasNext() {
2498 return (iterator.hasNext());
2499 }
2500
2501 public String next() {
2502 last = iterator.next();
2503 return (last);
2504 }
2505
2506 public void remove() {
2507 if (last == null) {
2508 throw new IllegalStateException();
2509 }
2510 map.remove(last);
2511 last = null;
2512 }
2513
2514 }
2515
2516
2517 // Private implementation of Collection for FacetsMap.values()
2518 private static class FacetsMapValues extends AbstractCollection<UIComponent> {
2519
2520 public FacetsMapValues(FacetsMap map) {
2521 this.map = map;
2522 }
2523
2524 private FacetsMap map;
2525
2526 public boolean add(UIComponent o) {
2527 throw new UnsupportedOperationException();
2528 }
2529
2530 public boolean addAll(Collection c) {
2531 throw new UnsupportedOperationException();
2532 }
2533
2534 public void clear() {
2535 map.clear();
2536 }
2537
2538 public boolean isEmpty() {
2539 return (map.isEmpty());
2540 }
2541
2542 public Iterator<UIComponent> iterator() {
2543 return (new FacetsMapValuesIterator(map));
2544 }
2545
2546 public int size() {
2547 return (map.size());
2548 }
2549
2550
2551 }
2552
2553
2554 // Private implementation of Iterator for FacetsMap.values().iterator()
2555 private static class FacetsMapValuesIterator implements Iterator<UIComponent> {
2556
2557 public FacetsMapValuesIterator(FacetsMap map) {
2558 this.map = map;
2559 this.iterator = map.keySetIterator();
2560 }
2561
2562 private FacetsMap map = null;
2563 private Iterator<String> iterator = null;
2564 private Object last = null;
2565
2566 public boolean hasNext() {
2567 return (iterator.hasNext());
2568 }
2569
2570 public UIComponent next() {
2571 last = iterator.next();
2572 return (map.get(last));
2573 }
2574
2575 public void remove() {
2576 if (last == null) {
2577 throw new IllegalStateException();
2578 }
2579 map.remove(last);
2580 last = null;
2581 }
2582
2583 }
2584
2585
2586
2587
2588 }