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

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

Go to download

Jakarta Faces defines an MVC framework for building user interfaces for web applications, including UI components, state management, event handing, input validation, page navigation, and support for internationalization and accessibility.

There is a newer version: 4.1.2
Show newest version
/*
 * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package jakarta.faces.component;


import static java.util.Collections.unmodifiableList;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.WARNING;

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.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;

import jakarta.el.MethodExpression;
import jakarta.faces.FacesException;
import jakarta.faces.FactoryFinder;
import jakarta.faces.application.ProjectStage;
import jakarta.faces.application.ResourceHandler;
import jakarta.faces.component.behavior.ClientBehaviorContext;
import jakarta.faces.component.visit.VisitCallback;
import jakarta.faces.component.visit.VisitContext;
import jakarta.faces.component.visit.VisitResult;
import jakarta.faces.context.FacesContext;
import jakarta.faces.context.PartialViewContext;
import jakarta.faces.event.AbortProcessingException;
import jakarta.faces.event.ComponentSystemEvent;
import jakarta.faces.event.ExceptionQueuedEvent;
import jakarta.faces.event.ExceptionQueuedEventContext;
import jakarta.faces.event.FacesEvent;
import jakarta.faces.event.PhaseEvent;
import jakarta.faces.event.PhaseId;
import jakarta.faces.event.PhaseListener;
import jakarta.faces.event.PostConstructViewMapEvent;
import jakarta.faces.event.PostRestoreStateEvent;
import jakarta.faces.event.PreDestroyViewMapEvent;
import jakarta.faces.event.SystemEvent;
import jakarta.faces.event.SystemEventListener;
import jakarta.faces.lifecycle.Lifecycle;
import jakarta.faces.lifecycle.LifecycleFactory;
import jakarta.faces.render.ResponseStateManager;
import jakarta.faces.view.ViewDeclarationLanguage;
import jakarta.faces.view.ViewMetadata;
import jakarta.faces.webapp.FacesServlet;


/**
 * 

* 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 /** * The key in the facet collection that contains the meta data of the view root. * For example, the UIViewParameters are stored here. */ public static final String METADATA_FACET_NAME = "jakarta_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 = "jakarta.faces.component.VIEW_PARAMETERS_KEY"; /** *

* The standard component type for this component. *

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

* The standard component family for this component. *

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

* If this param is set, and calling toLowerCase().equals("true") on a String representation of its value returns true, * exceptions thrown by {@link PhaseListener}s installed on the {@code UIViewRoot} are queued to the * {@link jakarta.faces.context.ExceptionHandler} instead of being logged and swallowed. *

* * @since 2.3 */ public static final String VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS_PARAM_NAME = "jakarta.faces.VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS"; /** *

* The prefix that will be used for identifiers generated by the createUniqueId() method. */ public static final String UNIQUE_ID_PREFIX = "j_id"; private static Lifecycle lifecycle; private static final Logger LOGGER = Logger.getLogger("jakarta.faces", "jakarta.faces.LogStrings"); private static final String LOCATION_IDENTIFIER_PREFIX = "jakarta_faces_location_"; private static final Map LOCATION_IDENTIFIER_MAP = new LinkedHashMap<>(3, 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 jakarta.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 during view build time. */ private Doctype doctype; /** *

* 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 ignore the value. */ @Override public void setInView(boolean isInView) { // no-op } /** * @see UIComponent#getFamily() */ @Override public String getFamily() { return COMPONENT_FAMILY; } /** *

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

* * @return the render kit id, or null. */ public String getRenderKitId() { return (String) getStateHelper().eval(PropertyKeys.renderKitId); } /** *

* Set the render kit identifier of the {@link jakarta.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 jakarta.faces.render.RenderKit} identifier, or null to disassociate * this view with any specific {@link jakarta.faces.render.RenderKit} instance */ public void setRenderKitId(String renderKitId) { getStateHelper().put(PropertyKeys.renderKitId, renderKitId); } /** *

* Return the view identifier for this view. *

* * @return the view id. */ 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); } /** *

* Return the doctype of this view. *

* * @return the doctype of this view. * * @since 4.0 */ public Doctype getDoctype() { return doctype; } /** *

* Set the doctype of this view. *

* * @param doctype The doctype. * * @since 4.0 */ public void setDoctype(Doctype doctype) { this.doctype = doctype; } // ------------------------------------------------ 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. *

* * @return the list of phase listeners. * @since 2.0 */ @SuppressWarnings("unchecked") public List getPhaseListeners() { List result = (List) getStateHelper().get(PropertyKeys.phaseListeners); return result != null ? unmodifiableList(result) : 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(jakarta.faces.context.FacesContext, jakarta.faces.component.UIComponent, java.lang.String)}. *

* * @param context {@link FacesContext} for the current request * @param componentResource The {@link UIComponent} representing a {@link jakarta.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. *

    *
  • * *
* *
* *
*

* The resource Renderer must ensure of the following: *

    *
  • Do not render when {@link ResourceHandler#isResourceRendered(FacesContext, String, String)} returns * true.
  • *
  • After rendering, call {@link ResourceHandler#markResourceRendered(FacesContext, String, String)}.
  • *
*
* * @param context {@link FacesContext} for the current request * @param componentResource The {@link UIComponent} representing a {@link jakarta.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"; } // this is not a normal "List" but a custom implementation called "ChildrenList" List facetChildren = getComponentResources(context, target, true); String id = componentResource.getId(); if (id != null) { // normally the following code may produce a ConcurrentModificationException // but probably the ChildrenList implementation knows how and when delete an UIComponent. // Normally we should use "removeIf" but doing this will produce other errors during rendering 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 “jakarta_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 context the Faces context. * @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(); } /** *

* Return an unmodifiable ordered List of all {@link UIComponent} resources of all supported targets. Each * component in the List is assumed to represent a resource instance. The ordering is the same * as the resources would appear in the component tree. *

* * @param context The Faces context. * * @return A List of all {@link UIComponent} resources of all supported targets. If no resources are found, * return an empty List. * * @throws NullPointerException If context is null. * * @since 2.3 */ public List getComponentResources(FacesContext context) { List resources = new ArrayList<>(); for (String target : LOCATION_IDENTIFIER_MAP.keySet()) { resources.addAll(getComponentResources(context, target)); } return unmodifiableList(resources); } /** *

* 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 jakarta.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 jakarta.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; /** *

* 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 */ @Override 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 (events == null) { // 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 ((eventsForPhaseId = events.get(phaseId.getOrdinal())) != null) { // 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 @SuppressWarnings("unchecked") private void initState() { skipPhase = false; beforeMethodException = false; 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 jakarta.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); } /** *

* If the argument event is an instance of {@link PostRestoreStateEvent} and * {@link PartialViewContext#isPartialRequest()} returns true, then loop over all component resources and * call {@link ResourceHandler#markResourceRendered(FacesContext, String, String)} for each of them. Finally, delegate * to super. *

*/ @Override public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { FacesContext context = event.getFacesContext(); if (event instanceof PostRestoreStateEvent && context.getPartialViewContext().isPartialRequest() && !context.getPartialViewContext().isRenderAll()) { ResourceHandler resourceHandler = context.getApplication().getResourceHandler(); for (UIComponent resource : getComponentResources(context)) { String name = (String) resource.getAttributes().get("name"); String library = (String) resource.getAttributes().get("library"); resourceHandler.markResourceRendered(context, name, library); } } super.processEvent(event); } /** *
*

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

    *
  • {@link jakarta.faces.context.PartialViewContext#isPartialRequest} returns true and we don't have a * request to process all components in the view ({@link jakarta.faces.context.PartialViewContext#isExecuteAll} returns * false)
  • *
* Perform full processing by calling {@link UIComponentBase#processDecodes} if one of the following conditions are met: *
    *
  • {@link jakarta.faces.context.PartialViewContext#isPartialRequest} returns true and we have a request * to process all components in the view ({@link jakarta.faces.context.PartialViewContext#isExecuteAll} returns * true)
  • *
  • {@link jakarta.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) { 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 {@code Exception}s that occur during invocation of any of the beforePhase listeners must * be logged and swallowed, unless the * {@link #VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS_PARAM_NAME} parameter is set. In that case, the {@code Exception} * must be passed to the {@link jakarta.faces.context.ExceptionHandler} as well. *

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

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

* *

* If this {@link UIViewRoot} is an instance of {@link NamingContainer}, then the Jakarta Faces implementation * must ensure that all encoded POST request parameter names are prefixed with * {@link UIViewRoot#getContainerClientId(FacesContext)} as per rules of {@link UIComponent#getClientId(FacesContext)}. * This also covers all predefined POST request parameters which are listed below: *

*
    *
  • {@link ResponseStateManager#VIEW_STATE_PARAM}
  • *
  • {@link ResponseStateManager#CLIENT_WINDOW_PARAM}
  • *
  • {@link ResponseStateManager#RENDER_KIT_ID_PARAM}
  • *
  • {@link ClientBehaviorContext#BEHAVIOR_SOURCE_PARAM_NAME}
  • *
  • {@link ClientBehaviorContext#BEHAVIOR_EVENT_PARAM_NAME}
  • *
  • {@link PartialViewContext#PARTIAL_EVENT_PARAM_NAME}
  • *
  • {@link PartialViewContext#PARTIAL_EXECUTE_PARAM_NAME}
  • *
  • {@link PartialViewContext#PARTIAL_RENDER_PARAM_NAME}
  • *
  • {@link PartialViewContext#RESET_VALUES_PARAM_NAME}
  • *
* * @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 {@code Exception}s that occur during * invocation of the afterPhase listener must be logged and swallowed, unless the * {@link #VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS_PARAM_NAME} parameter is set. In that case, the {@code Exception} * must be passed to the {@link jakarta.faces.context.ExceptionHandler} as well.. If the current view has view * parameters, as indicated by a non-empty and non-UnsupportedOperationException throwing return from * {@link jakarta.faces.view.ViewDeclarationLanguage#getViewMetadata(jakarta.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 jakarta.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(SEVERE)) { LOGGER.log(SEVERE, "severe.component.unable_to_process_expression", new Object[] { expression.getExpressionString(), isBefore ? "beforePhase" : "afterPhase" }); } if (context.getAttributes().containsKey(VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS_PARAM_NAME)) { ExceptionQueuedEventContext extx = new ExceptionQueuedEventContext(context, e); String booleanKey = isBefore ? ExceptionQueuedEventContext.IN_BEFORE_PHASE_KEY : ExceptionQueuedEventContext.IN_AFTER_PHASE_KEY; extx.getAttributes().put(booleanKey, Boolean.TRUE); context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, extx); } 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(SEVERE)) { LOGGER.log(SEVERE, "severe.component.uiviewroot_error_invoking_phaselistener", curListener.getClass().getName()); } if (context.getAttributes().containsKey(VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS_PARAM_NAME) && (Boolean) context.getAttributes().get(VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS_PARAM_NAME)) { ExceptionQueuedEventContext extx = new ExceptionQueuedEventContext(context, e); String booleanKey = isBefore ? ExceptionQueuedEventContext.IN_BEFORE_PHASE_KEY : ExceptionQueuedEventContext.IN_AFTER_PHASE_KEY; extx.getAttributes().put(booleanKey, Boolean.TRUE); context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, extx); } 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 jakarta.faces.context.PartialViewContext#processPartial} with * {@link PhaseId#PROCESS_VALIDATIONS} if: *

    *
  • {@link jakarta.faces.context.PartialViewContext#isPartialRequest} returns true and we don't have a * request to process all components in the view ({@link jakarta.faces.context.PartialViewContext#isExecuteAll} returns * false)
  • *
* Perform full processing by calling {@link UIComponentBase#processValidators} if one of the following conditions are * met: *
    *
  • {@link jakarta.faces.context.PartialViewContext#isPartialRequest} returns true and we have a request * to process all components in the view ({@link jakarta.faces.context.PartialViewContext#isExecuteAll} returns * true)
  • *
  • {@link jakarta.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 jakarta.faces.context.PartialViewContext#processPartial} with * {@link PhaseId#UPDATE_MODEL_VALUES} if: *

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

* * @return the identifier. */ 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 */ @Override public String createUniqueId(FacesContext context, String seed) { if (seed != null) { return UIViewRoot.UNIQUE_ID_PREFIX + seed; } 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 jakarta.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 jakarta.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; } 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 (localeStr == null || 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 ( (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) { result = new Locale(lang, country, variant); } else if (country != null) { result = new Locale(lang, country); } else { 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 Jakarta Expression Language 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. *

*
* * @return the view map, or null. * @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 jakarta.faces.application.Application#publishEvent} must be called, passing * the current FacesContext as the first argument, * {@link PostConstructViewMapEvent}.class as the second argument, * UIViewRoot.class as the third argument and this * UIViewRoot instance as the fourth argument. It is necessary to pass the * UIViewRoot.class argument to account for cases when the UIViewRoot has been extended with a * custom class. *

* *

* The returned Map must be implemented such that calling clear() on the Map * causes {@link jakarta.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 jakarta.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 jakarta.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. * @return the view map, or null. * @since 2.0 */ @SuppressWarnings("unchecked") 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 jakarta.faces.event.SystemEventListener} whose * {@link jakarta.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<>(4, 1.0f); } viewListeners.computeIfAbsent(systemEvent, k -> new CopyOnWriteArrayList<>()) .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 jakarta.faces.event.SystemEventListener} whose * {@link jakarta.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. * * @return Collection of view listeners. * * @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 @SuppressWarnings("unchecked") 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.computeIfAbsent(target, t -> LOCATION_IDENTIFIER_PREFIX + t); return id; } 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("jakarta.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 final 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(WARNING, "warning.component.uiviewroot_non_serializable_attribute_viewmap", new Object[] { key, value.getClass().getName() }); } return super.put(key, value); } @Override public void putAll(Map map) { // the HashMap.putAll use an internal algorithm instead of the put method. // We want that our putAll calls our put method to log the warn message map.forEach(this::put); } } // END ViewMap }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy