1 /*
2 * $Id: UIComponent.java,v 1.150.4.4 2008/04/17 21:17:00 rlubke Exp $
3 */
4
5 /*
6 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
7 *
8 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
9 *
10 * The contents of this file are subject to the terms of either the GNU
11 * General Public License Version 2 only ("GPL") or the Common Development
12 * and Distribution License("CDDL") (collectively, the "License"). You
13 * may not use this file except in compliance with the License. You can obtain
14 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
15 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
16 * language governing permissions and limitations under the License.
17 *
18 * When distributing the software, include this License Header Notice in each
19 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
20 * Sun designates this particular file as subject to the "Classpath" exception
21 * as provided by Sun in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the License
23 * Header, with the fields enclosed by brackets [] replaced by your own
24 * identifying information: "Portions Copyrighted [year]
25 * [name of copyright owner]"
26 *
27 * Contributor(s):
28 *
29 * If you wish your version of this file to be governed by only the CDDL or
30 * only the GPL Version 2, indicate your decision by adding "[Contributor]
31 * elects to include this software in this distribution under the [CDDL or GPL
32 * Version 2] license." If you don't indicate a single choice of license, a
33 * recipient has the option to distribute your version of this file under
34 * either the CDDL, the GPL Version 2 or to extend the choice of license to
35 * its licensees as provided above. However, if you add GPL Version 2 code
36 * and therefore, elected the GPL Version 2 license, then the option applies
37 * only if the new code is made subject to such option by the copyright
38 * holder.
39 */
40
41 package javax.faces.component;
42
43
44 import java.io.IOException;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.HashMap;
48 import java.util.Iterator;
49 import java.util.List;
50 import java.util.Map;
51
52 import javax.el.ELContext;
53 import javax.el.ELException;
54 import javax.el.ValueExpression;
55 import javax.faces.FacesException;
56 import javax.faces.context.FacesContext;
57 import javax.faces.el.ValueBinding;
58 import javax.faces.event.AbortProcessingException;
59 import javax.faces.event.FacesEvent;
60 import javax.faces.event.FacesListener;
61 import javax.faces.render.Renderer;
62
63 /**
64 * <p><strong>UIComponent</strong> is the base class for all user interface
65 * components in JavaServer Faces. The set of {@link UIComponent} instances
66 * associated with a particular request and response are organized into a
67 * component tree under a {@link UIViewRoot} that represents
68 * the entire content of the request or response.</p>
69 *
70 * <p>For the convenience of component developers,
71 * {@link UIComponentBase} provides the default
72 * behavior that is specified for a {@link UIComponent}, and is the base class
73 * for all of the concrete {@link UIComponent} "base" implementations.
74 * Component writers are encouraged to subclass
75 * {@link UIComponentBase}, instead of directly
76 * implementing this abstract class, to reduce the impact of any future changes
77 * to the method signatures.</p>
78 */
79
80 public abstract class UIComponent implements StateHolder {
81
82 /**
83 * This array represents the packages that can leverage the
84 * <code>attributesThatAreSet</code> List for optimized attribute
85 * rendering.
86 *
87 * Hopefully JSF 2.0 will remove the need for this.
88 */
89 private static final String[] OPTIMIZED_PACKAGES = {
90 "javax.faces.component",
91 "javax.faces.component.html"
92 };
93
94 static {
95 // Sort the array for use with Arrays.binarySearch()
96 Arrays.sort(OPTIMIZED_PACKAGES);
97 }
98
99 /**
100 * List of attributes that have been set on the component (this
101 * may be from setValueExpression, the attributes map, or setters
102 * from the concrete HTML components. This allows
103 * for faster rendering of attributes as this list is authoratative
104 * on what has been set.
105 */
106 List<String> attributesThatAreSet;
107
108
109 // -------------------------------------------------------------- Attributes
110
111
112 /**
113 * <p>Return a mutable <code>Map</code> representing the attributes
114 * (and properties, see below) associated wth this {@link UIComponent},
115 * keyed by attribute name (which must be a String). The returned
116 * implementation must support all of the standard and optional
117 * <code>Map</code> methods, plus support the following additional
118 * requirements:</p>
119 * <ul>
120 * <li>The <code>Map</code> implementation must implement
121 * the <code>java.io.Serializable</code> interface.</li>
122 * <li>Any attempt to add a <code>null</code> key or value must
123 * throw a <code>NullPointerException</code>.</li>
124 * <li>Any attempt to add a key that is not a String must throw
125 * a <code>ClassCastException</code>.</li>
126 * <li>If the attribute name specified as a key matches a property
127 * of this {@link UIComponent}'s implementation class, the following
128 * methods will have special behavior:
129 * <ul>
130 * <li><code>containsKey</code> - Return <code>false</code>.</li>
131 * <li><code>get()</code> - If the property is readable, call
132 * the getter method and return the returned value (wrapping
133 * primitive values in their corresponding wrapper classes);
134 * otherwise throw <code>IllegalArgumentException</code>.</li>
135 * <li><code>put()</code> - If the property is writeable, call
136 * the setter method to set the corresponding value (unwrapping
137 * primitive values in their corresponding wrapper classes).
138 * If the property is not writeable, or an attempt is made to
139 * set a property of primitive type to <code>null</code>,
140 * throw <code>IllegalArgumentException</code>.</li>
141 * <li><code>remove</code> - Throw
142 * <code>IllegalArgumentException</code>.</li>
143 * </ul></li>
144 * </ul>
145 */
146 public abstract Map<String, Object> getAttributes();
147
148
149 // ---------------------------------------------------------------- Bindings
150
151
152 /**
153 *
154 * <p>Call through to {@link #getValueExpression} and examine the
155 * result. If the result is an instance of the wrapper class
156 * mandated in {@link #setValueBinding}, extract the
157 * <code>ValueBinding</code> instance and return it. Otherwise,
158 * wrap the result in an implementation of
159 * <code>ValueBinding</code>, and return it.</p>
160 *
161 * @param name Name of the attribute or property for which to retrieve a
162 * {@link ValueBinding}
163 *
164 * @throws NullPointerException if <code>name</code>
165 * is <code>null</code>
166 *
167 * @deprecated This has been replaced by {@link #getValueExpression}.
168 */
169 public abstract ValueBinding getValueBinding(String name);
170
171
172 /**
173 * <p>Wrap the argument <code>binding</code> in an implementation of
174 * {@link ValueExpression} and call through to {@link
175 * #setValueExpression}.</p>
176 *
177 * @param name Name of the attribute or property for which to set a
178 * {@link ValueBinding}
179 * @param binding The {@link ValueBinding} to set, or <code>null</code>
180 * to remove any currently set {@link ValueBinding}
181 *
182 * @throws IllegalArgumentException if <code>name</code> is one of
183 * <code>id</code> or <code>parent</code>
184 * @throws NullPointerException if <code>name</code>
185 * is <code>null</code>
186 *
187 * @deprecated This has been replaced by {@link #setValueExpression}.
188 */
189 public abstract void setValueBinding(String name, ValueBinding binding);
190
191 // The set of ValueExpressions for this component, keyed by property
192 // name This collection is lazily instantiated
193 // The set of ValueExpressions for this component, keyed by property
194 // name This collection is lazily instantiated
195 protected Map<String,ValueExpression> bindings = null;
196
197 /**
198 * <p>Return the {@link ValueExpression} used to calculate the value for the
199 * specified attribute or property name, if any.</p>
200 *
201 * <p>This method must be overridden and implemented for components that
202 * comply with JSF 1.2 and later.</p>
203 *
204 * @since 1.2
205 *
206 * @param name Name of the attribute or property for which to retrieve a
207 * {@link ValueExpression}
208 *
209 * @throws NullPointerException if <code>name</code>
210 * is <code>null</code>
211 *
212 */
213 public ValueExpression getValueExpression(String name) {
214 ValueExpression result = null;
215
216 if (name == null) {
217 throw new NullPointerException();
218 }
219 if (bindings == null) {
220 if (!isUIComponentBase()) {
221 ValueBinding binding = getValueBinding(name);
222 if (null != binding) {
223 result = new ValueExpressionValueBindingAdapter(binding);
224 // Cache this for future reference.
225 //noinspection CollectionWithoutInitialCapacity
226 bindings = new HashMap<String, ValueExpression>();
227 bindings.put(name, result);
228 }
229 }
230 return (result);
231 } else {
232 return (bindings.get(name));
233 }
234
235 }
236
237 /**
238 * <p>Set the {@link ValueExpression} used to calculate the value
239 * for the specified attribute or property name, if any.</p>
240 *
241 * <p>The implementation must call {@link
242 * ValueExpression#isLiteralText} on the argument
243 * <code>expression</code>. If <code>isLiteralText()</code> returns
244 * <code>true</code>, invoke {@link ValueExpression#getValue} on the
245 * argument expression and pass the result as the <code>value</code>
246 * parameter in a call to <code>this.{@link
247 * #getAttributes()}.put(name, value)</code> where <code>name</code>
248 * is the argument <code>name</code>. If an exception is thrown as
249 * a result of calling {@link ValueExpression#getValue}, wrap it in
250 * a {@link javax.faces.FacesException} and re-throw it. If
251 * <code>isLiteralText()</code> returns <code>false</code>, simply
252 * store the un-evaluated <code>expression</code> argument in the
253 * collection of <code>ValueExpression</code>s under the key given
254 * by the argument <code>name</code>.</p>
255 *
256 * <p>This method must be overridden and implemented for components that
257 * comply with JSF 1.2 and later.</p>
258 *
259 * @since 1.2
260 *
261 * @param name Name of the attribute or property for which to set a
262 * {@link ValueExpression}
263 * @param binding The {@link ValueExpression} to set, or <code>null</code>
264 * to remove any currently set {@link ValueExpression}
265 *
266 * @throws IllegalArgumentException if <code>name</code> is one of
267 * <code>id</code> or <code>parent</code>
268 * @throws NullPointerException if <code>name</code>
269 * is <code>null</code>
270 *
271 */
272 public void setValueExpression(String name, ValueExpression binding) {
273
274 if (name == null) {
275 throw new NullPointerException();
276 } else if ("id".equals(name) || "parent".equals(name)) {
277 throw new IllegalArgumentException();
278 }
279 if (binding != null) {
280 if (!binding.isLiteralText()) {
281 if (bindings == null) {
282 //noinspection CollectionWithoutInitialCapacity
283 bindings = new HashMap<String, ValueExpression>();
284 }
285 // add this binding name to the 'attributesThatAreSet' list
286 List<String> sProperties = getAttributesThatAreSet(true);
287 if (sProperties != null && !sProperties.contains(name)) {
288 sProperties.add(name);
289 }
290
291 bindings.put(name, binding);
292 } else {
293 ELContext context =
294 FacesContext.getCurrentInstance().getELContext();
295 try {
296 getAttributes().put(name, binding.getValue(context));
297 } catch (ELException ele) {
298 throw new FacesException(ele);
299 }
300 }
301 } else {
302 if (bindings != null) {
303 // remove this binding name from the 'attributesThatAreSet' list
304 List<String> sProperties = getAttributesThatAreSet(false);
305 if (sProperties != null) {
306 sProperties.remove(name);
307 }
308 bindings.remove(name);
309 if (bindings.isEmpty()) {
310 bindings = null;
311 }
312 }
313 }
314
315 }
316
317 // -------------------------------------------------------------- Properties
318
319
320 /**
321 * <p>Return a client-side identifier for this component, generating
322 * one if necessary. The associated {@link Renderer}, if any,
323 * will be asked to convert the clientId to a form suitable for
324 * transmission to the client.</p>
325 *
326 * <p>The return from this method must be the same value throughout
327 * the lifetime of the instance, unless the <code>id</code> property
328 * of the component is changed, or the component is placed in
329 * a {@link NamingContainer} whose client ID changes (for example,
330 * {@link UIData}). However, even in these cases, consecutive
331 * calls to this method must always return the same value. The
332 * implementation must follow these steps in determining the
333 * clientId:</p>
334 *
335 * <p>Find the closest ancestor to <b>this</b> component in the view
336 * hierarchy that implements <code>NamingContainer</code>. Call
337 * <code>getContainerClientId()</code> on it and save the result as
338 * the <code>parentId</code> local variable. Call {@link #getId} on
339 * <b>this</b> component and save the result as the
340 * <code>myId</code> local variable. If <code>myId</code> is
341 * <code>null</code>, call
342 * <code>context.getViewRoot().createUniqueId()</code> and assign
343 * the result to myId. If <code>parentId</code> is
344 * non-<code>null</code>, let <code>myId</code> equal <code>parentId
345 * + NamingContainer.SEPARATOR_CHAR + myId</code>. Call {@link
346 * Renderer#convertClientId}, passing <code>myId</code>, and return
347 * the result.</p>
348 *
349 * @param context The {@link FacesContext} for the current request
350 *
351 * @throws NullPointerException if <code>context</code>
352 * is <code>null</code>
353 */
354 public abstract String getClientId(FacesContext context);
355
356 /**
357 * <p>Allow components that implement {@link NamingContainer} to
358 * selectively disable prepending their clientId to their
359 * descendent's clientIds by breaking the prepending logic into a
360 * seperately callable method. See {@link #getClientId} for usage.</p>
361 *
362 * <p>By default, this method will call through to {@link
363 * #getClientId} and return the result.
364 *
365 * @since 1.2
366 *
367 * @throws NullPointerException if <code>context</code> is
368 * <code>null</code>
369 */
370 public String getContainerClientId(FacesContext context) {
371 if (context == null) {
372 throw new NullPointerException();
373 }
374 return this.getClientId(context);
375 }
376
377 /**
378 * <p>Return the identifier of the component family to which this
379 * component belongs. This identifier, in conjunction with the value
380 * of the <code>rendererType</code> property, may be used to select
381 * the appropriate {@link Renderer} for this component instance.</p>
382 */
383 public abstract String getFamily();
384
385
386 /**
387 * <p>Return the component identifier of this {@link UIComponent}.</p>
388 */
389 public abstract String getId();
390
391
392 /**
393 * <p>Set the component identifier of this {@link UIComponent} (if any).
394 * Component identifiers must obey the following syntax restrictions:</p>
395 * <ul>
396 * <li>Must not be a zero-length String.</li>
397 * <li>First character must be a letter or an underscore ('_').</li>
398 * <li>Subsequent characters must be a letter, a digit,
399 * an underscore ('_'), or a dash ('-').</li>
400 * <li>
401 * </ul>
402 *
403 * <p>Component identifiers must also obey the following semantic
404 * restrictions (note that this restriction is <strong>NOT</strong>
405 * enforced by the <code>setId()</code> implementation):</p>
406 * <ul>
407 * <li>The specified identifier must be unique among all the components
408 * (including facets) that are descendents of the nearest ancestor
409 * {@link UIComponent} that is a {@link NamingContainer}, or within
410 * the scope of the entire component tree if there is no such
411 * ancestor that is a {@link NamingContainer}.</li>
412 * </ul>
413 *
414 * @param id The new component identifier, or <code>null</code> to indicate
415 * that this {@link UIComponent} does not have a component identifier
416 *
417 * @throws IllegalArgumentException if <code>id</code> is not
418 * syntactically valid
419 */
420 public abstract void setId(String id);
421
422
423 /**
424 * <p>Return the parent {@link UIComponent} of this
425 * <code>UIComponent</code>, if any. A component must allow child
426 * components to be added to and removed from the list of children
427 * of this component, even though the child component returns null
428 * from <code>getParent( )</code>.</p>
429 */
430 public abstract UIComponent getParent();
431
432
433 /**
434 * <p>Set the parent <code>UIComponent</code> of this
435 * <code>UIComponent</code>. <strong>This method must
436 * never be called by developers; a {@link UIComponent}'s internal
437 * implementation will call it as components are added to or
438 * removed from a parent's child <code>List</code> or
439 * facet <code>Map</code></strong>.</p>
440 *
441 * @param parent The new parent, or <code>null</code> for the root node
442 * of a component tree
443 */
444 public abstract void setParent(UIComponent parent);
445
446
447 /**
448 * <p>Return <code>true</code> if this component (and its children)
449 * should be rendered during the <em>Render Response</em> phase
450 * of the request processing lifecycle.</p>
451 */
452 public abstract boolean isRendered();
453
454
455 /**
456 * <p>Set the <code>rendered</code> property of this
457 * {@link UIComponent}.</p>
458 *
459 * @param rendered If <code>true</code> render this component;
460 * otherwise, do not render this component
461 */
462 public abstract void setRendered(boolean rendered);
463
464
465 /**
466 * <p>Return the {@link Renderer} type for this {@link UIComponent}
467 * (if any).</p>
468 */
469 public abstract String getRendererType();
470
471
472 /**
473 * <p>Set the {@link Renderer} type for this {@link UIComponent},
474 * or <code>null</code> for components that render themselves.</p>
475 *
476 * @param rendererType Logical identifier of the type of
477 * {@link Renderer} to use, or <code>null</code> for components
478 * that render themselves
479 */
480 public abstract void setRendererType(String rendererType);
481
482
483 /**
484 * <p>Return a flag indicating whether this component is responsible
485 * for rendering its child components. The default implementation
486 * in {@link UIComponentBase#getRendersChildren} tries to find the
487 * renderer for this component. If it does, it calls {@link
488 * Renderer#getRendersChildren} and returns the result. If it
489 * doesn't, it returns false. As of version 1.2 of the JavaServer
490 * Faces Specification, component authors are encouraged to return
491 * <code>true</code> from this method and rely on {@link
492 * UIComponentBase#encodeChildren}.</p>
493 */
494 public abstract boolean getRendersChildren();
495
496
497 // This is necessary for JSF components that extend from UIComponent
498 // directly rather than extending from UIComponentBase. Such components
499 // may need to have implementations provided for methods that originated
500 // from a spec version more recent than the version with which the component
501 // complies. Currently this private property is only consulted in the
502 // getValueExpression() method.
503 private boolean isUIComponentBase;
504 private boolean isUIComponentBaseIsSet = false;
505
506 private boolean isUIComponentBase() {
507 if (!isUIComponentBaseIsSet) {
508 isUIComponentBase = (this instanceof UIComponentBase);
509 }
510
511 return isUIComponentBase;
512 }
513
514
515 // ------------------------------------------------- Tree Management Methods
516
517
518 /**
519 * <p>Return a mutable <code>List</code> representing the child
520 * {@link UIComponent}s associated with this component. The returned
521 * implementation must support all of the standard and optional
522 * <code>List</code> methods, plus support the following additional
523 * requirements:</p>
524 * <ul>
525 * <li>The <code>List</code> implementation must implement
526 * the <code>java.io.Serializable</code> interface.</li>
527 * <li>Any attempt to add a <code>null</code> must throw
528 * a NullPointerException</li>
529 * <li>Any attempt to add an object that does not implement
530 * {@link UIComponent} must throw a ClassCastException.</li>
531 * <li>Whenever a new child component is added, the <code>parent</code>
532 * property of the child must be set to this component instance.
533 * If the <code>parent</code> property of the child was already
534 * non-null, the child must first be removed from its previous
535 * parent (where it may have been either a child or a facet).</li>
536 * <li>Whenever an existing child component is removed, the
537 * <code>parent</code> property of the child must be set to
538 * <code>null</code>.</li>
539 * </ul>
540 */
541 public abstract List<UIComponent> getChildren();
542
543
544 /**
545 * <p>Return the number of child {@link UIComponent}s that are
546 * associated with this {@link UIComponent}. If there are no
547 * children, this method must return 0. The method must not cause
548 * the creation of a child component list.</p>
549 */
550 public abstract int getChildCount();
551
552
553 /**
554 * <p>Search for and return the {@link UIComponent} with an <code>id</code>
555 * that matches the specified search expression (if any), according to the
556 * algorithm described below.</p>
557 *
558 * <p>For a method to find a component given a simple
559 * <code>clientId</code>, see {@link #invokeOnComponent}.</p>
560 *
561 * <p>Component identifiers are required to be unique within the scope of
562 * the closest ancestor {@link NamingContainer} that encloses this
563 * component (which might be this component itself). If there are no
564 * {@link NamingContainer} components in the ancestry of this component,
565 * the root component in the tree is treated as if it were a
566 * {@link NamingContainer}, whether or not its class actually implements
567 * the {@link NamingContainer} interface.</p>
568 *
569 * <p>A <em>search expression</em> consists of either an
570 * identifier (which is matched exactly against the <code>id</code>
571 * property of a {@link UIComponent}, or a series of such identifiers
572 * linked by the {@link NamingContainer#SEPARATOR_CHAR} character value.
573 * The search algorithm should operates as follows, though alternate
574 * alogrithms may be used as long as the end result is the same:</p>
575 * <ul>
576 * <li>Identify the {@link UIComponent} that will be the base for searching,
577 * by stopping as soon as one of the following conditions is met:
578 * <ul>
579 * <li>If the search expression begins with the the separator character
580 * (called an "absolute" search expression),
581 * the base will be the root {@link UIComponent} of the component
582 * tree. The leading separator character will be stripped off,
583 * and the remainder of the search expression will be treated as
584 * a "relative" search expression as described below.</li>
585 * <li>Otherwise, if this {@link UIComponent} is a
586 * {@link NamingContainer} it will serve as the basis.</li>
587 * <li>Otherwise, search up the parents of this component. If
588 * a {@link NamingContainer} is encountered, it will be the base.
589 * </li>
590 * <li>Otherwise (if no {@link NamingContainer} is encountered)
591 * the root {@link UIComponent} will be the base.</li>
592 * </ul></li>
593 * <li>The search expression (possibly modified in the previous step) is now
594 * a "relative" search expression that will be used to locate the
595 * component (if any) that has an <code>id</code> that matches, within
596 * the scope of the base component. The match is performed as follows:
597 * <ul>
598 * <li>If the search expression is a simple identifier, this value is
599 * compared to the <code>id</code> property, and then recursively
600 * through the facets and children of the base {@link UIComponent}
601 * (except that if a descendant {@link NamingContainer} is found,
602 * its own facets and children are not searched).</li>
603 * <li>If the search expression includes more than one identifier
604 * separated by the separator character, the first identifier is
605 * used to locate a {@link NamingContainer} by the rules in the
606 * previous bullet point. Then, the <code>findComponent()</code>
607 * method of this {@link NamingContainer} will be called, passing
608 * the remainder of the search expression.</li>
609 * </ul></li>
610 * </ul>
611 *
612 * @param expr Search expression identifying the {@link UIComponent}
613 * to be returned
614 *
615 * @return the found {@link UIComponent}, or <code>null</code>
616 * if the component was not found.
617 *
618 * @throws IllegalArgumentException if an intermediate identifier
619 * in a search expression identifies a {@link UIComponent} that is
620 * not a {@link NamingContainer}
621 * @throws NullPointerException if <code>expr</code>
622 * is <code>null</code>
623 */
624 public abstract UIComponent findComponent(String expr);
625
626 /**
627 *
628 * <p>Starting at this component in the View hierarchy, search for a
629 * component with a <code>clientId</code> equal to the argument
630 * <code>clientId</code> and, if found, call the {@link
631 * ContextCallback#invokeContextCallback} method on the argument
632 * <code>callback</code>, passing the current {@link FacesContext}
633 * and the found component as arguments. This method is similar to
634 * {@link #findComponent} but it does not support the leading
635 * {@link NamingContainer#SEPARATOR_CHAR} syntax for searching from the
636 * root of the View.</p>
637 *
638 * <p>The default implementation will first check if
639 * <code>this.getClientId()</code> is equal to the argument
640 * <code>clientId</code>. If so, call the {@link
641 * ContextCallback#invokeContextCallback} method on the argument callback,
642 * passing through the <code>FacesContext</code> argument and
643 * passing this as the component argument. If an
644 * <code>Exception</code> is thrown by the callback, wrap it in a
645 * {@link FacesException} and re-throw it. Otherwise, return
646 * <code>true</code>.</p>
647 *
648 * <p>Otherwise, for each component returned by {@link
649 * #getFacetsAndChildren}, call <code>invokeOnComponent()</code>
650 * passing the arguments to this method, in order. The first time
651 * <code>invokeOnComponent()</code> returns true, abort traversing
652 * the rest of the <code>Iterator</code> and return
653 * <code>true</code>.</p>
654 *
655 * <p>When calling {@link ContextCallback#invokeContextCallback}
656 * the implementation of this method must guarantee that the state
657 * of the component passed to the callback correctly reflects the
658 * component's position in the View hierarchy with respect to any
659 * state found in the argument <code>clientId</code>. For example,
660 * an iterating component such as {@link UIData} will need to set
661 * its row index to correctly reflect the argument
662 * <code>clientId</code> before finding the appropriate child
663 * component backed by the correct row. When the callback returns,
664 * either normally or by throwing an <code>Exception</code> the
665 * implementation of this method must restore the state of the view
666 * to the way it was before invoking the callback.</p>
667 *
668 * <p>If none of the elements from {@link
669 * #getFacetsAndChildren} returned <code>true</code> from
670 * <code>invokeOnComponent()</code>, return <code>false</code>.</p>
671 *
672 * <p>Simple usage example to find a component by
673 * <code>clientId</code>.</p>
674
675 * <pre><code>
676 private UIComponent found = null;
677
678 private void doFind(FacesContext context, String clientId) {
679 context.getViewRoot().invokeOnComponent(context, clientId,
680 new ContextCallback() {
681 public void invokeContextCallback(FacesContext context,
682 UIComponent component) {
683 found = component;
684 }
685 });
686 }
687 * </code></pre>
688
689 *
690 *
691 * @since 1.2
692 *
693 * @param context the {@link FacesContext} for the current request
694 *
695 * @param clientId the client identifier of the component to be passed
696 * to the argument callback.
697 *
698 * @param callback an implementation of the Callback interface.
699 *
700 * @throws NullPointerException if any of the arguments are null
701 *
702 * @throws FacesException if the argument Callback throws an
703 * Exception, it is wrapped in a <code>FacesException</code> and re-thrown.
704 *
705 * @return <code>true</code> if the a component with the given
706 * <code>clientId</code> is found, the callback method was
707 * successfully invoked passing that component as an argument, and
708 * no Exception was thrown. Returns <code>false</code> if no
709 * component with the given <code>clientId</code> is found.
710 *
711 */
712
713 public boolean invokeOnComponent(FacesContext context, String clientId,
714 ContextCallback callback) throws FacesException {
715 if (null == context || null == clientId || null == callback) {
716 throw new NullPointerException();
717 }
718
719 boolean found = false;
720 if (clientId.equals(this.getClientId(context))) {
721 try {
722 callback.invokeContextCallback(context, this);
723 return true;
724 } catch (Exception e) {
725 throw new FacesException(e);
726 }
727 } else {
728 Iterator<UIComponent> itr = this.getFacetsAndChildren();
729
730 while (itr.hasNext() && !found) {
731 found = itr.next().invokeOnComponent(context, clientId,
732 callback);
733 }
734 }
735 return found;
736 }
737
738
739 // ------------------------------------------------ Facet Management Methods
740
741
742 /**
743 * <p>Return a mutable <code>Map</code> representing the facet
744 * {@link UIComponent}s associated with this {@link UIComponent},
745 * keyed by facet name (which must be a String). The returned
746 * implementation must support all of the standard and optional
747 * <code>Map</code> methods, plus support the following additional
748 * requirements:</p>
749 * <ul>
750 * <li>The <code>Map</code> implementation must implement
751 * the <code>java.io.Serializable</code> interface.</li>
752 * <li>Any attempt to add a <code>null</code> key or value must
753 * throw a NullPointerException.</li>
754 * <li>Any attempt to add a key that is not a String must throw
755 * a ClassCastException.</li>
756 * <li>Any attempt to add a value that is not a {@link UIComponent}
757 * must throw a ClassCastException.</li>
758 * <li>Whenever a new facet {@link UIComponent} is added:
759 * <ul>
760 * <li>The <code>parent</code> property of the component must be set to
761 * this component instance.</li>
762 * <li>If the <code>parent</code> property of the component was already
763 * non-null, the component must first be removed from its previous
764 * parent (where it may have been either a child or a facet).</li>
765 * </ul></li>
766 * <li>Whenever an existing facet {@link UIComponent} is removed:
767 * <ul>
768 * <li>The <code>parent</code> property of the facet must be
769 * set to <code>null</code>.</li>
770 * </ul></li>
771 * </ul>
772 */
773 public abstract Map<String, UIComponent> getFacets();
774
775 /**
776 * <p>Return the number of facet {@link UIComponent}s that are
777 * associated with this {@link UIComponent}. If there are no
778 * facets, this method must return 0. The method must not cause
779 * the creation of a facet component map.</p>
780 *
781 * <p>For backwards compatability with classes that extend UIComponent
782 * directly, a default implementation is provided that simply calls
783 * {@link #getFacets} and then calls the <code>size()</code> method on the
784 * returned <code>Map</code>. A more optimized version of this method is
785 * provided in {@link UIComponentBase#getFacetCount}.
786 *
787 * @since 1.2
788 */
789 public int getFacetCount() {
790 return (getFacets().size());
791 }
792
793
794
795 /**
796 * <p>Convenience method to return the named facet, if it exists, or
797 * <code>null</code> otherwise. If the requested facet does not
798 * exist, the facets Map must not be created.</p>
799 *
800 * @param name Name of the desired facet
801 */
802 public abstract UIComponent getFacet(String name);
803
804
805 /**
806 * <p>Return an <code>Iterator</code> over the facet followed by child
807 * {@link UIComponent}s of this {@link UIComponent}.
808 * Facets are returned in an undefined order, followed by
809 * all the children in the order they are stored in the child list. If this
810 * component has no facets or children, an empty <code>Iterator</code>
811 * is returned.</p>
812 *
813 * <p>The returned <code>Iterator</code> must not support the
814 * <code>remove()</code> operation.</p>
815 */
816 public abstract Iterator<UIComponent> getFacetsAndChildren();
817
818
819 // -------------------------------------------- Lifecycle Processing Methods
820
821
822 /**
823 * <p>Broadcast the specified {@link FacesEvent} to all registered
824 * event listeners who have expressed an interest in events of this
825 * type. Listeners are called in the order in which they were
826 * added.</p>
827 *
828 * @param event The {@link FacesEvent} to be broadcast
829 *
830 * @throws AbortProcessingException Signal the JavaServer Faces
831 * implementation that no further processing on the current event
832 * should be performed
833 * @throws IllegalArgumentException if the implementation class
834 * of this {@link FacesEvent} is not supported by this component
835 * @throws NullPointerException if <code>event</code> is
836 * <code>null</code>
837 */
838 public abstract void broadcast(FacesEvent event)
839 throws AbortProcessingException;
840
841
842 /**
843 * <p>Decode any new state of this {@link UIComponent} from the
844 * request contained in the specified {@link FacesContext}, and store
845 * this state as needed.</p>
846 *
847 * <p>During decoding, events may be enqueued for later processing
848 * (by event listeners who have registered an interest), by calling
849 * <code>queueEvent()</code>.</p>
850 *
851 * @param context {@link FacesContext} for the request we are processing
852 *
853 * @throws NullPointerException if <code>context</code>
854 * is <code>null</code>
855 */
856 public abstract void decode(FacesContext context);
857
858
859 /**
860 * <p>If our <code>rendered</code> property is <code>true</code>,
861 * render the beginning of the current state of this
862 * {@link UIComponent} to the response contained in the specified
863 * {@link FacesContext}.
864 * </p>
865 *
866 * <p>If a {@link Renderer} is associated with this {@link UIComponent},
867 * the actual encoding will be delegated to
868 * {@link Renderer#encodeBegin(FacesContext, UIComponent)}.</p>
869 *
870 * @param context {@link FacesContext} for the response we are creating
871 *
872 * @throws IOException if an input/output error occurs while rendering
873 * @throws NullPointerException if <code>context</code>
874 * is <code>null</code>
875 */
876 public abstract void encodeBegin(FacesContext context) throws IOException;
877
878
879 /**
880 * <p>If our <code>rendered</code> property is <code>true</code>,
881 * render the child {@link UIComponent}s of this {@link UIComponent}.
882 * This method will only be called
883 * if the <code>rendersChildren</code> property is <code>true</code>.</p>
884 *
885 * <p>If a {@link Renderer} is associated with this {@link UIComponent},
886 * the actual encoding will be delegated to
887 * {@link Renderer#encodeChildren(FacesContext, UIComponent)}.</p>
888 *
889 * @param context {@link FacesContext} for the response we are creating
890 *
891 * @throws IOException if an input/output error occurs while rendering
892 * @throws NullPointerException if <code>context</code>
893 * is <code>null</code>
894 */
895 public abstract void encodeChildren(FacesContext context) throws IOException;
896
897
898 /**
899 * <p>If our <code>rendered</code> property is <code>true</code>,
900 * render the ending of the current state of this
901 * {@link UIComponent}.</p>
902 *
903 * <p>If a {@link Renderer} is associated with this {@link UIComponent},
904 * the actual encoding will be delegated to
905 * {@link Renderer#encodeEnd(FacesContext, UIComponent)}.</p>
906 *
907 * @param context {@link FacesContext} for the response we are creating
908 *
909 * @throws IOException if an input/output error occurs while rendering
910 * @throws NullPointerException if <code>context</code>
911 * is <code>null</code>
912 */
913 public abstract void encodeEnd(FacesContext context) throws IOException;
914
915 /**
916 * <p>If this component returns <code>true</code> from {@link
917 * #isRendered}, render this component and all its children that
918 * return <code>true</code> from <code>isRendered()</code>,
919 * regardless of the value of the {@link #getRendersChildren} flag.
920 * </p>
921 *
922 * @since 1.2
923 *
924 * @throws IOException if an input/output error occurs while rendering
925 * @throws NullPointerException if <code>context</code>
926 * is <code>null</code>
927 */
928
929 public void encodeAll(FacesContext context) throws IOException {
930 if (!isRendered()) {
931 return;
932 }
933
934 encodeBegin(context);
935 if (getRendersChildren()) {
936 encodeChildren(context);
937 }
938 else if (this.getChildCount() > 0) {
939 Iterator kids = getChildren().iterator();
940 while (kids.hasNext()) {
941 UIComponent kid = (UIComponent) kids.next();
942 kid.encodeAll(context);
943 }
944 }
945
946 encodeEnd(context);
947 }
948
949
950
951 // -------------------------------------------------- Event Listener Methods
952
953
954 /**
955 * <p>Add the specified {@link FacesListener} to the set of listeners
956 * registered to receive event notifications from this {@link UIComponent}.
957 * It is expected that {@link UIComponent} classes acting as event sources
958 * will have corresponding typesafe APIs for registering listeners of the
959 * required type, and the implementation of those registration methods
960 * will delegate to this method. For example:</p>
961 * <pre>
962 * public class FooEvent extends FacesEvent { ... }
963 *
964 * public interface FooListener extends FacesListener {
965 * public void processFoo(FooEvent event);
966 * }
967 *
968 * public class FooComponent extends UIComponentBase {
969 * ...
970 * public void addFooListener(FooListener listener) {
971 * addFacesListener(listener);
972 * }
973 * public void removeFooListener(FooListener listener) {
974 * removeFacesListener(listener);
975 * }
976 * ...
977 * }
978 * </pre>
979 *
980 * @param listener The {@link FacesListener} to be registered
981 *
982 * @throws NullPointerException if <code>listener</code>
983 * is <code>null</code>
984 */
985 protected abstract void addFacesListener(FacesListener listener);
986
987
988 /**
989 * <p>Return an array of registered {@link FacesListener}s that are
990 * instances of the specified class. If there are no such registered
991 * listeners, a zero-length array is returned. The returned
992 * array can be safely be cast to an array strongly typed to
993 * an element type of <code>clazz</code>.</p>
994 *
995 * @param clazz Class that must be implemented by a {@link FacesListener}
996 * for it to be returned
997 *
998 * @throws IllegalArgumentException if <code>class</code> is not,
999 * and does not implement, {@link FacesListener}
1000 * @throws NullPointerException if <code>clazz</code>
1001 * is <code>null</code>
1002 */
1003 protected abstract FacesListener[] getFacesListeners(Class clazz);
1004
1005
1006 /**
1007 * <p>Remove the specified {@link FacesListener} from the set of listeners
1008 * registered to receive event notifications from this {@link UIComponent}.
1009 *
1010 * @param listener The {@link FacesListener} to be deregistered
1011 *
1012 * @throws NullPointerException if <code>listener</code>
1013 * is <code>null</code>
1014 */
1015 protected abstract void removeFacesListener(FacesListener listener);
1016
1017
1018 /**
1019 * <p>Queue an event for broadcast at the end of the current request
1020 * processing lifecycle phase. The default implementation in
1021 * {@link UIComponentBase} must delegate this call to the
1022 * <code>queueEvent()</code> method of the parent {@link UIComponent}.</p>
1023 *
1024 * @param event {@link FacesEvent} to be queued
1025 *
1026 * @throws IllegalStateException if this component is not a
1027 * descendant of a {@link UIViewRoot}
1028 * @throws NullPointerException if <code>event</code>
1029 * is <code>null</code>
1030 */
1031 public abstract void queueEvent(FacesEvent event);
1032
1033
1034 // ------------------------------------------------ Lifecycle Phase Handlers
1035
1036
1037 /**
1038 * <p>Perform the component tree processing required by the
1039 * <em>Restore View</em> phase of the request processing
1040 * lifecycle for all facets of this component, all children of this
1041 * component, and this component itself, as follows.</p>
1042 * <ul>
1043 * <li>Call the <code>processRestoreState()</code> method of all
1044 * facets and children of this {@link UIComponent} in the order
1045 * determined by a call to <code>getFacetsAndChildren()</code>.</li>
1046 * <li>Call the <code>restoreState()</code> method of this component.</li>
1047 * </ul>
1048 *
1049 * <p>This method may not be called if the state saving method is
1050 * set to server.</p>
1051 *
1052 * @param context {@link FacesContext} for the request we are processing
1053 *
1054 * @throws NullPointerException if <code>context</code>
1055 * is <code>null</code>
1056 */
1057 public abstract void processRestoreState(FacesContext context,
1058 Object state);
1059
1060
1061 /**
1062 * <p>Perform the component tree processing required by the
1063 * <em>Apply Request Values</em> phase of the request processing
1064 * lifecycle for all facets of this component, all children of this
1065 * component, and this component itself, as follows.</p>
1066 * <ul>
1067 * <li>If the <code>rendered</code> property of this {@link UIComponent}
1068 * is <code>false</code>, skip further processing.</li>
1069 * <li>Call the <code>processDecodes()</code> method of all facets
1070 * and children of this {@link UIComponent}, in the order determined
1071 * by a call to <code>getFacetsAndChildren()</code>.</li>
1072 * <li>Call the <code>decode()</code> method of this component.</li>
1073 * <li>If a <code>RuntimeException</code> is thrown during
1074 * decode processing, call {@link FacesContext#renderResponse}
1075 * and re-throw the exception.</li>
1076 * </ul>
1077 *
1078 * @param context {@link FacesContext} for the request we are processing
1079 *
1080 * @throws NullPointerException if <code>context</code>
1081 * is <code>null</code>
1082 */
1083 public abstract void processDecodes(FacesContext context);
1084
1085
1086 /**
1087 * <p>Perform the component tree processing required by the
1088 * <em>Process Validations</em> phase of the request processing
1089 * lifecycle for all facets of this component, all children of this
1090 * component, and this component itself, as follows.</p>
1091 * <ul>
1092 * <li>If the <code>rendered</code> property of this {@link UIComponent}
1093 * is <code>false</code>, skip further processing.</li>
1094 * <li>Call the <code>processValidators()</code> method of all facets
1095 * and children of this {@link UIComponent}, in the order determined
1096 * by a call to <code>getFacetsAndChildren()</code>.</li>
1097 * </ul>
1098 *
1099 * @param context {@link FacesContext} for the request we are processing
1100 *
1101 * @throws NullPointerException if <code>context</code>
1102 * is <code>null</code>
1103 */
1104 public abstract void processValidators(FacesContext context);
1105
1106
1107 /**
1108 * <p>Perform the component tree processing required by the
1109 * <em>Update Model Values</em> phase of the request processing
1110 * lifecycle for all facets of this component, all children of this
1111 * component, and this component itself, as follows.</p>
1112 * <ul>
1113 * <li>If the <code>rendered</code> property of this {@link UIComponent}
1114 * is <code>false</code>, skip further processing.</li>
1115 * <li>Call the <code>processUpdates()</code> method of all facets
1116 * and children of this {@link UIComponent}, in the order determined
1117 * by a call to <code>getFacetsAndChildren()</code>.</li>
1118 * </ul>
1119 *
1120 * @param context {@link FacesContext} for the request we are processing
1121 *
1122 * @throws NullPointerException if <code>context</code>
1123 * is <code>null</code>
1124 */
1125 public abstract void processUpdates(FacesContext context);
1126
1127
1128 /**
1129 * <p>Perform the component tree processing required by the state
1130 * saving portion of the <em>Render Response</em> phase of the
1131 * request processing lifecycle for all facets of this component,
1132 * all children of this component, and this component itself, as
1133 * follows.</p> <ul>
1134 *
1135 * <li>consult the <code>transient</code> property of this
1136 * component. If true, just return <code>null</code>.</li>
1137 *
1138 * <li>Call the <code>processSaveState()</code> method of all facets
1139 * and children of this {@link UIComponent} in the order determined
1140 * by a call to <code>getFacetsAndChildren()</code>, skipping
1141 * children and facets that are transient.</li>
1142 *
1143 * <li>Call the <code>saveState()</code> method of this component.</li>
1144 *
1145 * <li>Encapsulate the child state and your state into a
1146 * Serializable Object and return it.</li>
1147 *
1148 * </ul>
1149 *
1150 * <p>This method may not be called if the state saving method is
1151 * set to server.</p>
1152 *
1153 * @param context {@link FacesContext} for the request we are processing
1154 *
1155 * @throws NullPointerException if <code>context</code>
1156 * is <code>null</code>
1157 */
1158 public abstract Object processSaveState(FacesContext context);
1159
1160
1161 // ----------------------------------------------------- Convenience Methods
1162
1163
1164 /**
1165 * <p>Convenience method to return the {@link FacesContext} instance
1166 * for the current request.</p>
1167 */
1168 protected abstract FacesContext getFacesContext();
1169
1170
1171 /**
1172 * <p>Convenience method to return the {@link Renderer} instance
1173 * associated with this component, if any; otherwise, return
1174 * <code>null</code>.</p>
1175 *
1176 * @param context {@link FacesContext} for the current request
1177 */
1178 protected abstract Renderer getRenderer(FacesContext context);
1179
1180
1181 // --------------------------------------------------------- Package Private
1182
1183
1184 /**
1185 * @param create <code>true</code> if the list should be created
1186 * @return A List of Strings of all the attributes that have been set
1187 * against this component. If the component isn't in the default
1188 * javax.faces.component or javax.faces.component.html packages, or
1189 * create is <code>false</code>, this will return null;
1190 */
1191 List<String> getAttributesThatAreSet(boolean create) {
1192 // Class.getPackage() returns null for Groovy based-classes.
1193 Package pkg = this.getClass().getPackage();
1194 if (pkg != null) {
1195 String pkgName = this.getClass().getPackage().getName();
1196 if (create
1197 && Arrays.binarySearch(OPTIMIZED_PACKAGES, pkgName) >= 0) {
1198 if (attributesThatAreSet == null) {
1199 attributesThatAreSet = new ArrayList<String>(6);
1200 }
1201 }
1202 }
1203 return attributesThatAreSet;
1204
1205 }
1206
1207 }