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

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

The 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), DoResetValues.INSTANCE); } private static class DoResetValues implements VisitCallback { private static final DoResetValues INSTANCE = new DoResetValues(); @Override public VisitResult visit(VisitContext context, UIComponent target) { if (target instanceof EditableValueHolder) { ((EditableValueHolder) target).resetValue(); } // If render ID didn't specifically point to an EditableValueHolder. Visit all children as well. else if (!VisitContext.ALL_IDS.equals(context.getIdsToVisit())) { target.visitTree(VisitContext.createVisitContext(context.getFacesContext(), null, context.getHints()), this); } 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 - 2024 Weber Informatics LLC | Privacy Policy