1 /*
2 * $Id: UIViewRoot.java,v 1.49.4.5 2008/06/06 19:19:37 edburns 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 javax.el.ELException;
45 import javax.el.MethodExpression;
46 import javax.el.ValueExpression;
47 import javax.faces.FacesException;
48 import javax.faces.FactoryFinder;
49 import javax.faces.context.FacesContext;
50 import javax.faces.event.AbortProcessingException;
51 import javax.faces.event.FacesEvent;
52 import javax.faces.event.PhaseEvent;
53 import javax.faces.event.PhaseId;
54 import javax.faces.event.PhaseListener;
55 import javax.faces.lifecycle.Lifecycle;
56 import javax.faces.lifecycle.LifecycleFactory;
57 import javax.faces.render.RenderKit;
58 import javax.faces.webapp.FacesServlet;
59
60 import java.io.IOException;
61 import java.util.ArrayList;
62 import java.util.Iterator;
63 import java.util.List;
64 import java.util.Locale;
65 import java.util.ListIterator;
66 import java.util.logging.Level;
67 import java.util.logging.Logger;
68
69
70 /**
71 * <p><strong>UIViewRoot</strong> is the UIComponent that represents the
72 * root of the UIComponent tree. This component has no rendering, it
73 * just serves as the root of the component tree, and as a place to hang
74 * per-view {@link PhaseListener}s.</p>
75 * <p/>
76 * <p>For each of the following lifecycle phase methods:</p>
77 * <p/>
78 * <ul>
79 * <p/>
80 * <li><p>{@link #processDecodes} </p></li>
81 * <p/>
82 * <li><p>{@link #processValidators} </p></li>
83 * <p/>
84 * <li><p>{@link #processUpdates} </p></li>
85 * <p/>
86 * <li><p>{@link #processApplication} </p></li>
87 * <p/>
88 * <li><p>RenderResponse, via {@link #encodeBegin} and {@link
89 * #encodeEnd} </p></li>
90 * <p/>
91 * </ul>
92 * <p/>
93 * <p>Take the following action regarding
94 * <code>PhaseListener</code>s.</p>
95 * <p/>
96 * <ul>
97 * <p/>
98 * <p>Initialize a state flag to <code>false</code>.</p>
99 * <p/>
100 * <p>If {@link #getBeforePhaseListener} returns non-<code>null</code>,
101 * invoke the listener, passing in the correct corresponding {@link
102 * PhaseId} for this phase.</p>
103 * <p/>
104 * <p>Upon return from the listener, call {@link
105 * FacesContext#getResponseComplete} and {@link
106 * FacesContext#getRenderResponse}. If either return <code>true</code>
107 * set the internal state flag to <code>true</code>. </p>
108 * <p/>
109 * <p>If or one or more listeners have been added by a call to {@link
110 * #addPhaseListener}, invoke the <code>beforePhase</code> method on
111 * each one whose {@link PhaseListener#getPhaseId} matches the current
112 * phaseId, passing in the same <code>PhaseId</code> as in the previous
113 * step.</p>
114 * <p/>
115 * <p>Upon return from each listener, call {@link
116 * FacesContext#getResponseComplete} and {@link
117 * FacesContext#getRenderResponse}. If either return <code>true</code>
118 * set the internal state flag to <code>true</code>. </p>
119 * <p/>
120 * <p/>
121 * <p>Execute any processing for this phase if the internal state flag
122 * was not set.</p>
123 * <p/>
124 * <p>If {@link #getAfterPhaseListener} returns non-<code>null</code>,
125 * invoke the listener, passing in the correct corresponding {@link
126 * PhaseId} for this phase.</p>
127 * <p/>
128 * <p>If or one or more listeners have been added by a call to {@link
129 * #addPhaseListener}, invoke the <code>afterPhase</code> method on each
130 * one whose {@link PhaseListener#getPhaseId} matches the current
131 * phaseId, passing in the same <code>PhaseId</code> as in the previous
132 * step.</p>
133 * <p/>
134 * <p/>
135 * </ul>
136 */
137
138 public class UIViewRoot extends UIComponentBase {
139
140 // ------------------------------------------------------ Manifest Constants
141
142
143 /** <p>The standard component type for this component.</p> */
144 public static final String COMPONENT_TYPE = "javax.faces.ViewRoot";
145
146
147 /** <p>The standard component family for this component.</p> */
148 public static final String COMPONENT_FAMILY = "javax.faces.ViewRoot";
149
150
151 /**
152 * <p>The prefix that will be used for identifiers generated
153 * by the <code>createUniqueId()</code> method.
154 */
155 static public final String UNIQUE_ID_PREFIX = "j_id";
156
157 private static Lifecycle lifecycle;
158
159 private static final Logger LOGGER =
160 Logger.getLogger("javax.faces", "javax.faces.LogStrings");
161
162 // ------------------------------------------------------------ Constructors
163
164
165 /**
166 * <p>Create a new {@link UIViewRoot} instance with default property
167 * values.</p>
168 */
169 public UIViewRoot() {
170
171 super();
172 setRendererType(null);
173
174 }
175
176 // ------------------------------------------------------ Instance Variables
177
178 private int lastId = 0;
179
180 /**
181 * <p>Set and cleared during the lifetime of a lifecycle phase. Has
182 * no meaning between phases. If <code>true</code>, the lifecycle
183 * processing for the current phase must not take place.</p>
184 */
185 private boolean skipPhase;
186
187 /**
188 * <p>Set and cleared during the lifetime of a lifecycle phase. Has
189 * no meaning between phases. If <code>true</code>, the <code>MethodExpression</code>
190 * associated with <code>afterPhase</code> will not be invoked nor will any
191 * PhaseListeners associated with this UIViewRoot.
192 */
193 private boolean beforeMethodException;
194
195 /**
196 * <p>Set and cleared during the lifetime of a lifecycle phase. Has
197 * no meaning between phases.
198 */
199 private ListIterator<PhaseListener> phaseListenerIterator;
200
201
202 // -------------------------------------------------------------- Properties
203
204
205 public String getFamily() {
206
207 return (COMPONENT_FAMILY);
208
209 }
210
211
212 /**
213 * <p>The render kit identifier of the {@link RenderKit} associated
214 * wth this view.</p>
215 */
216 private String renderKitId = null;
217
218
219 /**
220 * <p>Return the render kit identifier of the {@link RenderKit}
221 * associated with this view. Unless explicitly set, as in {@link
222 * javax.faces.application.ViewHandler#createView}, the returned
223 * value will be <code>null.</code></p>
224 */
225 public String getRenderKitId() {
226
227 String result;
228 if (null != renderKitId) {
229 result = this.renderKitId;
230 } else {
231 ValueExpression vb = getValueExpression("renderKitId");
232 FacesContext context = getFacesContext();
233 if (vb != null) {
234 try {
235 result = (String) vb.getValue(context.getELContext());
236 }
237 catch (ELException e) {
238 if (LOGGER.isLoggable(Level.SEVERE)) {
239 LOGGER.log(Level.SEVERE,
240 "severe.component.unable_to_process_expression",
241 new Object[] { vb.getExpressionString(), "renderKitId"});
242 }
243 result = null;
244 }
245 } else {
246 result = null;
247 }
248 }
249 return result;
250 }
251
252
253 /**
254 * <p>Set the render kit identifier of the {@link RenderKit}
255 * associated with this view. This method may be called at any time
256 * between the end of <em>Apply Request Values</em> phase of the
257 * request processing lifecycle (i.e. when events are being broadcast)
258 * and the beginning of the <em>Render Response</em> phase.</p>
259 *
260 * @param renderKitId The new {@link RenderKit} identifier,
261 * or <code>null</code> to disassociate this view with any
262 * specific {@link RenderKit} instance
263 */
264 public void setRenderKitId(String renderKitId) {
265
266 this.renderKitId = renderKitId;
267
268 }
269
270
271 /** <p>The view identifier of this view.</p> */
272 private String viewId = null;
273
274
275 /** <p>Return the view identifier for this view.</p> */
276 public String getViewId() {
277
278 return (this.viewId);
279
280 }
281
282
283 /**
284 * <p>Set the view identifier for this view.</p>
285 *
286 * @param viewId The new view identifier
287 */
288 public void setViewId(String viewId) {
289
290 this.viewId = viewId;
291
292 }
293
294 // ------------------------------------------------ Event Management Methods
295
296 private MethodExpression beforePhase = null;
297 private MethodExpression afterPhase = null;
298
299 /**
300 * @return the {@link MethodExpression} that will be invoked before
301 * this view is rendered.
302 */
303
304 public MethodExpression getBeforePhaseListener() {
305 return beforePhase;
306 }
307
308 /**
309 * <p>Allow an arbitrary method to be called for the "beforePhase"
310 * event as the UIViewRoot runs through its lifecycle. This method
311 * will be called for all phases except {@link
312 * PhaseId#RESTORE_VIEW}. Unlike a true {@link PhaseListener},
313 * this approach doesn't allow for only receiving {@link
314 * PhaseEvent}s for a given phase.</p>
315 * <p/>
316 * <p>The method must conform to the signature of {@link
317 * PhaseListener#beforePhase}.</p>
318 *
319 * @param newBeforePhase the {@link MethodExpression} that will be
320 * invoked before this view is rendered.
321 */
322
323 public void setBeforePhaseListener(MethodExpression newBeforePhase) {
324 beforePhase = newBeforePhase;
325 }
326
327 /**
328 * @return the {@link MethodExpression} that will be invoked after
329 * this view is rendered.
330 */
331
332 public MethodExpression getAfterPhaseListener() {
333 return afterPhase;
334 }
335
336 /**
337 * <p>Allow an arbitrary method to be called for the "afterPhase"
338 * event as the UIViewRoot runs through its lifecycle. This method
339 * will be called for all phases except {@link
340 * PhaseId#RESTORE_VIEW}. Unlike a true {@link PhaseListener},
341 * this approach doesn't allow for only receiving {@link
342 * PhaseEvent}s for a given phase.</p>
343 * <p/>
344 * <p>The method must conform to the signature of {@link
345 * PhaseListener#afterPhase}.</p>
346 *
347 * @param newAfterPhase the {@link MethodExpression} that will be
348 * invoked after this view is rendered.
349 */
350
351 public void setAfterPhaseListener(MethodExpression newAfterPhase) {
352 afterPhase = newAfterPhase;
353 }
354
355 private List<PhaseListener> phaseListeners = null;
356
357 public void removePhaseListener(PhaseListener toRemove) {
358 if (null != phaseListeners) {
359 phaseListeners.remove(toRemove);
360 }
361 }
362
363 public void addPhaseListener(PhaseListener newPhaseListener) {
364 if (null == phaseListeners) {
365 //noinspection CollectionWithoutInitialCapacity
366 phaseListeners = new ArrayList<PhaseListener>();
367 }
368 phaseListeners.add(newPhaseListener);
369 }
370
371 /**
372 * <p>An array of Lists of events that have been queued for later
373 * broadcast, with one List for each lifecycle phase. The list
374 * indices match the ordinals of the PhaseId instances. This
375 * instance is lazily instantiated. This list is
376 * <strong>NOT</strong> part of the state that is saved and restored
377 * for this component.</p>
378 */
379 private List<List<FacesEvent>> events = null;
380
381
382 /**
383 * <p>Override the default {@link UIComponentBase#queueEvent} behavior to
384 * accumulate the queued events for later broadcasting.</p>
385 *
386 * @param event {@link FacesEvent} to be queued
387 *
388 * @throws IllegalStateException if this component is not a
389 * descendant of a {@link UIViewRoot}
390 * @throws NullPointerException if <code>event</code>
391 * is <code>null</code>
392 */
393 public void queueEvent(FacesEvent event) {
394
395 if (event == null) {
396 throw new NullPointerException();
397 }
398 int i;
399 int len = PhaseId.VALUES.size();
400 // We are a UIViewRoot, so no need to check for the ISE
401 if (events == null) {
402 List<List<FacesEvent>> events = new ArrayList<List<FacesEvent>>(len);
403 for (i = 0; i < len; i++) {
404 events.add(new ArrayList<FacesEvent>(5));
405 }
406 this.events = events;
407 }
408 events.get(event.getPhaseId().getOrdinal()).add(event);
409 }
410
411
412 /**
413 * <p>Broadcast any events that have been queued.</p>
414 *
415 * @param context {@link FacesContext} for the current request
416 * @param phaseId {@link PhaseId} of the current phase
417 */
418 private void broadcastEvents(FacesContext context, PhaseId phaseId) {
419
420 if (null == events) {
421 // no events have been queued
422 return;
423 }
424 boolean hasMoreAnyPhaseEvents;
425 boolean hasMoreCurrentPhaseEvents;
426
427 List<FacesEvent> eventsForPhaseId =
428 events.get(PhaseId.ANY_PHASE.getOrdinal());
429
430 // keep iterating till we have no more events to broadcast.
431 // This is necessary for events that cause other events to be
432 // queued. PENDING(edburns): here's where we'd put in a check
433 // to prevent infinite event queueing.
434 do {
435 // broadcast the ANY_PHASE events first
436 if (null != eventsForPhaseId) {
437 // We cannot use an Iterator because we will get
438 // ConcurrentModificationException errors, so fake it
439 while (!eventsForPhaseId.isEmpty()) {
440 FacesEvent event =
441 eventsForPhaseId.get(0);
442 UIComponent source = event.getComponent();
443 try {
444 source.broadcast(event);
445 } catch (AbortProcessingException e) {
446 if (LOGGER.isLoggable(Level.SEVERE)) {
447 UIComponent component = event.getComponent();
448 String id = "";
449 if (component != null) {
450 id = component.getId();
451 if (id == null) {
452 id = component.getClientId(context);
453 }
454 }
455 LOGGER.log(Level.SEVERE,
456 "error.component.abortprocessing_thrown",
457 new Object[]{event.getClass().getName(),
458 phaseId.toString(),
459 id});
460 LOGGER.log(Level.SEVERE, e.toString(), e);
461 }
462 }
463 eventsForPhaseId.remove(0); // Stay at current position
464 }
465 }
466
467 // then broadcast the events for this phase.
468 if (null != (eventsForPhaseId = events.get(phaseId.getOrdinal()))) {
469 // We cannot use an Iterator because we will get
470 // ConcurrentModificationException errors, so fake it
471 while (!eventsForPhaseId.isEmpty()) {
472 FacesEvent event = eventsForPhaseId.get(0);
473 UIComponent source = event.getComponent();
474 try {
475 source.broadcast(event);
476 } catch (AbortProcessingException e) {
477 ; // A "return" here would abort remaining events too
478 }
479 eventsForPhaseId.remove(0); // Stay at current position
480 }
481 }
482
483 // true if we have any more ANY_PHASE events
484 hasMoreAnyPhaseEvents =
485 (null != (eventsForPhaseId =
486 events.get(PhaseId.ANY_PHASE.getOrdinal()))) &&
487 !eventsForPhaseId.isEmpty();
488 // true if we have any more events for the argument phaseId
489 hasMoreCurrentPhaseEvents =
490 (null != events.get(phaseId.getOrdinal())) &&
491 !events.get(phaseId.getOrdinal()).isEmpty();
492
493 } while (hasMoreAnyPhaseEvents || hasMoreCurrentPhaseEvents);
494
495 }
496
497 // ------------------------------------------------ Lifecycle Phase Handlers
498
499 private void initState() {
500 skipPhase = false;
501 beforeMethodException = false;
502 phaseListenerIterator =
503 ((phaseListeners != null) ? phaseListeners.listIterator() : null);
504 }
505
506 // avoid creating the PhaseEvent if possible by doing redundant
507 // null checks.
508 private void notifyBefore(FacesContext context, PhaseId phaseId) {
509 if (null != beforePhase || null != phaseListenerIterator) {
510 notifyPhaseListeners(context, phaseId, true);
511 }
512 }
513
514 // avoid creating the PhaseEvent if possible by doing redundant
515 // null checks.
516 private void notifyAfter(FacesContext context, PhaseId phaseId) {
517 if (null != afterPhase || null != phaseListenerIterator) {
518 notifyPhaseListeners(context, phaseId, false);
519 }
520 }
521
522 /**
523 * <p>Override the default {@link UIComponentBase#processDecodes}
524 * behavior to broadcast any queued events after the default
525 * processing has been completed and to clear out any events
526 * for later phases if the event processing for this phase caused {@link
527 * FacesContext#renderResponse} or {@link FacesContext#responseComplete}
528 * to be called.</p>
529 *
530 * @param context {@link FacesContext} for the request we are processing
531 *
532 * @throws NullPointerException if <code>context</code>
533 * is <code>null</code>
534 */
535 @Override
536 public void processDecodes(FacesContext context) {
537 initState();
538 notifyBefore(context, PhaseId.APPLY_REQUEST_VALUES);
539 if (!skipPhase) {
540 super.processDecodes(context);
541 broadcastEvents(context, PhaseId.APPLY_REQUEST_VALUES);
542 }
543 clearFacesEvents(context);
544 notifyAfter(context, PhaseId.APPLY_REQUEST_VALUES);
545 }
546
547 /**
548 * <p>Override the default {@link UIComponentBase#encodeBegin}
549 * behavior. If
550 * {@link #getBeforePhaseListener} returns non-<code>null</code>,
551 * invoke it, passing a {@link PhaseEvent} for the {@link
552 * PhaseId#RENDER_RESPONSE} phase. If the internal list populated
553 * by calls to {@link #addPhaseListener} is non-empty, any listeners
554 * in that list must have their {@link PhaseListener#beforePhase}
555 * method called, passing the <code>PhaseEvent</code>. Any errors
556 * that occur during invocation of any of the the beforePhase
557 * listeners must be logged and swallowed. After listeners are invoked
558 * call superclass processing.</p>
559 */
560 @Override
561 public void encodeBegin(FacesContext context) throws IOException {
562
563 initState();
564 notifyBefore(context, PhaseId.RENDER_RESPONSE);
565
566 if (!skipPhase) {
567 super.encodeBegin(context);
568 }
569 }
570
571 /**
572 * <p>Override the default {@link UIComponentBase#encodeEnd}
573 * behavior. If {@link #getAfterPhaseListener} returns
574 * non-<code>null</code>, invoke it, passing a {@link PhaseEvent}
575 * for the {@link PhaseId#RENDER_RESPONSE} phase. Any errors that
576 * occur during invocation of the afterPhase listener must be
577 * logged and swallowed.</p>
578 */
579 @Override
580 public void encodeEnd(FacesContext context) throws IOException {
581 super.encodeEnd(context);
582
583 notifyAfter(context, PhaseId.RENDER_RESPONSE);
584 }
585
586 /**
587 * <p>Utility method that notifies phaseListeners for the given
588 * phaseId. Assumes that either or both the MethodExpression or
589 * phaseListeners data structure are non-null.</p>
590 *
591 * @param context the context for this request
592 * @param phaseId the {@link PhaseId} of the current phase
593 * @param isBefore, if true, notify beforePhase listeners. Notify
594 * afterPhase listeners otherwise.
595 */
596 private void notifyPhaseListeners(FacesContext context,
597 PhaseId phaseId,
598 boolean isBefore) {
599 PhaseEvent event = createPhaseEvent(context, phaseId);
600
601 boolean hasPhaseMethodExpression =
602 (isBefore && (null != beforePhase)) ||
603 (!isBefore && (null != afterPhase) && !beforeMethodException);
604 MethodExpression expression = isBefore ? beforePhase : afterPhase;
605
606 if (hasPhaseMethodExpression) {
607 try {
608 expression.invoke(context.getELContext(), new Object[]{event});
609 skipPhase = context.getResponseComplete() ||
610 context.getRenderResponse();
611 }
612 catch (Exception e) {
613 if (isBefore) {
614 beforeMethodException = true;
615 }
616 if (LOGGER.isLoggable(Level.SEVERE)) {
617 LOGGER.log(Level.SEVERE, "Exception", e);
618 LOGGER.log(Level.SEVERE,
619 "severe.component.unable_to_process_expression",
620 new Object[] { expression.getExpressionString(),
621 (isBefore ? "beforePhase" : "afterPhase")});
622 }
623 return;
624 }
625 }
626 if (phaseListenerIterator != null && !beforeMethodException) {
627 while ((isBefore)
628 ? phaseListenerIterator.hasNext()
629 : phaseListenerIterator.hasPrevious()) {
630 PhaseListener curListener = ((isBefore)
631 ? phaseListenerIterator.next()
632 : phaseListenerIterator
633 .previous());
634 if (phaseId == curListener.getPhaseId() ||
635 PhaseId.ANY_PHASE == curListener.getPhaseId()) {
636 try {
637 if (isBefore) {
638 curListener.beforePhase(event);
639 } else {
640 curListener.afterPhase(event);
641 }
642 skipPhase = context.getResponseComplete() ||
643 context.getRenderResponse();
644 }
645 catch (Exception e) {
646 if (isBefore && phaseListenerIterator.hasPrevious()) {
647 phaseListenerIterator.previous();
648 }
649 if (LOGGER.isLoggable(Level.SEVERE)) {
650 LOGGER.log(Level.SEVERE,
651 "severe.component.uiviewroot_error_invoking_phaselistener",
652 curListener.getClass().getName());
653 }
654 return;
655 }
656 }
657 }
658 }
659 }
660
661 private static PhaseEvent createPhaseEvent(FacesContext context,
662 PhaseId phaseId)
663 throws FacesException {
664 if (lifecycle == null) {
665 LifecycleFactory lifecycleFactory = (LifecycleFactory)
666 FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
667 String lifecycleId =
668 context.getExternalContext()
669 .getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
670 if (lifecycleId == null) {
671 lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
672 }
673 lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
674 }
675
676 return (new PhaseEvent(context, phaseId, lifecycle));
677
678 }
679
680
681 /**
682 * <p>Override the default {@link UIComponentBase#processValidators}
683 * behavior to broadcast any queued events after the default
684 * processing has been completed and to clear out any events
685 * for later phases if the event processing for this phase caused {@link
686 * FacesContext#renderResponse} or {@link FacesContext#responseComplete}
687 * to be called.</p>
688 *
689 * @param context {@link FacesContext} for the request we are processing
690 *
691 * @throws NullPointerException if <code>context</code>
692 * is <code>null</code>
693 */
694 @Override
695 public void processValidators(FacesContext context) {
696 initState();
697 notifyBefore(context, PhaseId.PROCESS_VALIDATIONS);
698 try {
699 if (!skipPhase) {
700 super.processValidators(context);
701 broadcastEvents(context, PhaseId.PROCESS_VALIDATIONS);
702 }
703 } finally {
704 clearFacesEvents(context);
705 notifyAfter(context, PhaseId.PROCESS_VALIDATIONS);
706 }
707 }
708
709
710 /**
711 * <p>Override the default {@link UIComponentBase} behavior to broadcast
712 * any queued events after the default processing has been completed
713 * and to clear out any events for later phases if the event processing
714 * for this phase caused {@link FacesContext#renderResponse} or
715 * {@link FacesContext#responseComplete} to be called.</p>
716 *
717 * @param context {@link FacesContext} for the request we are processing
718 *
719 * @throws NullPointerException if <code>context</code>
720 * is <code>null</code>
721 */
722 @Override
723 public void processUpdates(FacesContext context) {
724 initState();
725 notifyBefore(context, PhaseId.UPDATE_MODEL_VALUES);
726 try {
727 if (!skipPhase) {
728 super.processUpdates(context);
729 broadcastEvents(context, PhaseId.UPDATE_MODEL_VALUES);
730 }
731 } finally {
732 clearFacesEvents(context);
733 notifyAfter(context, PhaseId.UPDATE_MODEL_VALUES);
734 }
735 }
736
737
738 /**
739 * <p>Broadcast any events that have been queued for the <em>Invoke
740 * Application</em> phase of the request processing lifecycle
741 * and to clear out any events for later phases if the event processing
742 * for this phase caused {@link FacesContext#renderResponse} or
743 * {@link FacesContext#responseComplete} to be called.</p>
744 *
745 * @param context {@link FacesContext} for the request we are processing
746 *
747 * @throws NullPointerException if <code>context</code>
748 * is <code>null</code>
749 */
750 public void processApplication(FacesContext context) {
751 initState();
752 notifyBefore(context, PhaseId.INVOKE_APPLICATION);
753 try {
754 if (!skipPhase) {
755 // NOTE - no tree walk is performed; this is a UIViewRoot-only operation
756 broadcastEvents(context, PhaseId.INVOKE_APPLICATION);
757 }
758 } finally {
759 clearFacesEvents(context);
760 notifyAfter(context, PhaseId.INVOKE_APPLICATION);
761 }
762 }
763
764
765 // clear out the events if we're skipping to render-response
766 // or if there is a response complete signal.
767 private void clearFacesEvents(FacesContext context) {
768 if (context.getRenderResponse() || context.getResponseComplete()) {
769 if (events != null) {
770 for (List<FacesEvent> eventList : events) {
771 if (eventList != null) {
772 eventList.clear();
773 }
774 }
775 events = null;
776 }
777 }
778 }
779
780 /**
781 * <p>Generate an identifier for a component. The identifier will
782 * be prefixed with UNIQUE_ID_PREFIX, and will be unique within
783 * this UIViewRoot.</p>
784 */
785 public String createUniqueId() {
786 return UNIQUE_ID_PREFIX + lastId++;
787 }
788
789 /*
790 * <p>The locale for this view.</p>
791 */
792 private Locale locale = null;
793
794 /**
795 * <p>Return the <code>Locale</code> to be used in localizing the
796 * response being created for this view.</p>
797 * <p/>
798 * <p>Algorithm:</p>
799 * <p/>
800 * <p>If we have a <code>locale</code> ivar, return it. If we have
801 * a value expression for "locale", get its value. If the value is
802 * <code>null</code>, return the result of calling {@link
803 * javax.faces.application.ViewHandler#calculateLocale}. If the
804 * value is an instance of <code>java.util.Locale</code> return it.
805 * If the value is a String, convert it to a
806 * <code>java.util.Locale</code> and return it. If there is no
807 * value expression for "locale", return the result of calling {@link
808 * javax.faces.application.ViewHandler#calculateLocale}.</p>
809 *
810 * @return The current <code>Locale</code> obtained by executing the
811 * above algorithm.
812 */
813 public Locale getLocale() {
814 Locale result = null;
815 if (null != locale) {
816 result = this.locale;
817 } else {
818 ValueExpression vb = getValueExpression("locale");
819 FacesContext context = getFacesContext();
820 if (vb != null) {
821 Object resultLocale = null;
822
823 try {
824 resultLocale = vb.getValue(context.getELContext());
825 }
826 catch (ELException e) {
827 if (LOGGER.isLoggable(Level.SEVERE)) {
828 LOGGER.log(Level.SEVERE,
829 "severe.component.unable_to_process_expression",
830 new Object[]{vb.getExpressionString(), "locale"});
831 }
832 }
833
834 if (null == resultLocale) {
835 result =
836 context.getApplication().getViewHandler()
837 .calculateLocale(context);
838 } else if (resultLocale instanceof Locale) {
839 result = (Locale) resultLocale;
840 } else if (resultLocale instanceof String) {
841 result = getLocaleFromString((String) resultLocale);
842 }
843 } else {
844 result =
845 context.getApplication().getViewHandler()
846 .calculateLocale(context);
847 }
848 }
849 return result;
850 }
851
852
853 // W3C XML specification refers to IETF RFC 1766 for language code
854 // structure, therefore the value for the xml:lang attribute should
855 // be in the form of language or language-country or
856 // language-country-variant.
857
858 private static Locale getLocaleFromString(String localeStr)
859 throws IllegalArgumentException {
860 // length must be at least 2.
861 if (null == localeStr || localeStr.length() < 2) {
862 throw new IllegalArgumentException("Illegal locale String: " +
863 localeStr);
864 }
865
866 Locale result = null;
867 String lang = null;
868 String country = null;
869 String variant = null;
870 char[] seps = {
871 '-',
872 '_'
873 };
874 int i = 0;
875 int j = 0;
876 int inputLength = localeStr.length();
877
878 // to have a language, the length must be >= 2
879 if ((inputLength >= 2) &&
880 ((i = indexOfSet(localeStr, seps, 0)) == -1)) {
881 // we have only Language, no country or variant
882 if (localeStr.length() != 2) {
883 throw new
884 IllegalArgumentException("Illegal locale String: " +
885 localeStr);
886 }
887 lang = localeStr.toLowerCase();
888 }
889
890 // we have a separator, it must be either '-' or '_'
891 if (i != -1) {
892 lang = localeStr.substring(0, i);
893 // look for the country sep.
894 // to have a country, the length must be >= 5
895 if ((inputLength >= 5) &&
896 ((j = indexOfSet(localeStr, seps, i + 1)) == -1)) {
897 // no further separators, length must be 5
898 if (inputLength != 5) {
899 throw new
900 IllegalArgumentException("Illegal locale String: " +
901 localeStr);
902 }
903 country = localeStr.substring(i + 1);
904 }
905 if (j != -1) {
906 country = localeStr.substring(i + 1, j);
907 // if we have enough separators for language, locale,
908 // and variant, the length must be >= 8.
909 if (inputLength >= 8) {
910 variant = localeStr.substring(j + 1);
911 } else {
912 throw new
913 IllegalArgumentException("Illegal locale String: " +
914 localeStr);
915 }
916 }
917 }
918 if (variant != null && country != null && lang != null) {
919 result = new Locale(lang, country, variant);
920 } else if (lang != null && country != null) {
921 result = new Locale(lang, country);
922 } else if (lang != null) {
923 result = new Locale(lang, "");
924 }
925 return result;
926 }
927
928
929 /**
930 * @param str local string
931 * @param set the substring
932 * @param fromIndex starting index
933 * @return starting at <code>fromIndex</code>, the index of the
934 * first occurrence of any substring from <code>set</code> in
935 * <code>toSearch</code>, or -1 if no such match is found
936 */
937 private static int indexOfSet(String str, char[] set, int fromIndex) {
938 int result = -1;
939 for (int i = fromIndex, len = str.length(); i < len; i++) {
940 for (int j = 0, innerLen = set.length; j < innerLen; j++) {
941 if (str.charAt(i) == set[j]) {
942 result = i;
943 break;
944 }
945 }
946 if (result != -1) {
947 break;
948 }
949 }
950 return result;
951 }
952
953 /**
954 * <p>Set the <code>Locale</code> to be used in localizing the
955 * response being created for this view. </p>
956 *
957 * @param locale The new localization Locale
958 */
959 public void setLocale(Locale locale) {
960 this.locale = locale;
961 // Make sure to appraise the EL of this switch in Locale.
962 FacesContext.getCurrentInstance().getELContext().setLocale(locale);
963 }
964
965 // ----------------------------------------------------- StateHolder Methods
966
967
968 private Object[] values;
969
970 @Override
971 public Object saveState(FacesContext context) {
972
973 if (values == null) {
974 values = new Object[8];
975 }
976
977 values[0] = super.saveState(context);
978 values[1] = renderKitId;
979 values[2] = viewId;
980 values[3] = locale;
981 values[4] = lastId;
982 values[5] = saveAttachedState(context, beforePhase);
983 values[6] = saveAttachedState(context, afterPhase);
984 values[7] = saveAttachedState(context, phaseListeners);
985 return (values);
986
987 }
988
989 @Override
990 public void restoreState(FacesContext context, Object state) {
991
992 values = (Object[]) state;
993 super.restoreState(context, values[0]);
994 renderKitId = (String) values[1];
995 viewId = (String) values[2];
996 locale = (Locale) values[3];
997 lastId = ((Integer) values[4]).intValue();
998 beforePhase =
999 (MethodExpression) restoreAttachedState(context, values[5]);
1000 afterPhase =
1001 (MethodExpression) restoreAttachedState(context, values[6]);
1002 phaseListeners = TypedCollections.dynamicallyCastList((List)
1003 restoreAttachedState(context, values[7]), PhaseListener.class);
1004 }
1005
1006
1007 }