All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.faces.component.UIViewRoot Maven / Gradle / Ivy

Go to download

This is the master POM file for Oracle's Implementation of the JSF 2.2 Specification.

There is a newer version: 2.2.20
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package javax.faces.component;

import javax.el.MethodExpression;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.ProjectStage;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.faces.webapp.FacesServlet;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ListIterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.event.*;
import javax.faces.view.ViewDeclarationLanguage;
import javax.faces.view.ViewMetadata;

/**
 * 

UIViewRoot is the UIComponent * that represents the root of the UIComponent tree. This component * renders markup as the response to Ajax requests. It also serves as * the root of the component tree, and as a place to hang per-view * {@link PhaseListener}s.

* *

For each of the following lifecycle phase methods:

*
    *
  • {@link #processDecodes}

  • *
  • {@link #processValidators}

  • *
  • {@link #processUpdates}

  • *
  • {@link #processApplication}

  • *
  • RenderResponse, via {@link #encodeBegin} and {@link * #encodeEnd}

  • *
*

Take the following action regarding * PhaseListeners.

*
    *

    Initialize a state flag to false.

    *

    If {@link #getBeforePhaseListener} returns non-null, * invoke the listener, passing in the correct corresponding {@link * PhaseId} for this phase.

    *

    Upon return from the listener, call {@link * FacesContext#getResponseComplete} and {@link * FacesContext#getRenderResponse}. If either return true * set the internal state flag to true.

    *

    If or one or more listeners have been added by a call to {@link * #addPhaseListener}, invoke the beforePhase method on * each one whose {@link PhaseListener#getPhaseId} matches the current * phaseId, passing in the same PhaseId as in the previous * step.

    *

    Upon return from each listener, call {@link * FacesContext#getResponseComplete} and {@link * FacesContext#getRenderResponse}. If either return true * set the internal state flag to true.

    *

    Execute any processing for this phase if the internal state flag * was not set.

    *

    If {@link #getAfterPhaseListener} returns non-null, * invoke the listener, passing in the correct corresponding {@link * PhaseId} for this phase.

    *

    *

    If or one or more listeners have been added by a call to {@link * #addPhaseListener}, invoke the afterPhase method on each * one whose {@link PhaseListener#getPhaseId} matches the current * phaseId, passing in the same PhaseId as in the previous * step.

    *

    *

    *

*/ public class UIViewRoot extends UIComponentBase implements UniqueIdVendor { // ------------------------------------------------------ Manifest Constants public static final String METADATA_FACET_NAME = "javax_faces_metadata"; /** *

The key in the value set of the * view metadata BeanDescriptor, the value of which is a * List<{@link UIViewParameter.Reference}>.

* * @since 2.0 */ public static final String VIEW_PARAMETERS_KEY = "javax.faces.component.VIEW_PARAMETERS_KEY"; /**

The standard component type for this component.

*/ public static final String COMPONENT_TYPE = "javax.faces.ViewRoot"; /**

The standard component family for this component.

*/ public static final String COMPONENT_FAMILY = "javax.faces.ViewRoot"; /** *

The prefix that will be used for identifiers generated * by the createUniqueId() method. */ static public final String UNIQUE_ID_PREFIX = "j_id"; private static Lifecycle lifecycle; private static final Logger LOGGER = Logger.getLogger("javax.faces", "javax.faces.LogStrings"); private static final String LOCATION_IDENTIFIER_PREFIX = "javax_faces_location_"; private static final Map LOCATION_IDENTIFIER_MAP = new HashMap(6, 1.0f); static { LOCATION_IDENTIFIER_MAP.put("head", LOCATION_IDENTIFIER_PREFIX + "HEAD"); LOCATION_IDENTIFIER_MAP.put("form", LOCATION_IDENTIFIER_PREFIX + "FORM"); LOCATION_IDENTIFIER_MAP.put("body", LOCATION_IDENTIFIER_PREFIX + "BODY"); } enum PropertyKeys { /** *

The render kit identifier of the {@link javax.faces.render.RenderKit} associated * wth this view.

*/ renderKitId, /** *

The view identifier of this view.

*/ viewId, locale, lastId, beforePhase, afterPhase, phaseListeners, resourceLibraryContracts } // ------------------------------------------------------------ Constructors /** *

Create a new {@link UIViewRoot} instance with default property * values.

*/ public UIViewRoot() { super(); setRendererType(null); setId(createUniqueId()); } // ------------------------------------------------------ Instance Variables /** *

Set and cleared during the lifetime of a lifecycle phase. Has * no meaning between phases. If true, the lifecycle * processing for the current phase must not take place.

*/ private boolean skipPhase; /** *

Set and cleared during the lifetime of a lifecycle phase. Has no * meaning between phases. If true, the * MethodExpression associated with afterPhase * will not be invoked nor will any PhaseListeners associated with this * UIViewRoot. */ private boolean beforeMethodException; /** *

Set and cleared during the lifetime of a lifecycle phase. Has no * meaning between phases. */ private ListIterator phaseListenerIterator; // -------------------------------------------------------------- Properties /** *

Override superclass method to always return * {@code true} because a {@code UIViewRoot} is * defined to always be in a view.

* * @since 2.0 */ @Override public boolean isInView() { return true; } /** *

Overridden to take no action.

* * @since 2.0 * @param isInView */ @Override public void setInView(boolean isInView) { // no-op } /** * @see UIComponent#getFamily() */ public String getFamily() { return (COMPONENT_FAMILY); } /** *

Return the render kit identifier of the {@link * javax.faces.render.RenderKit} associated with this view. Unless * explicitly set, as in {@link * javax.faces.application.ViewHandler#createView}, the returned * value will be null.

*/ public String getRenderKitId() { return (String) getStateHelper().eval(PropertyKeys.renderKitId); } /** *

Set the render kit identifier of the {@link javax.faces.render.RenderKit} * associated with this view. This method may be called at any time * between the end of Apply Request Values phase of the * request processing lifecycle (i.e. when events are being broadcast) * and the beginning of the Render Response phase.

* * @param renderKitId The new {@link javax.faces.render.RenderKit} identifier, * or null to disassociate this view with any * specific {@link javax.faces.render.RenderKit} instance */ public void setRenderKitId(String renderKitId) { getStateHelper().put(PropertyKeys.renderKitId, renderKitId); } /**

Return the view identifier for this view.

*/ public String getViewId() { return (String) getStateHelper().get(PropertyKeys.viewId); } /** *

Set the view identifier for this view.

* * @param viewId The new view identifier */ public void setViewId(String viewId) { getStateHelper().put(PropertyKeys.viewId, viewId); } // ------------------------------------------------ Event Management Methods /** *

Return the {@link MethodExpression} that will be invoked * before this view is rendered.

* * @return the {@link MethodExpression} that will be invoked before * this view is rendered. * @since 1.2 */ public MethodExpression getBeforePhaseListener() { return (MethodExpression) getStateHelper().get(PropertyKeys.beforePhase); } /** *

Allow an arbitrary method to * be called for the "beforePhase" event as the UIViewRoot runs * through its lifecycle. This method will be called for all phases * except {@link * PhaseId#RESTORE_VIEW}. Unlike a true {@link PhaseListener}, this * approach doesn't allow for only receiving {@link PhaseEvent}s for * a given phase.

*

The method must conform to the signature of {@link * PhaseListener#beforePhase}.

* * @param newBeforePhase the {@link MethodExpression} that will be * invoked before this view is rendered. * @since 1.2 */ public void setBeforePhaseListener(MethodExpression newBeforePhase) { getStateHelper().put(PropertyKeys.beforePhase, newBeforePhase); } /** *

Return the {@link MethodExpression} that will be invoked after * this view is rendered.

* * @return the {@link MethodExpression} that will be invoked after * this view is rendered. * * @since 1.2 */ public MethodExpression getAfterPhaseListener() { return (MethodExpression) getStateHelper().get(PropertyKeys.afterPhase); } /** *

Allow an arbitrary * method to be called for the "afterPhase" event as the UIViewRoot * runs through its lifecycle. This method will be called for all * phases including {@link * PhaseId#RESTORE_VIEW}. Unlike a true {@link * PhaseListener}, this approach doesn't allow for only receiving * {@link PhaseEvent}s for a given phase.

The method * must conform to the signature of {@link * PhaseListener#afterPhase}.

* * @param newAfterPhase the {@link MethodExpression} that will be * invoked after this view is rendered. * * @since 1.2 */ public void setAfterPhaseListener(MethodExpression newAfterPhase) { getStateHelper().put(PropertyKeys.afterPhase, newAfterPhase); } /** *

If the argument toRemove is in the list of {@link * PhaseListener}s for this instance, it must be removed.

* @param toRemove the {@link PhaseListener} to remove. * * @since 1.2 */ public void removePhaseListener(PhaseListener toRemove) { getStateHelper().remove(PropertyKeys.phaseListeners, toRemove); } /** *

Add the argument newPhaseListener to the list of * {@link PhaseListener}s on this UIViewRoot.

* * @param newPhaseListener the {@link PhaseListener} to add * * @since 1.2 */ public void addPhaseListener(PhaseListener newPhaseListener) { getStateHelper().add(PropertyKeys.phaseListeners, newPhaseListener); } /** * *

Return an unmodifiable list of the * PhaseListener instances attached to this * UIViewRoot instance.

* * @since 2.0 */ public List getPhaseListeners() { //noinspection unchecked List result = (List) getStateHelper().get(PropertyKeys.phaseListeners); return ((result != null) ? Collections.unmodifiableList(result) : Collections.unmodifiableList(Collections.emptyList())); } /** *

Add argument component, * which is assumed to represent a resource instance, as a resource * to this view. A resource instance is rendered by a resource * Renderer, as described in the Standard HTML * RenderKit. The default implementation must call through to * {@link #addComponentResource(javax.faces.context.FacesContext, * javax.faces.component.UIComponent, java.lang.String)}.

* *
*

* * @param context {@link FacesContext} for the current request * @param componentResource The {@link UIComponent} representing a * {@link javax.faces.application.Resource} instance * * @since 2.0 */ public void addComponentResource(FacesContext context, UIComponent componentResource) { addComponentResource(context, componentResource, null); } /** *

Add argument component, * which is assumed to represent a resource instance, as a resource * to this view. A resource instance is rendered by a resource * Renderer, as described in the Standard HTML * RenderKit.

* *
*

The component must be added using the following * algorithm:

*
    *
  • If the target argument is null, * look for a target attribute on the * component. If there is no target * attribute, set target to be the default value * head

  • *
  • Call {@link #getComponentResources} to obtain the child * list for the given target.

  • * *
  • If the component ID of componentResource matches the * the ID of a resource that has allready been added, remove the old * resource.

  • *
  • Add the component resource to the * list.

  • *
*
* * @param context {@link FacesContext} for the current request * @param componentResource The {@link UIComponent} representing a * {@link javax.faces.application.Resource} instance * @param target The name of the facet for which the {@link UIComponent} will be added * * @since 2.0 */ public void addComponentResource(FacesContext context, UIComponent componentResource, String target) { final Map attributes = componentResource.getAttributes(); // look for a target in the component attribute set if arg is not set. if (target == null) { target = (String) attributes.get("target"); } if (target == null) { target = "head"; } List facetChildren = getComponentResources(context, target, true); String id = componentResource.getId(); if (id != null) { for (UIComponent c : facetChildren) { if (id.equals(c.getId())) { facetChildren.remove(c); } } } // add the resource to the facet facetChildren.add(componentResource); } /** *

Return an unmodifiable * List of {@link UIComponent}s for the provided * target agrument. Each component in the * List is assumed to represent a resource * instance.

* *
*

The default implementation must use an algorithm equivalent to the * the following.

*
    *
  • Locate the facet for the component by calling getFacet() using * target as the argument.
  • *
  • If the facet is not found, create the facet by calling * context.getApplication().createComponent(). The argument to this method * must refer to a component that extends {@link UIPanel} and * overrides the encodeAll() method to take no action. * This is necessary to prevent component resources from being * inadvertently rendered.
  • *
      *
    • Set the id of the * facet to be a string created by prepending the literal string * “javax_faces_location_” (without the * quotes) to the value of the target argument
    • *
    • Add the facet to the facets Map using target as the key
    • *
    *
  • return the children of the facet
  • *
*
* * @param target The name of the facet for which the components will be returned. * * @return A List of {@link UIComponent} children of * the facet with the name target. If no children are * found for the facet, return Collections.emptyList(). * * @throws NullPointerException if target or * context is null * * @since 2.0 */ public List getComponentResources(FacesContext context, String target) { if (target == null) { throw new NullPointerException(); } List resources = getComponentResources(context, target, false); return ((resources != null) ? resources : Collections.emptyList()); } /** *

Remove argument component, * which is assumed to represent a resource instance, as a resource * to this view.

* *
*

* * @param context {@link FacesContext} for the current request * @param componentResource The {@link UIComponent} representing a * {@link javax.faces.application.Resource} instance * * @since 2.0 */ public void removeComponentResource(FacesContext context, UIComponent componentResource) { removeComponentResource(context, componentResource, null); } /** *

Remove argument component, * which is assumed to represent a resource instance, as a resource * to this view. A resource instance is rendered by a resource * Renderer, as described in the Standard HTML * RenderKit.

* *
*

* The component must be removed using the following algorithm: *

    *
  • If the target argument is null, look for a target * attribute on the component.
    * If there is no target attribute, set target to be the default value head
  • *
  • Call {@link #getComponentResources} to obtain the child list for the * given target.
  • *
  • Remove the component resource from the child list.
  • *
*

*
* * @param context {@link FacesContext} for the current request * @param componentResource The {@link UIComponent} representing a * {@link javax.faces.application.Resource} instance * @param target The name of the facet for which the {@link UIComponent} will be added * * @since 2.0 */ public void removeComponentResource(FacesContext context, UIComponent componentResource, String target) { final Map attributes = componentResource.getAttributes(); // look for a target in the component attribute set if arg is not set. if (target == null) { target = (String) attributes.get("target"); } if (target == null) { target = "head"; } List facetChildren = getComponentResources(context, target, false); if (facetChildren != null) { facetChildren.remove(componentResource); } } /** *

An array of Lists of events that have been queued for later * broadcast, with one List for each lifecycle phase. The list * indices match the ordinals of the PhaseId instances. This * instance is lazily instantiated. This list is * NOT part of the state that is saved and restored * for this component.

*/ private List> events = null; /** *

Override the default {@link UIComponentBase#queueEvent} behavior to * accumulate the queued events for later broadcasting.

* * @param event {@link FacesEvent} to be queued * * @throws IllegalStateException if this component is not a * descendant of a {@link UIViewRoot} * @throws NullPointerException if event * is null */ public void queueEvent(FacesEvent event) { if (event == null) { throw new NullPointerException(); } // We are a UIViewRoot, so no need to check for the ISE if (events == null) { int len = PhaseId.VALUES.size(); List> events = new ArrayList>(len); for (int i = 0; i < len; i++) { events.add(new ArrayList(5)); } this.events = events; } events.get(event.getPhaseId().getOrdinal()).add(event); } /** *

Broadcast any events that have been * queued. First broadcast events that have been queued for {@link * PhaseId#ANY_PHASE}. Then broadcast ane events that have been * queued for the current phase. In both cases, {@link * UIComponent#pushComponentToEL} must be called before the event is * broadcast, and {@link UIComponent#popComponentFromEL} must be * called after the return from the broadcast, even in the case of * an exception.

* * @param context {@link FacesContext} for the current request * @param phaseId {@link PhaseId} of the current phase * @since 2.0 */ public void broadcastEvents(FacesContext context, PhaseId phaseId) { if (null == events) { // no events have been queued return; } boolean hasMoreAnyPhaseEvents; boolean hasMoreCurrentPhaseEvents; List eventsForPhaseId = events.get(PhaseId.ANY_PHASE.getOrdinal()); // keep iterating till we have no more events to broadcast. // This is necessary for events that cause other events to be // queued. PENDING(edburns): here's where we'd put in a check // to prevent infinite event queueing. do { // broadcast the ANY_PHASE events first if (null != eventsForPhaseId) { // We cannot use an Iterator because we will get // ConcurrentModificationException errors, so fake it while (!eventsForPhaseId.isEmpty()) { FacesEvent event = eventsForPhaseId.get(0); UIComponent source = event.getComponent(); UIComponent compositeParent = null; try { if (!UIComponent.isCompositeComponent(source)) { compositeParent = UIComponent.getCompositeComponentParent(source); } if (compositeParent != null) { compositeParent.pushComponentToEL(context, null); } source.pushComponentToEL(context, null); source.broadcast(event); } catch (AbortProcessingException e) { context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, new ExceptionQueuedEventContext(context, e, source, phaseId)); } finally { source.popComponentFromEL(context); if (compositeParent != null) { compositeParent.popComponentFromEL(context); } } eventsForPhaseId.remove(0); // Stay at current position } } // then broadcast the events for this phase. if (null != (eventsForPhaseId = events.get(phaseId.getOrdinal()))) { // We cannot use an Iterator because we will get // ConcurrentModificationException errors, so fake it while (!eventsForPhaseId.isEmpty()) { FacesEvent event = eventsForPhaseId.get(0); UIComponent source = event.getComponent(); UIComponent compositeParent = null; try { if (!UIComponent.isCompositeComponent(source)) { compositeParent = getCompositeComponentParent(source); } if (compositeParent != null) { compositeParent.pushComponentToEL(context, null); } source.pushComponentToEL(context, null); source.broadcast(event); } catch (AbortProcessingException ape) { // A "return" here would abort remaining events too context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, new ExceptionQueuedEventContext(context, ape, source, phaseId)); } finally { source.popComponentFromEL(context); if (compositeParent != null) { compositeParent.popComponentFromEL(context); } } eventsForPhaseId.remove(0); // Stay at current position } } // true if we have any more ANY_PHASE events hasMoreAnyPhaseEvents = (null != (eventsForPhaseId = events.get(PhaseId.ANY_PHASE.getOrdinal()))) && !eventsForPhaseId.isEmpty(); // true if we have any more events for the argument phaseId hasMoreCurrentPhaseEvents = (null != events.get(phaseId.getOrdinal())) && !events.get(phaseId.getOrdinal()).isEmpty(); } while (hasMoreAnyPhaseEvents || hasMoreCurrentPhaseEvents); } // ------------------------------------------------ Lifecycle Phase Handlers private void initState() { skipPhase = false; beforeMethodException = false; //noinspection unchecked List listeners = (List) getStateHelper().get(PropertyKeys.phaseListeners); phaseListenerIterator = ((listeners != null) ? listeners.listIterator() : null); } // avoid creating the PhaseEvent if possible by doing redundant // null checks. private void notifyBefore(FacesContext context, PhaseId phaseId) { if (getBeforePhaseListener() != null || phaseListenerIterator != null) { notifyPhaseListeners(context, phaseId, true); } } // avoid creating the PhaseEvent if possible by doing redundant // null checks. private void notifyAfter(FacesContext context, PhaseId phaseId) { if (getAfterPhaseListener() != null || phaseListenerIterator != null) { notifyPhaseListeners(context, phaseId, false); } } /** *

The default * implementation must call {@link * UIComponentBase#processRestoreState} from within a * try block. The try block must have a * finally block that ensures that no {@link * FacesEvent}s remain in the event queue.    

* @param context the FacesContext for this requets * @param state the opaque state object obtained from the {@link * javax.faces.application.StateManager} */ @Override public void processRestoreState(FacesContext context, Object state) { // hack to work around older state managers that may not set the // view root early enough if (context.getViewRoot() == null) { context.setViewRoot(this); } super.processRestoreState(context, state); } /** *
*

Perform partial processing by calling * {@link javax.faces.context.PartialViewContext#processPartial} with * {@link PhaseId#APPLY_REQUEST_VALUES} if: *

    *
  • {@link javax.faces.context.PartialViewContext#isPartialRequest} * returns true and we don't have a request to process all * components in the view * ({@link javax.faces.context.PartialViewContext#isExecuteAll} returns * false)
  • *
* Perform full processing by calling * {@link UIComponentBase#processDecodes} if one of the following * conditions are met: *
    *
  • {@link javax.faces.context.PartialViewContext#isPartialRequest} * returns true and we have a request to process all * components in the view * ({@link javax.faces.context.PartialViewContext#isExecuteAll} returns * true)
  • *
  • {@link javax.faces.context.PartialViewContext#isPartialRequest} * returns false
  • *
*

*
*

Override the default * {@link UIComponentBase#processDecodes} behavior to broadcast any queued * events after the default processing or partial processing has been * completed and to clear out any events for later phases if the event * processing for this phase caused {@link FacesContext#renderResponse} * or {@link FacesContext#responseComplete} to be called.

* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null */ @Override public void processDecodes(FacesContext context) { initState(); notifyBefore(context, PhaseId.APPLY_REQUEST_VALUES); try { if (!skipPhase) { if (context.getPartialViewContext().isPartialRequest() && !context.getPartialViewContext().isExecuteAll()) { context.getPartialViewContext().processPartial(PhaseId.APPLY_REQUEST_VALUES); } else { super.processDecodes(context); } broadcastEvents(context, PhaseId.APPLY_REQUEST_VALUES); } } finally { clearFacesEvents(context); notifyAfter(context, PhaseId.APPLY_REQUEST_VALUES); } } /** *

Visit the clientIds and, if the component is * an instance of {@link EditableValueHolder}, * call its {@link EditableValueHolder#resetValue} method. * Use {@link #visitTree} to do the visiting.

* * @since 2.2 * @param context the {@link FacesContext} for the request we are processing. * @param clientIds The client ids to be visited, on which the described action will be taken. */ public void resetValues(FacesContext context, Collection clientIds) { this.visitTree(VisitContext.createVisitContext(context, clientIds, null), new DoResetValues()); } private static class DoResetValues implements VisitCallback { @Override public VisitResult visit(VisitContext context, UIComponent target) { if (target instanceof EditableValueHolder) { ((EditableValueHolder)target).resetValue(); } return VisitResult.ACCEPT; } } /** *

Override the default * {@link UIComponentBase#encodeBegin} behavior. If * {@link #getBeforePhaseListener} returns non-null, * invoke it, passing a {@link PhaseEvent} for the {@link * PhaseId#RENDER_RESPONSE} phase. If the internal list populated * by calls to {@link #addPhaseListener} is non-empty, any listeners * in that list must have their {@link PhaseListener#beforePhase} * method called, passing the PhaseEvent. Any errors * that occur during invocation of any of the the beforePhase * listeners must be logged and swallowed. After listeners are invoked * call superclass processing.

*/ @Override public void encodeBegin(FacesContext context) throws IOException { initState(); notifyBefore(context, PhaseId.RENDER_RESPONSE); if (!context.getResponseComplete()) { super.encodeBegin(context); } } /** *

If {@link * javax.faces.context.PartialViewContext#isAjaxRequest} returns * true, perform partial rendering by calling * {@link javax.faces.context.PartialViewContext#processPartial} with * {@link PhaseId#RENDER_RESPONSE}. If {@link * javax.faces.context.PartialViewContext#isAjaxRequest} returns * false, delegate to the parent {@link * javax.faces.component.UIComponentBase#encodeChildren} method.

* * @since 2.0 */ @Override public void encodeChildren(FacesContext context) throws IOException { if (context.getPartialViewContext().isAjaxRequest()) { context.getPartialViewContext().processPartial(PhaseId.RENDER_RESPONSE); } else { super.encodeChildren(context); } } /** *

If {@link #getAfterPhaseListener} * returns non-null, invoke it, passing a {@link * PhaseEvent} for the {@link PhaseId#RENDER_RESPONSE} phase. Any * errors that occur during invocation of the afterPhase listener * must be logged and swallowed. If the current view has view * parameters, as indicated by a non-empty and * non-UnsupportedOperationException throwing return * from {@link javax.faces.view.ViewDeclarationLanguage#getViewMetadata(javax.faces.context.FacesContext, String)}, * call {@link UIViewParameter#encodeAll} on each parameter. If * calling getViewParameters() causes * UnsupportedOperationException to be thrown, the * exception must be silently swallowed.

*/ @Override public void encodeEnd(FacesContext context) throws IOException { super.encodeEnd(context); encodeViewParameters(context); notifyAfter(context, PhaseId.RENDER_RESPONSE); } /** *

Call {@link UIComponentBase#getRendersChildren} * If {@link javax.faces.context.PartialViewContext#isAjaxRequest} * returns true this method must return true.

* * @since 2.0 */ @Override public boolean getRendersChildren() { boolean value = super.getRendersChildren(); FacesContext context = FacesContext.getCurrentInstance(); if (context.getPartialViewContext().isAjaxRequest()) { value = true; } return value; } /** *

Utility method that notifies phaseListeners for the given * phaseId. Assumes that either or both the MethodExpression or * phaseListeners data structure are non-null.

* * @param context the context for this request * @param phaseId the {@link PhaseId} of the current phase * @param isBefore, if true, notify beforePhase listeners. Notify * afterPhase listeners otherwise. */ private void notifyPhaseListeners(FacesContext context, PhaseId phaseId, boolean isBefore) { PhaseEvent event = createPhaseEvent(context, phaseId); MethodExpression beforePhase = getBeforePhaseListener(); MethodExpression afterPhase = getAfterPhaseListener(); boolean hasPhaseMethodExpression = (isBefore && (null != beforePhase)) || (!isBefore && (null != afterPhase) && !beforeMethodException); MethodExpression expression = isBefore ? beforePhase : afterPhase; if (hasPhaseMethodExpression) { try { expression.invoke(context.getELContext(), new Object[]{event}); skipPhase = context.getResponseComplete() || context.getRenderResponse(); } catch (Exception e) { if (isBefore) { beforeMethodException = true; } if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, "severe.component.unable_to_process_expression", new Object[] { expression.getExpressionString(), (isBefore ? "beforePhase" : "afterPhase")}); } return; } } if (phaseListenerIterator != null && !beforeMethodException) { while ((isBefore) ? phaseListenerIterator.hasNext() : phaseListenerIterator.hasPrevious()) { PhaseListener curListener = ((isBefore) ? phaseListenerIterator.next() : phaseListenerIterator .previous()); if (phaseId == curListener.getPhaseId() || PhaseId.ANY_PHASE == curListener.getPhaseId()) { try { if (isBefore) { curListener.beforePhase(event); } else { curListener.afterPhase(event); } skipPhase = context.getResponseComplete() || context.getRenderResponse(); } catch (Exception e) { if (isBefore && phaseListenerIterator.hasPrevious()) { phaseListenerIterator.previous(); } if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, "severe.component.uiviewroot_error_invoking_phaselistener", curListener.getClass().getName()); } return; } } } } } private static PhaseEvent createPhaseEvent(FacesContext context, PhaseId phaseId) throws FacesException { if (lifecycle == null) { LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY); String lifecycleId = context.getExternalContext() .getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR); if (lifecycleId == null) { lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE; } lifecycle = lifecycleFactory.getLifecycle(lifecycleId); } return (new PhaseEvent(context, phaseId, lifecycle)); } /** *
*

Perform partial processing by calling * {@link javax.faces.context.PartialViewContext#processPartial} with * {@link PhaseId#PROCESS_VALIDATIONS} if: *

    *
  • {@link javax.faces.context.PartialViewContext#isPartialRequest} * returns true and we don't have a request to process all * components in the view * ({@link javax.faces.context.PartialViewContext#isExecuteAll} returns * false)
  • *
* Perform full processing by calling * {@link UIComponentBase#processValidators} if one of the following * conditions are met: *
    *
  • {@link javax.faces.context.PartialViewContext#isPartialRequest} * returns true and we have a request to process all * components in the view * ({@link javax.faces.context.PartialViewContext#isExecuteAll} returns * true)
  • *
  • {@link javax.faces.context.PartialViewContext#isPartialRequest} * returns false
  • *
*

*
*

Override the default * {@link UIComponentBase#processValidators} behavior to broadcast any * queued events after the default processing or partial processing has been * completed and to clear out any events for later phases if the event * processing for this phase caused {@link FacesContext#renderResponse} or * {@link FacesContext#responseComplete} to be called.

* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null */ @Override public void processValidators(FacesContext context) { initState(); notifyBefore(context, PhaseId.PROCESS_VALIDATIONS); try { if (!skipPhase) { if (context.getPartialViewContext().isPartialRequest() && !context.getPartialViewContext().isExecuteAll()) { context.getPartialViewContext().processPartial(PhaseId.PROCESS_VALIDATIONS); } else { super.processValidators(context); } broadcastEvents(context, PhaseId.PROCESS_VALIDATIONS); } } finally { clearFacesEvents(context); notifyAfter(context, PhaseId.PROCESS_VALIDATIONS); } } /** *
*

Perform partial processing by calling * {@link javax.faces.context.PartialViewContext#processPartial} with * {@link PhaseId#UPDATE_MODEL_VALUES} if: *

    *
  • {@link javax.faces.context.PartialViewContext#isPartialRequest} * returns true and we don't have a request to process all * components in the view * ({@link javax.faces.context.PartialViewContext#isExecuteAll} returns * false)
  • *
* Perform full processing by calling * {@link UIComponentBase#processUpdates} if one of the following * conditions are met: *
    *
  • {@link javax.faces.context.PartialViewContext#isPartialRequest} * returns true and we have a request to process all * components in the view * ({@link javax.faces.context.PartialViewContext#isExecuteAll} returns * true)
  • *
  • {@link javax.faces.context.PartialViewContext#isPartialRequest} * returns false
  • *
*

*
*

Override the default {@link UIComponentBase} * behavior to broadcast any queued events after the default processing or * partial processing has been completed and to clear out any events for * later phases if the event processing for this phase caused * {@link FacesContext#renderResponse} or * {@link FacesContext#responseComplete} to be called.

* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null */ @Override public void processUpdates(FacesContext context) { initState(); notifyBefore(context, PhaseId.UPDATE_MODEL_VALUES); try { if (!skipPhase) { if (context.getPartialViewContext().isPartialRequest() && !context.getPartialViewContext().isExecuteAll()) { context.getPartialViewContext().processPartial(PhaseId.UPDATE_MODEL_VALUES); } else { super.processUpdates(context); } broadcastEvents(context, PhaseId.UPDATE_MODEL_VALUES); } } finally { clearFacesEvents(context); notifyAfter(context, PhaseId.UPDATE_MODEL_VALUES); } } /** *

Broadcast any events that have been queued for the Invoke * Application phase of the request processing lifecycle * and to clear out any events for later phases if the event processing * for this phase caused {@link FacesContext#renderResponse} or * {@link FacesContext#responseComplete} to be called.

* * @param context {@link FacesContext} for the request we are processing * * @throws NullPointerException if context * is null */ public void processApplication(FacesContext context) { initState(); notifyBefore(context, PhaseId.INVOKE_APPLICATION); try { if (!skipPhase) { // NOTE - no tree walk is performed; this is a UIViewRoot-only operation broadcastEvents(context, PhaseId.INVOKE_APPLICATION); } } finally { clearFacesEvents(context); notifyAfter(context, PhaseId.INVOKE_APPLICATION); } } // clear out the events if we're skipping to render-response // or if there is a response complete signal. private void clearFacesEvents(FacesContext context) { if (context.getRenderResponse() || context.getResponseComplete()) { if (events != null) { for (List eventList : events) { if (eventList != null) { eventList.clear(); } } events = null; } } } /** *

Generate an * identifier for a component. The identifier will be prefixed with * UNIQUE_ID_PREFIX, and will be unique within the non-{@link NamingContainer} child * sub-trees of this UIViewRoot.

*/ public String createUniqueId() { return createUniqueId(getFacesContext(), null); } /**

Generate an identifier for a component. The identifier * will be prefixed with UNIQUE_ID_PREFIX, and will be unique * within this UIViewRoot. Optionally, a unique seed value can * be supplied by component creators which should be * included in the generated unique id.

* * @param context FacesContext * @param seed an optional seed value - e.g. based on the position of the component in the VDL-template * @return a unique-id in this component-container */ public String createUniqueId(FacesContext context, String seed) { if (seed != null) { return UIViewRoot.UNIQUE_ID_PREFIX + seed; } else { Integer i = (Integer) getStateHelper().get(PropertyKeys.lastId); int lastId = ((i != null) ? i : 0); getStateHelper().put(PropertyKeys.lastId, ++lastId); return UIViewRoot.UNIQUE_ID_PREFIX + lastId; } } /** *

Return the Locale to be used in localizing the * response being created for this view.

*

*

Algorithm:

*

*

If we have a locale ivar, return it. If we have * a value expression for "locale", get its value. If the value is * null, return the result of calling {@link * javax.faces.application.ViewHandler#calculateLocale}. If the * value is an instance of java.util.Locale return it. * If the value is a String, convert it to a * java.util.Locale and return it. If there is no * value expression for "locale", return the result of calling {@link * javax.faces.application.ViewHandler#calculateLocale}.

* * @return The current Locale obtained by executing the * above algorithm. */ public Locale getLocale() { Object result = getStateHelper().eval(PropertyKeys.locale); if (result != null) { Locale locale = null; if (result instanceof Locale) { locale = (Locale) result; } else if (result instanceof String) { locale = getLocaleFromString((String) result); } return locale; } else { FacesContext context = getFacesContext(); return context.getApplication().getViewHandler().calculateLocale(context); } } // W3C XML specification refers to IETF RFC 1766 for language code // structure, therefore the value for the xml:lang attribute should // be in the form of language or language-country or // language-country-variant. private static Locale getLocaleFromString(String localeStr) throws IllegalArgumentException { // length must be at least 2. if (null == localeStr || localeStr.length() < 2) { throw new IllegalArgumentException("Illegal locale String: " + localeStr); } Locale result = null; String lang = null; String country = null; String variant = null; char[] seps = { '-', '_' }; int inputLength = localeStr.length(); int i = 0; int j = 0; // to have a language, the length must be >= 2 if ((inputLength >= 2) && ((i = indexOfSet(localeStr, seps, 0)) == -1)) { // we have only Language, no country or variant if (2 != localeStr.length()) { throw new IllegalArgumentException("Illegal locale String: " + localeStr); } lang = localeStr.toLowerCase(); } // we have a separator, it must be either '-' or '_' if (i != -1) { lang = localeStr.substring(0, i); // look for the country sep. // to have a country, the length must be >= 5 if ((inputLength >= 5) && (-1 == (j = indexOfSet(localeStr, seps, i + 1)))) { // no further separators, length must be 5 if (inputLength != 5) { throw new IllegalArgumentException("Illegal locale String: " + localeStr); } country = localeStr.substring(i + 1); } if (j != -1) { country = localeStr.substring(i + 1, j); // if we have enough separators for language, locale, // and variant, the length must be >= 8. if (inputLength >= 8) { variant = localeStr.substring(j + 1); } else { throw new IllegalArgumentException("Illegal locale String: " + localeStr); } } } if (variant != null && country != null && lang != null) { result = new Locale(lang, country, variant); } else if (lang != null && country != null) { result = new Locale(lang, country); } else if (lang != null) { result = new Locale(lang, ""); } return result; } /** * @param str local string * @param set the substring * @param fromIndex starting index * @return starting at fromIndex, the index of the * first occurrence of any substring from set in * toSearch, or -1 if no such match is found */ private static int indexOfSet(String str, char[] set, int fromIndex) { int result = -1; for (int i = fromIndex, len = str.length(); i < len; i++) { for (int j = 0, innerLen = set.length; j < innerLen; j++) { if (str.charAt(i) == set[j]) { result = i; break; } } if (-1 != result) { break; } } return result; } /** *

Set the Locale to be used in localizing the * response being created for this view.

* * @param locale The new localization Locale */ public void setLocale(Locale locale) { getStateHelper().put(PropertyKeys.locale, locale); // Make sure to appraise the EL of this switch in Locale. FacesContext.getCurrentInstance().getELContext().setLocale(locale); } /** *

This implementation simply calls through to {@link * #getViewMap(boolean)}, passing true as the argument, and * returns the result.

*
* * @since 2.0 */ public Map getViewMap() { return getViewMap(true); } /** *

Returns * a Map that acts as the * interface to the data store that is the "view scope", or, if this * instance does not have such a Map and the * create argument is true, creates one and * returns it. This map must be instantiated lazily and cached for return * from subsequent calls to this method on this UIViewRoot * instance. {@link javax.faces.application.Application#publishEvent} must * be called, passing {@link PostConstructViewMapEvent}.class as the * first argument and this UIViewRoot instance as the second * argument.

* *

The returned Map must be implemented such that calling * clear() on the Map causes {@link javax.faces.application.Application#publishEvent} to be * called, passing {@link PreDestroyViewMapEvent}.class * as the first argument and this UIViewRoot instance * as the second argument.

*

Depending upon application * configuration, objects stored in the view map may need to be * Serializable. In general, it is a good idea to * ensure that any objects stored in the view map are * Serializable.

* *

For reasons made clear in {@link javax.faces.view.ViewScoped}, * this map must ultimately be stored in the session. For this reason, a * {@code true} value for the {@code create} argument will force the * session to be created with a call to * {@link javax.faces.context.ExternalContext#getSession(boolean)}. * *

* *

See {@link FacesContext#setViewRoot} for the specification of when the * clear() method must be called.

*

*

* * @param create true to create a new Map for this * instance if necessary; false to return * null if there's no current Map. * * @since 2.0 */ public Map getViewMap(boolean create) { Map viewMap = (Map) getTransientStateHelper().getTransient("com.sun.faces.application.view.viewMap"); if (create && viewMap == null) { viewMap = new ViewMap(getFacesContext().getApplication().getProjectStage()); getTransientStateHelper().putTransient("com.sun.faces.application.view.viewMap", viewMap); getFacesContext().getApplication().publishEvent(getFacesContext(), PostConstructViewMapEvent.class, UIViewRoot.class, this); } return viewMap; } Map,List> viewListeners; /** *

Install the listener instance * referenced by argument listener into the * UIViewRoot as a listener for events of type * systemEventClass.

* *

Note that installed listeners are not maintained as part of the * UIViewRoot's state.

* * @param systemEvent the Class of event for which * listener must be fired. * @param listener the implementation of {@link * javax.faces.event.SystemEventListener} whose {@link * javax.faces.event.SystemEventListener#processEvent} method must * be called when events of type systemEventClass are * fired. * * @throws NullPointerException if systemEventClass * or listener are null. * * @since 2.0 */ public void subscribeToViewEvent(Class systemEvent, SystemEventListener listener) { if (systemEvent == null) { throw new NullPointerException(); } if (listener == null) { throw new NullPointerException(); } if (viewListeners == null) { viewListeners = new HashMap, List>(4, 1.0f); } List listeners = viewListeners.get(systemEvent); if (listeners == null) { listeners = new CopyOnWriteArrayList(); viewListeners.put(systemEvent, listeners); } listeners.add(listener); } /** *

Remove the listener instance * referenced by argument listener from the * UIViewRoot as a listener for events of type * systemEventClass. * * @param systemEvent the Class of event for which * listener must be fired. * @param listener the implementation of {@link * javax.faces.event.SystemEventListener} whose {@link * javax.faces.event.SystemEventListener#processEvent} method must * be called when events of type systemEventClass are * fired. * * @throws NullPointerException if * systemEventClass or listener are * null. * * @since 2.0 */ public void unsubscribeFromViewEvent(Class systemEvent, SystemEventListener listener) { if (systemEvent == null) { throw new NullPointerException(); } if (listener == null) { throw new NullPointerException(); } if (viewListeners != null) { List listeners = viewListeners.get(systemEvent); if (listeners != null) { listeners.remove(listener); } } } /** *

Return the * SystemEventListener instances registered on this * UIComponent instance that are interested in events * of type eventClass.

* * @param systemEvent the Class of event for which the * listeners must be returned. * @throws NullPointerException if argument systemEvent * is null. * * @since 2.0 */ public List getViewListenersForEventClass(Class systemEvent) { if (systemEvent == null) { throw new NullPointerException(); } if (viewListeners != null) { return viewListeners.get(systemEvent); } return null; } private void encodeViewParameters(FacesContext context) { ViewDeclarationLanguage vdl = context.getApplication().getViewHandler(). getViewDeclarationLanguage(context, getViewId()); if (vdl == null) { return; } ViewMetadata metadata = vdl.getViewMetadata(context, getViewId()); if (metadata != null) { // perhaps it's not supported Collection params = ViewMetadata.getViewParameters(this); if (params.isEmpty()) { return; } try { for (UIViewParameter param : params) { param.encodeAll(context); } } catch (IOException e) { // IOException is forced by contract and is not expected to be thrown in this case throw new RuntimeException("Unexpected IOException", e); } } } /** *

Restore ViewScope state. This is * needed to allow the use of view scoped beans for EL-expressions * in the template from which the component tree is built. For * example: <ui:include * src="#{viewScopedBean.includeFileName}"/>.

* * @param context * current FacesContext. * @param state * the state object. */ public void restoreViewScopeState(FacesContext context, Object state) { if (context == null) { throw new NullPointerException(); } if (state == null) { return; } values = (Object[]) state; super.restoreState(context, values[0]); } // END TENATIVE // ----------------------------------------------------- StateHolder Methods private Object[] values; @Override public Object saveState(FacesContext context) { if (context == null) { throw new NullPointerException(); } String viewMapId = (String) getTransientStateHelper().getTransient("com.sun.faces.application.view.viewMapId"); Object superState = super.saveState(context); if (superState != null || viewMapId != null) { values = new Object[] {superState, viewMapId}; } return (values); } @Override public void restoreState(FacesContext context, Object state) { if (context == null) { throw new NullPointerException(); } if (state == null) { return; } values = (Object[]) state; if (!context.getAttributes().containsKey("com.sun.faces.application.view.restoreViewScopeOnly")) { super.restoreState(context, values[0]); } String viewMapId = (String) values[1]; getTransientStateHelper().putTransient("com.sun.faces.application.view.viewMapId", viewMapId); Map viewMaps = (Map) context.getExternalContext(). getSessionMap().get("com.sun.faces.application.view.activeViewMaps"); if (viewMaps != null) { Map viewMap = (Map) viewMaps.get(viewMapId); getTransientStateHelper().putTransient("com.sun.faces.application.view.viewMap", viewMap); } } // --------------------------------------------------------- Private Methods private static String getIdentifier(String target) { // check map String id = LOCATION_IDENTIFIER_MAP.get(target); if (id == null) { id = LOCATION_IDENTIFIER_PREFIX + target; LOCATION_IDENTIFIER_MAP.put(target, id); } return id; } @SuppressWarnings({"UnusedDeclaration"}) private List getComponentResources(FacesContext context, String target, boolean create) { String location = getIdentifier(target); UIComponent facet = getFacet(location); if (facet == null && create) { // Using an implementation specific component type to prevent // component resources being rendered at the incorrect time if // a caller calls UIViewRoot.encodeAll(). facet = context.getApplication().createComponent("javax.faces.ComponentResourceContainer"); facet.setId(location); getFacets().put(location, facet); } return ((facet != null) ? facet.getChildren() : null); } private static final class ViewMap extends HashMap { private static final long serialVersionUID = -1l; private ProjectStage stage; // -------------------------------------------------------- Constructors ViewMap(ProjectStage stage) { this.stage = stage; } // ---------------------------------------------------- Methods from Map @Override public void clear() { FacesContext context = FacesContext.getCurrentInstance(); context.getApplication().publishEvent(context, PreDestroyViewMapEvent.class, UIViewRoot.class, context.getViewRoot()); super.clear(); } @Override public Object put(String key, Object value) { if (value != null && ProjectStage.Development.equals(stage) && !(value instanceof Serializable)) { LOGGER.log(Level.WARNING, "warning.component.uiviewroot_non_serializable_attribute_viewmap", new Object[] { key, value.getClass().getName() }); } return super.put(key, value); } @Override public void putAll(Map m) { for (Map.Entry entry : m.entrySet()) { String k = entry.getKey(); Object v = entry.getValue(); this.put(k, v); } } } // END ViewMap }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy