| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.myfaces.view.facelets; |
| |
| import java.beans.BeanDescriptor; |
| import java.beans.BeanInfo; |
| import java.beans.PropertyDescriptor; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.Writer; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import java.util.stream.Stream; |
| |
| import javax.el.ELContext; |
| import javax.el.ELException; |
| import javax.el.MethodExpression; |
| import javax.el.ValueExpression; |
| import javax.el.VariableMapper; |
| import javax.faces.FacesException; |
| import javax.faces.FacesWrapper; |
| import javax.faces.application.Application; |
| import javax.faces.application.ProjectStage; |
| import javax.faces.application.Resource; |
| import javax.faces.application.StateManager; |
| import javax.faces.application.ViewHandler; |
| import javax.faces.application.ViewVisitOption; |
| import javax.faces.component.ActionSource2; |
| import javax.faces.component.EditableValueHolder; |
| import javax.faces.component.UIComponent; |
| import javax.faces.component.UINamingContainer; |
| import javax.faces.component.UIPanel; |
| import javax.faces.component.UIViewRoot; |
| import javax.faces.component.visit.VisitContext; |
| import javax.faces.context.ExternalContext; |
| import javax.faces.context.FacesContext; |
| import javax.faces.context.ResponseWriter; |
| import javax.faces.event.ActionEvent; |
| import javax.faces.event.ActionListener; |
| import javax.faces.event.MethodExpressionActionListener; |
| import javax.faces.event.MethodExpressionValueChangeListener; |
| import javax.faces.event.PhaseId; |
| import javax.faces.event.PostAddToViewEvent; |
| import javax.faces.event.PostRestoreStateEvent; |
| import javax.faces.event.ValueChangeEvent; |
| import javax.faces.event.ValueChangeListener; |
| import javax.faces.render.RenderKit; |
| import javax.faces.render.ResponseStateManager; |
| import javax.faces.validator.MethodExpressionValidator; |
| import javax.faces.validator.Validator; |
| import javax.faces.view.ActionSource2AttachedObjectHandler; |
| import javax.faces.view.ActionSource2AttachedObjectTarget; |
| import javax.faces.view.AttachedObjectHandler; |
| import javax.faces.view.AttachedObjectTarget; |
| import javax.faces.view.BehaviorHolderAttachedObjectHandler; |
| import javax.faces.view.BehaviorHolderAttachedObjectTarget; |
| import javax.faces.view.EditableValueHolderAttachedObjectHandler; |
| import javax.faces.view.EditableValueHolderAttachedObjectTarget; |
| import javax.faces.view.StateManagementStrategy; |
| import javax.faces.view.ValueHolderAttachedObjectHandler; |
| import javax.faces.view.ValueHolderAttachedObjectTarget; |
| import javax.faces.view.ViewDeclarationLanguage; |
| import javax.faces.view.ViewMetadata; |
| import javax.faces.view.facelets.Facelet; |
| import javax.faces.view.facelets.FaceletContext; |
| import javax.faces.view.facelets.ResourceResolver; |
| import javax.servlet.http.HttpServletResponse; |
| import org.apache.myfaces.application.StateManagerImpl; |
| |
| import org.apache.myfaces.config.RuntimeConfig; |
| import org.apache.myfaces.application.ViewIdSupport; |
| import org.apache.myfaces.config.MyfacesConfig; |
| import org.apache.myfaces.util.lang.ClassUtils; |
| import org.apache.myfaces.util.lang.StringUtils; |
| import org.apache.myfaces.component.visit.MyFacesVisitHints; |
| import org.apache.myfaces.util.WebConfigParamUtils; |
| import org.apache.myfaces.view.ViewDeclarationLanguageStrategy; |
| import org.apache.myfaces.view.ViewMetadataBase; |
| import org.apache.myfaces.view.facelets.compiler.Compiler; |
| import org.apache.myfaces.view.facelets.compiler.SAXCompiler; |
| import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils; |
| import org.apache.myfaces.view.facelets.el.LocationMethodExpression; |
| import org.apache.myfaces.view.facelets.el.LocationValueExpression; |
| import org.apache.myfaces.view.facelets.el.MethodExpressionMethodExpression; |
| import org.apache.myfaces.view.facelets.el.RedirectMethodExpressionValueExpressionActionListener; |
| import org.apache.myfaces.view.facelets.el.RedirectMethodExpressionValueExpressionValidator; |
| import org.apache.myfaces.view.facelets.el.RedirectMethodExpressionValueExpressionValueChangeListener; |
| import org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression; |
| import org.apache.myfaces.view.facelets.el.VariableMapperWrapper; |
| import org.apache.myfaces.view.facelets.impl.DefaultFaceletFactory; |
| import org.apache.myfaces.view.facelets.impl.DefaultResourceResolver; |
| import org.apache.myfaces.view.facelets.tag.composite.ClientBehaviorAttachedObjectTarget; |
| import org.apache.myfaces.view.facelets.tag.composite.ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper; |
| import org.apache.myfaces.view.facelets.tag.composite.ClientBehaviorRedirectEventComponentWrapper; |
| import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport; |
| import org.apache.myfaces.view.facelets.tag.jsf.core.AjaxHandler; |
| import org.apache.myfaces.view.facelets.tag.ui.UIDebug; |
| |
| import static org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy.*; |
| import org.apache.myfaces.view.facelets.compiler.FaceletsCompilerSupport; |
| import org.apache.myfaces.view.facelets.compiler.RefreshDynamicComponentListener; |
| import org.apache.myfaces.view.facelets.impl.SectionUniqueIdCounter; |
| import org.apache.myfaces.view.facelets.pool.RestoreViewFromPoolResult; |
| import org.apache.myfaces.view.facelets.pool.ViewEntry; |
| import org.apache.myfaces.view.facelets.pool.ViewPool; |
| import org.apache.myfaces.view.facelets.pool.ViewStructureMetadata; |
| import org.apache.myfaces.view.facelets.tag.composite.CreateDynamicCompositeComponentListener; |
| import org.apache.myfaces.view.facelets.tag.jsf.PartialMethodExpressionActionListener; |
| import org.apache.myfaces.view.facelets.tag.jsf.PartialMethodExpressionValidator; |
| import org.apache.myfaces.view.facelets.tag.jsf.PartialMethodExpressionValueChangeListener; |
| import org.apache.myfaces.view.facelets.util.FaceletsTemplateMappingUtils; |
| import org.apache.myfaces.view.facelets.util.FaceletsViewDeclarationLanguageUtils; |
| |
| /** |
| * This class represents the abstraction of Facelets as a ViewDeclarationLanguage. |
| * |
| * @author Simon Lessard (latest modification by $Author$) |
| * @version $Revision$ $Date$ |
| * |
| * @since 2.0 |
| */ |
| public class FaceletViewDeclarationLanguage extends FaceletViewDeclarationLanguageBase |
| { |
| private static final Logger log = Logger.getLogger(FaceletViewDeclarationLanguage.class.getName()); |
| |
| private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; |
| |
| private static final Class<?>[] VALUE_CHANGE_LISTENER_SIGNATURE = new Class[]{ValueChangeEvent.class}; |
| |
| private static final Class<?>[] ACTION_LISTENER_SIGNATURE = new Class[]{ActionEvent.class}; |
| |
| private static final Class<?>[] VALIDATOR_SIGNATURE |
| = new Class[]{FacesContext.class, UIComponent.class, Object.class}; |
| |
| public final static long DEFAULT_REFRESH_PERIOD = 0; |
| public final static long DEFAULT_REFRESH_PERIOD_PRODUCTION = -1; |
| |
| public final static String DEFAULT_CHARACTER_ENCODING = "UTF-8"; |
| |
| /** |
| * Constant used by EncodingHandler to indicate the current encoding of the page being built, |
| * and indicate which one is the response encoding on getResponseEncoding(FacesContext, String) method. |
| */ |
| public final static String PARAM_ENCODING = "facelets.Encoding"; |
| |
| |
| //BEGIN CONSTANTS SET ON BUILD VIEW |
| public final static String BUILDING_VIEW_METADATA = "org.apache.myfaces.BUILDING_VIEW_METADATA"; |
| |
| public final static String REFRESHING_TRANSIENT_BUILD = "org.apache.myfaces.REFRESHING_TRANSIENT_BUILD"; |
| |
| public final static String REFRESH_TRANSIENT_BUILD_ON_PSS = "org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS"; |
| |
| public final static String USING_PSS_ON_THIS_VIEW = "org.apache.myfaces.USING_PSS_ON_THIS_VIEW"; |
| |
| public final static String REMOVING_COMPONENTS_BUILD = "org.apache.myfaces.REMOVING_COMPONENTS_BUILD"; |
| |
| public final static String DYN_WRAPPER = "oam.vf.DYN_WRAPPER"; |
| |
| public final static String GEN_MARK_ID = "oam.vf.GEN_MARK_ID"; |
| //END CONSTANTS SET ON BUILD VIEW |
| |
| /** |
| * Marker to indicate tag handlers the view currently being built is using |
| * partial state saving and it is necessary to call UIComponent.markInitialState |
| * after component instances are populated. |
| */ |
| public final static String MARK_INITIAL_STATE_KEY = "org.apache.myfaces.MARK_INITIAL_STATE"; |
| |
| public final static String IS_BUILDING_INITIAL_STATE_KEY_ALIAS |
| = "javax.faces.view.ViewDeclarationLanguage.IS_BUILDING_INITIAL_STATE"; |
| |
| public final static String CLEAN_TRANSIENT_BUILD_ON_RESTORE |
| = "org.apache.myfaces.CLEAN_TRANSIENT_BUILD_ON_RESTORE"; |
| |
| private final static String STATE_KEY = "<!--@@JSF_FORM_STATE_MARKER@@-->"; |
| |
| private final static int STATE_KEY_LEN = STATE_KEY.length(); |
| |
| private static final String SERIALIZED_VIEW_REQUEST_ATTR = |
| StateManagerImpl.class.getName() + ".SERIALIZED_VIEW"; |
| |
| /** |
| * Key used to cache component ids for the counter |
| */ |
| public final static String CACHED_COMPONENT_IDS = "oam.CACHED_COMPONENT_IDS"; |
| |
| private static final String ASTERISK = "*"; |
| |
| private final FaceletFactory faceletFactory; |
| private final ViewDeclarationLanguageStrategy strategy; |
| private final FaceletsCompilerSupport faceletsCompilerSupport; |
| private final MyfacesConfig config; |
| private final ViewPoolProcessor viewPoolProcessor; |
| private final ViewIdSupport viewIdSupport; |
| |
| private StateManagementStrategy stateManagementStrategy; |
| private Set<String> fullStateSavingViewIds; |
| private ResourceResolver _resourceResolver; |
| private Map<String, List<String>> _contractMappings; |
| private List<String> _prefixWildcardKeys; |
| |
| public FaceletViewDeclarationLanguage(FacesContext context) |
| { |
| this(context, new FaceletViewDeclarationLanguageStrategy()); |
| } |
| |
| public FaceletViewDeclarationLanguage(FacesContext context, ViewDeclarationLanguageStrategy strategy) |
| { |
| log.finest("Initializing"); |
| |
| this.config = MyfacesConfig.getCurrentInstance(context); |
| this.strategy = strategy; |
| this.viewPoolProcessor = ViewPoolProcessor.getInstance(context); |
| this.viewIdSupport = ViewIdSupport.getInstance(context); |
| this.faceletsCompilerSupport = new FaceletsCompilerSupport(); |
| |
| Compiler compiler = createCompiler(context); |
| |
| faceletFactory = createFaceletFactory(context, compiler); |
| |
| if (config.isPartialStateSaving()) |
| { |
| stateManagementStrategy = new DefaultFaceletsStateManagementStrategy(context); |
| } |
| |
| ExternalContext externalContext = context.getExternalContext(); |
| |
| String[] fullStateSavingViewIds = config.getFullStateSavingViewIds(); |
| if (fullStateSavingViewIds != null && fullStateSavingViewIds.length > 0) |
| { |
| this.fullStateSavingViewIds = new HashSet<>(fullStateSavingViewIds.length, 1.0f); |
| Collections.addAll(this.fullStateSavingViewIds, fullStateSavingViewIds); |
| } |
| |
| _initializeContractMappings(externalContext); |
| |
| // Create a component ids cache and store it on application map to |
| // reduce the overhead associated with create such ids over and over. |
| if (config.getComponentUniqueIdsCacheSize() > 0) |
| { |
| String[] componentIdsCached = SectionUniqueIdCounter.generateUniqueIdCache("_", |
| config.getComponentUniqueIdsCacheSize()); |
| externalContext.getApplicationMap().put(CACHED_COMPONENT_IDS, componentIdsCached); |
| } |
| |
| log.finest("Initialization Successful"); |
| } |
| |
| |
| @Override |
| public String getId() |
| { |
| return ViewDeclarationLanguage.FACELETS_VIEW_DECLARATION_LANGUAGE_ID; |
| } |
| |
| @Override |
| public boolean viewExists(FacesContext facesContext, String viewId) |
| { |
| if (strategy.handles(viewId)) |
| { |
| if (_resourceResolver instanceof DefaultResourceResolver) |
| { |
| return ((DefaultResourceResolver)_resourceResolver).resolveUrl(facesContext, viewId) != null; |
| } |
| else |
| { |
| return _resourceResolver.resolveUrl(viewId) != null; |
| } |
| } |
| return false; |
| } |
| |
| private RestoreViewFromPoolResult tryRestoreViewFromCache(FacesContext context, UIViewRoot view) |
| { |
| if (viewPoolProcessor != null) |
| { |
| ViewPool viewPool = viewPoolProcessor.getViewPool(context, view); |
| if (viewPool != null) |
| { |
| ViewStructureMetadata metadata = viewPool.retrieveStaticViewStructureMetadata(context, view); |
| if (metadata != null) |
| { |
| ViewEntry entry = viewPool.popStaticOrPartialStructureView(context, view); |
| if (entry != null) |
| { |
| viewPoolProcessor.cloneAndRestoreView(context, view, entry, metadata); |
| return entry.getResult(); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void buildView(FacesContext context, UIViewRoot view) throws IOException |
| { |
| if (isFilledView(context, view)) |
| { |
| if (view != null && |
| FaceletViewDeclarationLanguageBase.isDynamicComponentRefreshTransientBuildActive(context, view)) |
| { |
| // don't return |
| } |
| else |
| { |
| return; |
| } |
| } |
| |
| String viewId = view.getViewId(); |
| |
| if (log.isLoggable(Level.FINEST)) |
| { |
| log.finest("Building View: " + viewId); |
| } |
| |
| boolean usePartialStateSavingOnThisView = _usePartialStateSavingOnThisView(viewId); |
| boolean refreshTransientBuild = view.getChildCount() > 0; |
| boolean refreshTransientBuildOnPSS = usePartialStateSavingOnThisView && config.isRefreshTransientBuildOnPSS(); |
| boolean refreshPartialView = false; |
| |
| if (viewPoolProcessor != null && !refreshTransientBuild) |
| { |
| RestoreViewFromPoolResult result = tryRestoreViewFromCache(context, view); |
| if (result != null) |
| { |
| // Since all transient stuff has been removed, add listeners that keep |
| // track of tree updates. |
| if (RestoreViewFromPoolResult.COMPLETE.equals(result)) |
| { |
| if (!PhaseId.RESTORE_VIEW.equals(context.getCurrentPhaseId())) |
| { |
| ((DefaultFaceletsStateManagementStrategy) |
| getStateManagementStrategy(context, view.getViewId())). |
| suscribeListeners(view); |
| } |
| // If the result is complete, the view restored here is static. |
| // static views can be marked as filled. |
| if (!refreshTransientBuildOnPSS) |
| { |
| // This option will be true on this cases: |
| // -pss is true and refresh is not active |
| setFilledView(context, view); |
| } |
| //At this point refreshTransientBuild = false && refreshTransientBuildOnPSS is true |
| else if (config.isRefreshTransientBuildOnPSSAuto() && |
| !context.getAttributes().containsKey(CLEAN_TRANSIENT_BUILD_ON_RESTORE)) |
| { |
| setFilledView(context, view); |
| } |
| return; |
| } |
| else |
| { |
| // We need to refresh a partial view. |
| refreshTransientBuild = true; |
| refreshPartialView = true; |
| } |
| } |
| } |
| |
| if (usePartialStateSavingOnThisView) |
| { |
| // Before apply we need to make sure the current view has |
| // a clientId that will be used as a key to save and restore |
| // the current view. Note that getClientId is never called (or used) |
| // from UIViewRoot. |
| if (view.getId() == null) |
| { |
| view.setId(view.createUniqueId(context, null)); |
| } |
| |
| context.getAttributes().put(USING_PSS_ON_THIS_VIEW, Boolean.TRUE); |
| //Add a key to indicate ComponentTagHandlerDelegate to |
| //call UIComponent.markInitialState after it is populated |
| if (!refreshTransientBuild || refreshPartialView) |
| { |
| context.getAttributes().put(StateManager.IS_BUILDING_INITIAL_STATE, Boolean.TRUE); |
| context.getAttributes().put(IS_BUILDING_INITIAL_STATE_KEY_ALIAS, Boolean.TRUE); |
| } |
| if (!refreshTransientBuild && config.isMarkInitialStateWhenApplyBuildView()) |
| { |
| context.getAttributes().put(MARK_INITIAL_STATE_KEY, Boolean.TRUE); |
| } |
| if (refreshTransientBuildOnPSS) |
| { |
| //This value is only set when _refreshTransientBuildOnPSSMode is "auto" or "true" |
| context.getAttributes().put(REFRESH_TRANSIENT_BUILD_ON_PSS, |
| config.isRefreshTransientBuildOnPSSAuto() ? "auto" : "true"); |
| } |
| } |
| |
| try |
| { |
| if (refreshTransientBuild) |
| { |
| context.getAttributes().put(REFRESHING_TRANSIENT_BUILD, Boolean.TRUE); |
| |
| // In theory, this should be disabled on ComponentTagHandlerDelegate, |
| // otherwise we could lost PostAddToViewEvent / PreRemoveFromViewEvent |
| // caused by c:if effect or facelets cleanup algorithm |
| //context.setProcessingEvents(false); |
| } |
| // populate UIViewRoot |
| _getFacelet(context, viewId).apply(context, view); |
| } |
| finally |
| { |
| if (refreshTransientBuildOnPSS) |
| { |
| context.getAttributes().remove(REFRESH_TRANSIENT_BUILD_ON_PSS); |
| } |
| if (refreshTransientBuild) |
| { |
| //context.setProcessingEvents(true); |
| if (FaceletViewDeclarationLanguageBase.isDynamicComponentRefreshTransientBuildActive(context)) |
| { |
| VisitContext visitContext = (VisitContext) getVisitContextFactory(). |
| getVisitContext(context, null, MyFacesVisitHints.SET_SKIP_ITERATION); |
| view.visitTree(visitContext, PublishDynamicComponentRefreshTransientBuildCallback.INSTANCE); |
| } |
| if (!usePartialStateSavingOnThisView || refreshTransientBuildOnPSS) |
| { |
| // When the facelet is applied, all components are removed and added from view, |
| // but the difference resides in the ordering. Since the view is |
| // being refreshed, if we don't do this manually, some tags like |
| // cc:insertChildren or cc:insertFacet will not work correctly, because |
| // we expect PostAddToViewEvent will be propagated from parent to child, and |
| // facelets refreshing algorithm do the opposite. |
| //FaceletViewDeclarationLanguage._publishPreRemoveFromViewEvent(context, view); |
| //FaceletViewDeclarationLanguage._publishPostAddToViewEvent(context, view); |
| } |
| |
| context.getAttributes().remove(REFRESHING_TRANSIENT_BUILD); |
| } |
| else |
| { |
| // Publish PostAddToView over UIViewRoot, because this is not done automatically. |
| context.getApplication().publishEvent(context, PostAddToViewEvent.class, UIViewRoot.class, view); |
| } |
| } |
| |
| // set this view as filled |
| if (refreshTransientBuild) |
| { |
| //This option will be true on this cases: |
| //- pss is false, but we are refreshing |
| //- pss is true, and we are refreshing a view already filled |
| setFilledView(context, view); |
| } |
| else if (!refreshTransientBuildOnPSS) |
| { |
| // This option will be true on this cases: |
| // -pss is true and refresh is not active |
| setFilledView(context, view); |
| } |
| //At this point refreshTransientBuild = false && refreshTransientBuildOnPSS is true |
| else if (config.isRefreshTransientBuildOnPSSAuto() |
| && !context.getAttributes().containsKey(CLEAN_TRANSIENT_BUILD_ON_RESTORE)) |
| { |
| setFilledView(context, view); |
| } |
| |
| // Suscribe listeners if we are using partialStateSaving |
| if (usePartialStateSavingOnThisView) |
| { |
| // UIViewRoot.markInitialState() is not called because it does |
| // not have a facelet tag handler class that create it, instead |
| // new instances are created programatically. |
| if (!refreshTransientBuild || refreshPartialView) |
| { |
| // Save the state |
| if (viewPoolProcessor != null && |
| viewPoolProcessor.isViewPoolEnabledForThisView(context, view)) |
| { |
| viewPoolProcessor.storeViewStructureMetadata(context, view); |
| } |
| if (config.isMarkInitialStateWhenApplyBuildView()) |
| { |
| if (!refreshTransientBuildOnPSS || |
| !view.getAttributes().containsKey(COMPONENT_ADDED_AFTER_BUILD_VIEW)) |
| { |
| view.markInitialState(); |
| } |
| |
| //Remove the key that indicate we need to call UIComponent.markInitialState |
| //on the current tree |
| context.getAttributes().remove(MARK_INITIAL_STATE_KEY); |
| } |
| else |
| { |
| context.getAttributes().put(MARK_INITIAL_STATE_KEY, Boolean.TRUE); |
| _markInitialStateOnView(view, refreshTransientBuildOnPSS); |
| context.getAttributes().remove(MARK_INITIAL_STATE_KEY); |
| } |
| context.getAttributes().remove(StateManager.IS_BUILDING_INITIAL_STATE); |
| context.getAttributes().remove(IS_BUILDING_INITIAL_STATE_KEY_ALIAS); |
| } |
| |
| // We need to suscribe the listeners of changes in the component tree |
| // only the first time here. Later we suscribe this listeners on |
| // DefaultFaceletsStateManagement.restoreView, to ensure |
| // relocated components are not retrieved later on getClientIdsRemoved(). |
| if (!(refreshTransientBuild && PhaseId.RESTORE_VIEW.equals(context.getCurrentPhaseId())) |
| && !view.isTransient()) |
| { |
| ((DefaultFaceletsStateManagementStrategy) getStateManagementStrategy(context, view.getViewId())). |
| suscribeListeners(view); |
| } |
| |
| context.getAttributes().remove(USING_PSS_ON_THIS_VIEW); |
| } |
| |
| // Remove this var from faces context because this one prevent AjaxHandler |
| // register the standard script library on Post-Redirect-Get pattern or |
| // in the next view |
| context.getAttributes().remove(AjaxHandler.STANDARD_JSF_AJAX_LIBRARY_LOADED); |
| } |
| |
| private void _markInitialStateOnView(final UIViewRoot view, final boolean refreshTransientBuildOnPSS) |
| { |
| if (!refreshTransientBuildOnPSS || |
| !view.getAttributes().containsKey(COMPONENT_ADDED_AFTER_BUILD_VIEW)) |
| { |
| if (!view.isTransient()) |
| { |
| view.markInitialState(); |
| } |
| } |
| |
| int childCount = view.getChildCount(); |
| if (childCount > 0) |
| { |
| for (int i = 0; i < childCount; i++) |
| { |
| UIComponent child = view.getChildren().get(i); |
| if (!child.isTransient()) |
| { |
| _markInitialState(child); |
| } |
| } |
| } |
| if (view.getFacetCount() > 0) |
| { |
| Map<String, UIComponent> facetMap = view.getFacets(); |
| for (Map.Entry<String, UIComponent> entry : facetMap.entrySet()) |
| { |
| UIComponent child = entry.getValue(); |
| if (!child.isTransient()) |
| { |
| _markInitialState(child); |
| } |
| } |
| |
| } |
| } |
| |
| private void _markInitialState(final UIComponent component) |
| { |
| component.markInitialState(); |
| |
| final int childCount = component.getChildCount(); |
| if (childCount > 0) |
| { |
| for (int i = 0; i < childCount; i++) |
| { |
| UIComponent child = component.getChildren().get(i); |
| if (!child.isTransient()) |
| { |
| _markInitialState(child); |
| } |
| } |
| } |
| if (component.getFacetCount() > 0) |
| { |
| Map<String, UIComponent> facetMap = component.getFacets(); |
| for (Map.Entry<String, UIComponent> entry : facetMap.entrySet()) |
| { |
| UIComponent child = entry.getValue(); |
| if (!child.isTransient()) |
| { |
| _markInitialState(child); |
| } |
| } |
| |
| } |
| } |
| |
| private boolean isFilledView(FacesContext context, UIViewRoot view) |
| { |
| // The view is only built on restoreView or renderView, but if |
| // we are not using partial state saving, we need to mark the current |
| // view as filled, otherwise it will be filled again on renderView. |
| return context.getAttributes().containsKey(view); |
| // -= Leonardo Uribe =- save this key on view cause render fail, because the view |
| // is built before render view to "restore" the transient components that has |
| // facelet markup (facelets UIInstructions ...) This effect is only notice when |
| // partial state saving is not used. |
| //return view.getAttributes().containsKey(FILLED_VIEW); |
| } |
| |
| private void setFilledView(FacesContext context, UIViewRoot view) |
| { |
| context.getAttributes().put(view, Boolean.TRUE); |
| // -= Leonardo Uribe =- save this key on view cause render fail, because the view |
| // is built before render view to "restore" the transient components that has |
| // facelet markup (facelets UIInstructions ...) This effect is only notice when |
| // partial state saving is not used. |
| // view.getAttributes().put(FILLED_VIEW, Boolean.TRUE); |
| } |
| |
| /** |
| * retargetMethodExpressions(FacesContext, UIComponent) has some clues about the behavior of this method |
| * |
| * {@inheritDoc} |
| */ |
| @Override |
| public BeanInfo getComponentMetadata(FacesContext context, Resource componentResource) |
| { |
| BeanInfo beanInfo = null; |
| |
| checkNull(context, "context"); |
| |
| try |
| { |
| Facelet compositeComponentFacelet; |
| FaceletFactory.setInstance(faceletFactory); |
| try |
| { |
| compositeComponentFacelet |
| = faceletFactory.getCompositeComponentMetadataFacelet(componentResource.getURL()); |
| } |
| finally |
| { |
| FaceletFactory.setInstance(null); |
| } |
| //context.getAttributes().put(BUILDING_COMPOSITE_COMPONENT_METADATA, Boolean.TRUE); |
| |
| // Create a temporal tree where all components will be put, but we are only |
| // interested in metadata. |
| UINamingContainer compositeComponentBase |
| = (UINamingContainer) context.getApplication().createComponent( |
| context, UINamingContainer.COMPONENT_TYPE, null); |
| |
| // Fill the component resource key, because this information should be available |
| // on metadata to recognize which is the component used as composite component base. |
| // Since this method is called from Application.createComponent(FacesContext,Resource), |
| // and in that specific method this key is updated, this is the best option we |
| // have for recognize it (also this key is used by UIComponent.isCompositeComponent) |
| compositeComponentBase.getAttributes().put(Resource.COMPONENT_RESOURCE_KEY, componentResource); |
| |
| // According to UserTagHandler, in this point we need to wrap the facelet |
| // VariableMapper, so local changes are applied on "page context", but |
| // data is retrieved from full context |
| FaceletContext faceletContext = (FaceletContext) context. |
| getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY); |
| VariableMapper orig = faceletContext.getVariableMapper(); |
| try |
| { |
| faceletContext.setVariableMapper(new VariableMapperWrapper(orig)); |
| |
| compositeComponentBase.pushComponentToEL(context, compositeComponentBase); |
| |
| compositeComponentFacelet.apply(context, compositeComponentBase); |
| |
| compositeComponentBase.popComponentFromEL(context); |
| } |
| finally |
| { |
| faceletContext.setVariableMapper(orig); |
| } |
| |
| beanInfo = (BeanInfo) compositeComponentBase.getAttributes().get(UIComponent.BEANINFO_KEY); |
| } |
| catch (IOException e) |
| { |
| throw new FacesException(e); |
| } |
| |
| return beanInfo; |
| } |
| |
| |
| /** |
| * Check if the current facelet applied is used to build view metadata. |
| * |
| * @param context |
| * @return |
| */ |
| public static boolean isBuildingViewMetadata(FacesContext context) |
| { |
| return context.getAttributes().containsKey(BUILDING_VIEW_METADATA); |
| } |
| |
| public static boolean isRefreshingTransientBuild(FacesContext context) |
| { |
| return context.getAttributes().containsKey(REFRESHING_TRANSIENT_BUILD); |
| } |
| |
| public static boolean isRemovingComponentBuild(FacesContext context) |
| { |
| return context.getAttributes().containsKey(REMOVING_COMPONENTS_BUILD); |
| } |
| |
| public static boolean isMarkInitialState(FacesContext context) |
| { |
| return Boolean.TRUE.equals(context.getAttributes().get(MARK_INITIAL_STATE_KEY)); |
| } |
| |
| public static boolean isRefreshTransientBuildOnPSS(FacesContext context) |
| { |
| //this include both "true" and "auto" |
| return context.getAttributes().containsKey(REFRESH_TRANSIENT_BUILD_ON_PSS); |
| } |
| |
| public static boolean isRefreshTransientBuildOnPSSAuto(FacesContext context) |
| { |
| return "auto".equalsIgnoreCase((String) context.getAttributes().get(REFRESH_TRANSIENT_BUILD_ON_PSS)); |
| } |
| |
| public static boolean isCleanTransientBuildOnRestore(FacesContext context) |
| { |
| return context.getAttributes().containsKey(CLEAN_TRANSIENT_BUILD_ON_RESTORE); |
| } |
| |
| public static void cleanTransientBuildOnRestore(FacesContext context) |
| { |
| context.getAttributes().put(CLEAN_TRANSIENT_BUILD_ON_RESTORE, Boolean.TRUE); |
| } |
| |
| public static boolean isUsingPSSOnThisView(FacesContext context) |
| { |
| return context.getAttributes().containsKey(USING_PSS_ON_THIS_VIEW); |
| } |
| |
| /** |
| * In short words, this method take care of "target" an "attached object". |
| * <ul> |
| * <li>The "attached object" is instantiated by a tag handler.</li> |
| * <li>The "target" is an object used as "marker", that exposes a List<UIComponent></li> |
| * </ul> |
| * This method should be called from some composite component tag handler, after |
| * all children of composite component has been applied. |
| */ |
| @Override |
| @SuppressWarnings("unchecked") |
| public void retargetAttachedObjects(FacesContext context, |
| UIComponent topLevelComponent, List<AttachedObjectHandler> handlerList) |
| { |
| checkNull(context, "context"); |
| checkNull(topLevelComponent, "topLevelComponent"); |
| checkNull(handlerList, "handlerList"); |
| |
| BeanInfo compositeComponentMetadata |
| = (BeanInfo) topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY); |
| |
| if (compositeComponentMetadata == null) |
| { |
| log.severe("Composite component metadata not found for: " + topLevelComponent.getClientId(context)); |
| return; |
| } |
| |
| BeanDescriptor compositeComponentDescriptor = compositeComponentMetadata.getBeanDescriptor(); |
| |
| List<AttachedObjectTarget> targetList = (List<AttachedObjectTarget>) |
| compositeComponentDescriptor.getValue(AttachedObjectTarget.ATTACHED_OBJECT_TARGETS_KEY); |
| |
| if (targetList == null || targetList.isEmpty()) |
| { |
| return; |
| } |
| |
| for (int i = 0, size = handlerList.size(); i < size; i++) |
| { |
| AttachedObjectHandler currentHandler = handlerList.get(i); |
| // In the spec javadoc this variable is referred as forAttributeValue, but |
| // note it is also called curTargetName |
| String forValue = currentHandler.getFor(); |
| |
| // perf: targetList is always arrayList: see AttachedObjectTargetHandler.apply |
| // and ClientBehaviorHandler.apply |
| for (int k = 0, targetsSize = targetList.size(); k < targetsSize; k++) |
| { |
| AttachedObjectTarget currentTarget = targetList.get(k); |
| FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance(); |
| |
| if ((forValue != null && forValue.equals(currentTarget.getName())) && |
| ((currentTarget instanceof ActionSource2AttachedObjectTarget && |
| currentHandler instanceof ActionSource2AttachedObjectHandler) || |
| (currentTarget instanceof EditableValueHolderAttachedObjectTarget && |
| currentHandler instanceof EditableValueHolderAttachedObjectHandler) || |
| (currentTarget instanceof ValueHolderAttachedObjectTarget && |
| currentHandler instanceof ValueHolderAttachedObjectHandler))) |
| { |
| // perf: getTargets return ArrayList - see getTargets implementations |
| List<UIComponent> targets = currentTarget.getTargets(topLevelComponent); |
| for (int l = 0, targetsCount = targets.size(); l < targetsCount; l++) |
| { |
| UIComponent component = targets.get(l); |
| // If we found composite components when traverse the tree |
| // we have to call this one recursively, because each composite component |
| // should have its own AttachedObjectHandler list, filled earlier when |
| // its tag handler is applied. |
| if (UIComponent.isCompositeComponent(component)) |
| { |
| // How we obtain the list of AttachedObjectHandler for |
| // the current composite component? It should be a component |
| // attribute or retrieved by a key inside component.getAttributes |
| // map. Since api does not specify any attribute, we suppose |
| // this is an implementation detail and it should be retrieved |
| // from component attribute map. |
| // But this is only the point of the iceberg, because we should |
| // define how we register attached object handlers in this list. |
| // ANS: see CompositeComponentResourceTagHandler. |
| // The current handler should be added to the list, to be chained. |
| // Note that the inner component should have a target with the same name |
| // as "for" attribute |
| mctx.addAttachedObjectHandler(component, currentHandler); |
| |
| List<AttachedObjectHandler> handlers = mctx.getAttachedObjectHandlers(component); |
| |
| retargetAttachedObjects(context, component, handlers); |
| |
| handlers.remove(currentHandler); |
| } |
| else |
| { |
| currentHandler.applyAttachedObject(context, component); |
| } |
| if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState()) |
| { |
| component.markInitialState(); |
| } |
| } |
| } |
| else if ((currentTarget instanceof BehaviorHolderAttachedObjectTarget && |
| currentHandler instanceof BehaviorHolderAttachedObjectHandler)) |
| { |
| String eventName = ((BehaviorHolderAttachedObjectHandler) currentHandler).getEventName(); |
| boolean isDefaultEvent = ((BehaviorHolderAttachedObjectTarget) currentTarget).isDefaultEvent(); |
| |
| if ((eventName != null && eventName.equals(currentTarget.getName())) || |
| (eventName == null && isDefaultEvent)) |
| { |
| List<UIComponent> targets = currentTarget.getTargets(topLevelComponent); |
| for (int j = 0, targetssize = targets.size(); j < targetssize; j++) |
| { |
| UIComponent component = targets.get(j); |
| // If we found composite components when traverse the tree |
| // we have to call this one recursively, because each composite component |
| // should have its own AttachedObjectHandler list, filled earlier when |
| // its tag handler is applied. |
| if (UIComponent.isCompositeComponent(component)) |
| { |
| if (currentTarget instanceof ClientBehaviorAttachedObjectTarget) |
| { |
| mctx.addAttachedObjectHandler(component, |
| new ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper( |
| (BehaviorHolderAttachedObjectHandler) currentHandler, |
| ((ClientBehaviorAttachedObjectTarget) currentTarget).getEvent())); |
| } |
| else |
| { |
| mctx.addAttachedObjectHandler(component, currentHandler); |
| } |
| |
| List<AttachedObjectHandler> handlers = mctx.getAttachedObjectHandlers(component); |
| |
| retargetAttachedObjects(context, component, handlers); |
| |
| handlers.remove(currentHandler); |
| } |
| else |
| { |
| if (currentHandler instanceof |
| ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper) |
| { |
| currentHandler.applyAttachedObject(context, |
| new ClientBehaviorRedirectEventComponentWrapper(component, |
| ((ClientBehaviorRedirectBehaviorAttachedObjectHandlerWrapper) |
| currentHandler).getWrappedEventName(), eventName)); |
| } |
| else |
| { |
| currentHandler.applyAttachedObject(context, component); |
| } |
| } |
| if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState()) |
| { |
| component.markInitialState(); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void retargetMethodExpressions(FacesContext context, UIComponent topLevelComponent) |
| { |
| checkNull(context, "context"); |
| |
| BeanInfo compositeComponentMetadata |
| = (BeanInfo) topLevelComponent.getAttributes().get(UIComponent.BEANINFO_KEY); |
| |
| if (compositeComponentMetadata == null) |
| { |
| log.severe("Composite component metadata not found for: " + topLevelComponent.getClientId(context)); |
| return; |
| } |
| |
| // "...For each attribute that is a MethodExpression..." This means we have to scan |
| // all attributes with "method-signature" attribute and no "type" attribute |
| // javax.faces.component._ComponentAttributesMap uses BeanInfo.getPropertyDescriptors to |
| // traverse over it, but here the metadata returned by UIComponent.BEANINFO_KEY is available |
| // only for composite components. |
| // That means somewhere we need to create a custom BeanInfo object for composite components |
| // that will be filled somewhere (theorically in ViewDeclarationLanguage.getComponentMetadata()) |
| |
| PropertyDescriptor[] propertyDescriptors = compositeComponentMetadata.getPropertyDescriptors(); |
| |
| ELContext elContext = (ELContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY); |
| |
| for (PropertyDescriptor propertyDescriptor : propertyDescriptors) |
| { |
| if (propertyDescriptor.getValue("type") != null) |
| { |
| // This check is necessary if we have both "type" and "method-signature" set. |
| // In that case, "method-signature" is ignored |
| continue; |
| } |
| |
| String attributeName = propertyDescriptor.getName(); |
| |
| // <composite:attribute> method-signature attribute is |
| // ValueExpression that must evaluate to String |
| ValueExpression methodSignatureExpression |
| = (ValueExpression) propertyDescriptor.getValue("method-signature"); |
| String methodSignature = null; |
| if (methodSignatureExpression != null) |
| { |
| // Check if the value expression holds a method signature |
| // Note that it could be null, so in that case we don't have to do anything |
| methodSignature = (String) methodSignatureExpression.getValue(elContext); |
| } |
| |
| String targetAttributeName = null; |
| ValueExpression targetAttributeNameVE |
| = (ValueExpression) propertyDescriptor.getValue("targetAttributeName"); |
| if (targetAttributeNameVE != null) |
| { |
| targetAttributeName = (String) targetAttributeNameVE.getValue(context.getELContext()); |
| if (targetAttributeName == null) |
| { |
| targetAttributeName = attributeName; |
| } |
| } |
| else |
| { |
| targetAttributeName = attributeName; |
| } |
| |
| boolean isKnownTargetAttributeMethod |
| = "action".equals(targetAttributeName) || "actionListener".equals(targetAttributeName) |
| || "validator".equals(targetAttributeName) || "valueChangeListener".equals(targetAttributeName); |
| |
| // either the attributeName has to be a knownMethod or there has to be a method-signature |
| if (isKnownTargetAttributeMethod || methodSignature != null) |
| { |
| ValueExpression targetsExpression = |
| (ValueExpression) propertyDescriptor.getValue("targets"); |
| |
| String targets = null; |
| // <composite:attribute> targets attribute is |
| // ValueExpression that must evaluate to String |
| if (targetsExpression != null) |
| { |
| targets = (String) targetsExpression.getValue(elContext); |
| } |
| |
| if (targets == null) |
| { |
| // "...let the name of the metadata element be the |
| // evaluated value of the targets attribute..." |
| targets = attributeName; |
| } |
| |
| FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance(); |
| |
| // If the MethodExpression attribute has been already applied, there is no need to |
| // handle it and it is probably a MethodExpression instance is on attribute map, so the |
| // inner code will cause a ClassCastException. |
| if (!mctx.isMethodExpressionAttributeApplied(topLevelComponent, attributeName)) |
| { |
| ValueExpression attributeNameValueExpression = |
| (ValueExpression) topLevelComponent.getAttributes().get(attributeName); |
| |
| if (attributeNameValueExpression == null) |
| { |
| // composite:attribute has a default property, so if we can't found on the |
| // component attribute map, we should get the default as CompositeComponentELResolver |
| // does. |
| attributeNameValueExpression = (ValueExpression) propertyDescriptor.getValue("default"); |
| if (attributeNameValueExpression == null) |
| { |
| // It is only valid to log an error if the attribute is required |
| ValueExpression ve = (ValueExpression) propertyDescriptor.getValue("required"); |
| if (ve != null) |
| { |
| Object requiredValue = ve.getValue(elContext); |
| Boolean required; |
| if (requiredValue instanceof Boolean) |
| { |
| required = (Boolean) requiredValue; |
| } |
| else |
| { |
| required = Boolean.valueOf(requiredValue.toString()); |
| } |
| |
| if (required != null && required) |
| { |
| if (log.isLoggable(Level.SEVERE)) |
| { |
| log.severe("attributeValueExpression not found under the key \"" |
| + attributeName |
| + "\". Looking for the next attribute"); |
| } |
| } |
| } |
| continue; |
| } |
| } |
| |
| String[] targetsArray = StringUtils.splitShortString(targets, ' '); |
| String attributeExpressionString = attributeNameValueExpression.getExpressionString(); |
| |
| //Check if the stored valueExpression is a ccRedirection, to handle it properly later. |
| boolean ccAttrMeRedirection = |
| attributeNameValueExpression instanceof LocationValueExpression && |
| CompositeComponentELUtils.isCompositeComponentAttrsMethodExpression( |
| attributeNameValueExpression.getExpressionString()); |
| |
| if (isKnownTargetAttributeMethod) |
| { |
| // To add support to #{cc.attrs.action}, #{cc.attrs.actionListener}, #{cc.attrs.validator} or |
| // #{cc.attrs.valueChangeListener} it is necessary to put a MethodExpression or a |
| // ValueExpression pointing to the associated java method in the component attribute map. |
| // org.apache.myfaces.view.facelets.tag.composite.RetargetMethodExpressionRule already put |
| // a ValueExpression, so we only need to put a MethodExpression when a non redirecting |
| // expression is used (for example when a nested #{cc.attrs.xxx} is used). |
| if ("action".equals(targetAttributeName)) |
| { |
| applyActionMethodExpressionEL(context, elContext, |
| topLevelComponent, attributeName, |
| attributeExpressionString, attributeNameValueExpression, |
| ccAttrMeRedirection); |
| } |
| else if ("actionListener".equals(targetAttributeName)) |
| { |
| applyActionListenerMethodExpressionEL(context, elContext, |
| topLevelComponent, attributeName, |
| attributeExpressionString, attributeNameValueExpression, |
| ccAttrMeRedirection); |
| } |
| else if ("validator".equals(targetAttributeName)) |
| { |
| applyValidatorMethodExpressionEL(context, elContext, |
| topLevelComponent, attributeName, |
| attributeExpressionString, attributeNameValueExpression, |
| ccAttrMeRedirection); |
| } |
| else if ("valueChangeListener".equals(targetAttributeName)) |
| { |
| applyValueChangeListenerMethodExpressionEL(context, elContext, |
| topLevelComponent, attributeName, |
| attributeExpressionString, attributeNameValueExpression, |
| ccAttrMeRedirection); |
| } |
| |
| UIComponent topLevelComponentBase = |
| topLevelComponent.getFacet(UIComponent.COMPOSITE_FACET_NAME); |
| |
| for (String target : targetsArray) |
| { |
| UIComponent innerComponent |
| = ComponentSupport.findComponentChildOrFacetFrom(context, topLevelComponentBase, |
| target); |
| |
| if (innerComponent == null) |
| { |
| continue; |
| } |
| |
| if (isCompositeComponentRetarget(context, innerComponent, targetAttributeName)) |
| { |
| innerComponent.getAttributes().put(targetAttributeName, attributeNameValueExpression); |
| |
| mctx.clearMethodExpressionAttribute(innerComponent, targetAttributeName); |
| |
| retargetMethodExpressions(context, innerComponent); |
| if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState()) |
| { |
| innerComponent.markInitialState(); |
| } |
| } |
| else |
| { |
| if ("action".equals(targetAttributeName)) |
| { |
| applyActionMethodExpressionTarget(context, mctx, elContext, |
| topLevelComponentBase, innerComponent, |
| attributeName, targetAttributeName, |
| attributeExpressionString, attributeNameValueExpression, |
| ccAttrMeRedirection); |
| if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState()) |
| { |
| innerComponent.markInitialState(); |
| } |
| } |
| else if ("actionListener".equals(targetAttributeName)) |
| { |
| applyActionListenerMethodExpressionTarget(context, mctx, elContext, |
| topLevelComponentBase, innerComponent, |
| attributeName, targetAttributeName, |
| attributeExpressionString, attributeNameValueExpression, |
| ccAttrMeRedirection); |
| if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState()) |
| { |
| innerComponent.markInitialState(); |
| } |
| } |
| else if ("validator".equals(targetAttributeName)) |
| { |
| applyValidatorMethodExpressionTarget(context, mctx, elContext, |
| topLevelComponentBase, innerComponent, |
| attributeName, targetAttributeName, |
| attributeExpressionString, attributeNameValueExpression, |
| ccAttrMeRedirection); |
| if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState()) |
| { |
| innerComponent.markInitialState(); |
| } |
| } |
| else if ("valueChangeListener".equals(targetAttributeName)) |
| { |
| applyValueChangeListenerMethodExpressionTarget(context, mctx, elContext, |
| topLevelComponentBase, innerComponent, |
| attributeName, targetAttributeName, |
| attributeExpressionString, attributeNameValueExpression, |
| ccAttrMeRedirection); |
| if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState()) |
| { |
| innerComponent.markInitialState(); |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| MethodExpression methodExpression = null; |
| // composite:attribute targets property only has sense for action, actionListener, |
| // validator or valueChangeListener. This means we have to retarget the method expression |
| // to the topLevelComponent. |
| |
| // Since a MethodExpression has no state, we can use it multiple times without problem, so |
| // first create it here. |
| methodSignature = methodSignature.trim(); |
| methodExpression = context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, |
| FaceletsViewDeclarationLanguageUtils.getReturnType(methodSignature), |
| FaceletsViewDeclarationLanguageUtils.getParameters(methodSignature)); |
| |
| methodExpression = reWrapMethodExpression(methodExpression, attributeNameValueExpression); |
| |
| applyMethodExpression(context, mctx, topLevelComponent, attributeName, |
| targetAttributeName, attributeNameValueExpression, methodExpression, |
| ccAttrMeRedirection, targetsArray); |
| } |
| mctx.markMethodExpressionAttribute(topLevelComponent, attributeName); |
| } |
| |
| // We need to remove the previous ValueExpression, to prevent some possible |
| // confusion when the same value is retrieved from the attribute map. |
| topLevelComponent.setValueExpression(attributeName, null); |
| } |
| } |
| } |
| |
| private void applyActionMethodExpressionEL(FacesContext context, |
| ELContext elContext, |
| UIComponent topLevelComponent, |
| String attributeName, |
| String attributeExpressionString, |
| ValueExpression attributeNameValueExpression, |
| boolean ccAttrMeRedirection) |
| { |
| // target is ActionSource2 |
| MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, null, |
| EMPTY_CLASS_ARRAY), attributeNameValueExpression); |
| |
| //Store the method expression to the topLevelComponent to allow reference it through EL |
| if (!ccAttrMeRedirection) |
| { |
| //Replace it with a method expression |
| topLevelComponent.getAttributes().put(attributeName, methodExpression); |
| } |
| // Otherwise keep the current ValueExpression, |
| // because it will be used chain other value expressions |
| } |
| |
| private void applyActionListenerMethodExpressionEL(FacesContext context, |
| ELContext elContext, |
| UIComponent topLevelComponent, |
| String attributeName, |
| String attributeExpressionString, |
| ValueExpression attributeNameValueExpression, |
| boolean ccAttrMeRedirection) |
| { |
| // target is ActionSource2 |
| MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, Void.TYPE, ACTION_LISTENER_SIGNATURE), |
| attributeNameValueExpression); |
| |
| MethodExpression methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, Void.TYPE, EMPTY_CLASS_ARRAY), |
| attributeNameValueExpression); |
| |
| //Store the method expression to the topLevelComponent to allow reference it through EL |
| if (!ccAttrMeRedirection) |
| { |
| //Replace it with a method expression |
| topLevelComponent.getAttributes().put(attributeName, |
| new MethodExpressionMethodExpression(methodExpression, methodExpression2)); |
| } |
| // Otherwise keep the current ValueExpression, |
| // because it will be used chain other value expressions |
| } |
| |
| private void applyValidatorMethodExpressionEL(FacesContext context, |
| ELContext elContext, |
| UIComponent topLevelComponent, |
| String attributeName, |
| String attributeExpressionString, |
| ValueExpression attributeNameValueExpression, |
| boolean ccAttrMeRedirection) |
| { |
| // target is EditableValueHolder |
| MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, Void.TYPE, |
| VALIDATOR_SIGNATURE), attributeNameValueExpression); |
| |
| //Store the method expression to the topLevelComponent to allow reference it through EL |
| if (!ccAttrMeRedirection) |
| { |
| //Replace it with a method expression |
| topLevelComponent.getAttributes().put(attributeName, methodExpression); |
| } |
| // Otherwise keep the current ValueExpression, |
| // because it will be used chain other value expressions |
| } |
| |
| private void applyValueChangeListenerMethodExpressionEL(FacesContext context, |
| ELContext elContext, |
| UIComponent topLevelComponent, |
| String attributeName, |
| String attributeExpressionString, |
| ValueExpression attributeNameValueExpression, |
| boolean ccAttrMeRedirection) |
| { |
| // target is EditableValueHolder |
| MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, Void.TYPE, |
| VALUE_CHANGE_LISTENER_SIGNATURE), attributeNameValueExpression); |
| |
| MethodExpression methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, Void.TYPE, |
| EMPTY_CLASS_ARRAY), attributeNameValueExpression); |
| |
| //Store the method expression to the topLevelComponent to allow reference it through EL |
| if (!ccAttrMeRedirection) |
| { |
| //Replace it with a method expression |
| topLevelComponent.getAttributes().put(attributeName, |
| new MethodExpressionMethodExpression(methodExpression, methodExpression2)); |
| } |
| // Otherwise keep the current ValueExpression, because it will be used chain other value expressions |
| } |
| |
| private void applyActionMethodExpressionTarget(FacesContext context, FaceletCompositionContext mctx, |
| ELContext elContext, |
| UIComponent topLevelComponent, |
| UIComponent innerComponent, |
| String attributeName, |
| String targetAttributeName, |
| String attributeExpressionString, |
| ValueExpression attributeNameValueExpression, |
| boolean ccAttrMeRedirection) |
| { |
| // target is ActionSource2 |
| MethodExpression methodExpression |
| = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, null, EMPTY_CLASS_ARRAY), |
| attributeNameValueExpression); |
| |
| // If it is a redirection, a wrapper is used to |
| // locate the right instance and call it properly. |
| if (ccAttrMeRedirection) |
| { |
| ((ActionSource2) innerComponent).setActionExpression( |
| new ValueExpressionMethodExpression(attributeNameValueExpression)); |
| } |
| else |
| { |
| ((ActionSource2) innerComponent).setActionExpression(methodExpression); |
| } |
| } |
| |
| private void applyActionListenerMethodExpressionTarget(FacesContext context, FaceletCompositionContext mctx, |
| ELContext elContext, |
| UIComponent topLevelComponent, |
| UIComponent innerComponent, |
| String attributeName, |
| String targetAttributeName, |
| String attributeExpressionString, |
| ValueExpression attributeNameValueExpression, |
| boolean ccAttrMeRedirection) |
| { |
| //First try to remove any prevous target if any |
| ActionListener o = (ActionListener) |
| mctx.removeMethodExpressionTargeted(innerComponent, targetAttributeName); |
| if (o != null) |
| { |
| ((ActionSource2) innerComponent).removeActionListener(o); |
| } |
| |
| // target is ActionSource2 |
| ActionListener actionListener = null; |
| // If it is a redirection, a wrapper is used to locate the right instance and call |
| //it properly. |
| if (ccAttrMeRedirection) |
| { |
| actionListener = new RedirectMethodExpressionValueExpressionActionListener( |
| attributeNameValueExpression); |
| } |
| else |
| { |
| MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, Void.TYPE, ACTION_LISTENER_SIGNATURE), attributeNameValueExpression); |
| |
| MethodExpression methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, Void.TYPE, EMPTY_CLASS_ARRAY), attributeNameValueExpression); |
| |
| if (mctx.isUsingPSSOnThisView()) |
| { |
| actionListener = new PartialMethodExpressionActionListener(methodExpression, methodExpression2); |
| } |
| else |
| { |
| actionListener = new MethodExpressionActionListener(methodExpression, methodExpression2); |
| } |
| } |
| ((ActionSource2) innerComponent).addActionListener(actionListener); |
| mctx.addMethodExpressionTargeted(innerComponent, targetAttributeName, actionListener); |
| } |
| |
| private void applyValidatorMethodExpressionTarget(FacesContext context, FaceletCompositionContext mctx, |
| ELContext elContext, |
| UIComponent topLevelComponent, |
| UIComponent innerComponent, |
| String attributeName, |
| String targetAttributeName, |
| String attributeExpressionString, |
| ValueExpression attributeNameValueExpression, |
| boolean ccAttrMeRedirection) |
| { |
| //First try to remove any prevous target if any |
| Validator o = (Validator) mctx.removeMethodExpressionTargeted(innerComponent, targetAttributeName); |
| if (o != null) |
| { |
| ((EditableValueHolder) innerComponent).removeValidator(o); |
| } |
| |
| // target is EditableValueHolder |
| Validator validator = null; |
| // If it is a redirection, a wrapper is used to locate the right instance and call it properly. |
| if (ccAttrMeRedirection) |
| { |
| validator = new RedirectMethodExpressionValueExpressionValidator(attributeNameValueExpression); |
| } |
| else |
| { |
| MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, Void.TYPE, |
| VALIDATOR_SIGNATURE), attributeNameValueExpression); |
| |
| if (mctx.isUsingPSSOnThisView()) |
| { |
| validator = new PartialMethodExpressionValidator(methodExpression); |
| } |
| else |
| { |
| validator = new MethodExpressionValidator(methodExpression); |
| } |
| } |
| ((EditableValueHolder) innerComponent).addValidator(validator); |
| mctx.addMethodExpressionTargeted(innerComponent, targetAttributeName, validator); |
| } |
| |
| private void applyValueChangeListenerMethodExpressionTarget(FacesContext context, FaceletCompositionContext mctx, |
| ELContext elContext, |
| UIComponent topLevelComponent, |
| UIComponent innerComponent, |
| String attributeName, |
| String targetAttributeName, |
| String attributeExpressionString, |
| ValueExpression attributeNameValueExpression, |
| boolean ccAttrMeRedirection) |
| { |
| ValueChangeListener o = (ValueChangeListener) mctx.removeMethodExpressionTargeted( |
| innerComponent, targetAttributeName); |
| if (o != null) |
| { |
| ((EditableValueHolder) innerComponent).removeValueChangeListener(o); |
| } |
| |
| // target is EditableValueHolder |
| ValueChangeListener valueChangeListener = null; |
| // If it is a redirection, a wrapper is used to locate the right instance and call it properly. |
| if (ccAttrMeRedirection) |
| { |
| valueChangeListener = new RedirectMethodExpressionValueExpressionValueChangeListener( |
| attributeNameValueExpression); |
| } |
| else |
| { |
| MethodExpression methodExpression = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, Void.TYPE, |
| VALUE_CHANGE_LISTENER_SIGNATURE), attributeNameValueExpression); |
| |
| MethodExpression methodExpression2 = reWrapMethodExpression(context.getApplication().getExpressionFactory(). |
| createMethodExpression(elContext, |
| attributeExpressionString, Void.TYPE, |
| EMPTY_CLASS_ARRAY), attributeNameValueExpression); |
| |
| if (mctx.isUsingPSSOnThisView()) |
| { |
| valueChangeListener = new PartialMethodExpressionValueChangeListener(methodExpression, |
| methodExpression2); |
| } |
| else |
| { |
| valueChangeListener = new MethodExpressionValueChangeListener(methodExpression, methodExpression2); |
| } |
| } |
| ((EditableValueHolder) innerComponent).addValueChangeListener(valueChangeListener); |
| mctx.addMethodExpressionTargeted(innerComponent, targetAttributeName, valueChangeListener); |
| } |
| |
| private void applyMethodExpression(FacesContext context, FaceletCompositionContext mctx, |
| UIComponent topLevelComponent, |
| String attributeName, |
| String targetAttributeName, |
| ValueExpression attributeNameValueExpression, |
| MethodExpression methodExpression, |
| boolean ccAttrMeRedirection, |
| String[] targetsArray) |
| { |
| UIComponent topLevelComponentBase = topLevelComponent.getFacet(UIComponent.COMPOSITE_FACET_NAME); |
| |
| for (String target : targetsArray) |
| { |
| UIComponent innerComponent = ComponentSupport.findComponentChildOrFacetFrom(context, |
| topLevelComponentBase, target); |
| if (innerComponent == null) |
| { |
| continue; |
| } |
| |
| // If a component is found, that means the expression should be retarget to the |
| // components related |
| if (isCompositeComponentRetarget(context, innerComponent, targetAttributeName)) |
| { |
| innerComponent.getAttributes().put(targetAttributeName, attributeNameValueExpression); |
| |
| mctx.clearMethodExpressionAttribute(innerComponent, targetAttributeName); |
| |
| retargetMethodExpressions(context, innerComponent); |
| |
| if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState()) |
| { |
| //retargetMethodExpression occur on build view time, so it is safe to call markInitiaState here |
| innerComponent.markInitialState(); |
| } |
| } |
| else |
| { |
| //Put the retarget |
| if (ccAttrMeRedirection) |
| { |
| // Since we require here a method expression, it is necessary to wrap |
| // the ValueExpression into a MethodExpression that handles redirection. |
| innerComponent.getAttributes().put(targetAttributeName, |
| new ValueExpressionMethodExpression(attributeNameValueExpression)); |
| } |
| else |
| { |
| innerComponent.getAttributes().put(targetAttributeName, methodExpression); |
| } |
| if (mctx.isUsingPSSOnThisView() && mctx.isMarkInitialState()) |
| { |
| innerComponent.markInitialState(); |
| } |
| } |
| } |
| //Store the method expression to the topLevelComponent to allow reference it through EL |
| if (!ccAttrMeRedirection) |
| { |
| //Replace it with a method expression |
| topLevelComponent.getAttributes().put(attributeName, methodExpression); |
| } |
| // Othewise keep the current ValueExpression, because it will be used chain other value |
| // expressions |
| } |
| |
| |
| private boolean isCompositeComponentRetarget(FacesContext context, UIComponent component, String attributeName) |
| { |
| if (UIComponent.isCompositeComponent(component)) |
| { |
| BeanInfo compositeComponentMetadata = (BeanInfo) component.getAttributes().get(UIComponent.BEANINFO_KEY); |
| |
| PropertyDescriptor[] propertyDescriptors = compositeComponentMetadata.getPropertyDescriptors(); |
| |
| ELContext elContext = (ELContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY); |
| |
| for (PropertyDescriptor propertyDescriptor : propertyDescriptors) |
| { |
| if (propertyDescriptor.getValue("type") != null) |
| { |
| // This check is necessary if we have both "type" and "method-signature" set. |
| // In that case, "method-signature" is ignored |
| continue; |
| } |
| |
| if (attributeName.equals(propertyDescriptor.getName())) |
| { |
| // <composite:attribute> method-signature attribute is |
| // ValueExpression that must evaluate to String |
| ValueExpression methodSignatureExpression |
| = (ValueExpression) propertyDescriptor.getValue("method-signature"); |
| String methodSignature = null; |
| if (methodSignatureExpression != null) |
| { |
| // Check if the value expression holds a method signature |
| // Note that it could be null, so in that case we don't have to do anything |
| methodSignature = (String) methodSignatureExpression.getValue(elContext); |
| } |
| |
| String targetAttributeName = null; |
| ValueExpression targetAttributeNameVE = (ValueExpression) |
| propertyDescriptor.getValue("targetAttributeName"); |
| if (targetAttributeNameVE != null) |
| { |
| targetAttributeName = (String) targetAttributeNameVE.getValue(context.getELContext()); |
| if (targetAttributeName == null) |
| { |
| targetAttributeName = attributeName; |
| } |
| } |
| else |
| { |
| targetAttributeName = attributeName; |
| } |
| |
| boolean isKnownTargetAttributeMethod = "action".equals(targetAttributeName) |
| || "actionListener".equals(targetAttributeName) |
| || "validator".equals(targetAttributeName) |
| || "valueChangeListener".equals(targetAttributeName); |
| |
| // either the attributeName has to be a knownMethod or there has to be a method-signature |
| if (isKnownTargetAttributeMethod || methodSignature != null) |
| { |
| if ("action".equals(targetAttributeName)) |
| { |
| return !(component instanceof ActionSource2); |
| } |
| else if ("actionListener".equals(targetAttributeName)) |
| { |
| return !(component instanceof ActionSource2); |
| } |
| else if ("validator".equals(targetAttributeName)) |
| { |
| return !(component instanceof EditableValueHolder); |
| } |
| else if ("valueChangeListener".equals(targetAttributeName)) |
| { |
| return !(component instanceof EditableValueHolder); |
| } |
| else |
| { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private MethodExpression reWrapMethodExpression(MethodExpression createdMethodExpression, |
| ValueExpression originalValueExpression) |
| { |
| if (originalValueExpression instanceof LocationValueExpression) |
| { |
| return new LocationMethodExpression( |
| ((LocationValueExpression) originalValueExpression).getLocation(), |
| reWrapMethodExpression(createdMethodExpression, |
| ((LocationValueExpression) originalValueExpression).getWrapped()), |
| ((LocationValueExpression) originalValueExpression).getCCLevel()); |
| } |
| else if (originalValueExpression instanceof FacesWrapper && |
| ((FacesWrapper) originalValueExpression).getWrapped() instanceof ValueExpression) |
| { |
| return reWrapMethodExpression(createdMethodExpression, |
| (ValueExpression) ((FacesWrapper) originalValueExpression).getWrapped()); |
| } |
| else |
| { |
| return createdMethodExpression; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public Resource getScriptComponentResource(FacesContext context, Resource componentResource) |
| { |
| checkNull(context, "context"); |
| checkNull(componentResource, "componentResource"); |
| |
| return null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public StateManagementStrategy getStateManagementStrategy(FacesContext context, String viewId) |
| { |
| // Use partial state saving strategy only if javax.faces.PARTIAL_STATE_SAVING is "true" and |
| // the current view is not on javax.faces.FULL_STATE_SAVING_VIEW_IDS. |
| return _usePartialStateSavingOnThisView(viewId) ? stateManagementStrategy : null; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public ViewMetadata getViewMetadata(FacesContext context, String viewId) |
| { |
| checkNull(context, "facesContext"); |
| checkNull(viewId, "viewId"); |
| return new FaceletViewMetadata(viewId); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void renderView(FacesContext context, UIViewRoot view) throws IOException |
| { |
| if (!view.isRendered()) |
| { |
| return; |
| } |
| |
| // log request |
| if (log.isLoggable(Level.FINE)) |
| { |
| log.fine("Rendering View: " + view.getViewId()); |
| } |
| |
| try |
| { |
| // build view - but not if we're in "buildBeforeRestore" |
| // land and we've already got a populated view. Note |
| // that this optimizations breaks if there's a "c:if" in |
| // the page that toggles as a result of request processing - |
| // should that be handled? Or |
| // is this optimization simply so minor that it should just |
| // be trimmed altogether? |
| // See JSF 2.0 spec section 2.2.6, buildView is called before |
| // Render Response |
| //if (!isFilledView(context, view)) |
| //{ |
| // buildView(context, view); |
| //} |
| |
| // setup writer and assign it to the context |
| ResponseWriter origWriter = createResponseWriter(context); |
| |
| ExternalContext extContext = context.getExternalContext(); |
| Writer outputWriter = extContext.getResponseOutputWriter(); |
| |
| StateWriter stateWriter = new StateWriter(outputWriter, 1024, context); |
| try |
| { |
| ResponseWriter writer = origWriter.cloneWithWriter(stateWriter); |
| try |
| { |
| context.setResponseWriter(writer); |
| |
| StateManager stateMgr = context.getApplication().getStateManager(); |
| // force creation of session if saving state there |
| // -= Leonardo Uribe =- Do this does not have any sense!. The only reference |
| // about these lines are on http://java.net/projects/facelets/sources/svn/revision/376 |
| // and it says: "fixed lazy session instantiation with eager response commit" |
| // This code is obviously to prevent this exception: |
| // java.lang.IllegalStateException: Cannot create a session after the response has been committed |
| // But in theory if that so, StateManager.saveState must happen before writer.close() is called, |
| // which can be done very easily. |
| //if (!stateMgr.isSavingStateInClient(context)) |
| //{ |
| // extContext.getSession(true); |
| //} |
| |
| // render the view to the response |
| writer.startDocument(); |
| |
| view.encodeAll(context); |
| |
| writer.endDocument(); |
| |
| // finish writing |
| // -= Leonardo Uribe =- This does not has sense too, because that's the reason |
| // of the try/finally block. In practice, it only forces the close of the tag |
| // in HtmlResponseWriter if necessary, but according to the spec, this should |
| // be done using writer.flush() instead. |
| // writer.close(); |
| |
| // flush to origWriter |
| if (stateWriter.isStateWritten()) |
| { |
| // Call this method to force close the tag if necessary. |
| // The spec javadoc says this: |
| // "... Flush any ouput buffered by the output method to the underlying |
| // Writer or OutputStream. This method will not flush the underlying |
| // Writer or OutputStream; it simply clears any values buffered by this |
| // ResponseWriter. ..." |
| writer.flush(); |
| |
| // =-= markoc: STATE_KEY is in output ONLY if |
| // stateManager.isSavingStateInClient(context)is true - see |
| // org.apache.myfaces.application.ViewHandlerImpl.writeState(FacesContext) |
| // TODO this class and ViewHandlerImpl contain same constant <!--@@JSF_FORM_STATE_MARKER@@--> |
| Object stateObj = stateMgr.saveView(context); |
| String content = stateWriter.getAndResetBuffer(); |
| int end = content.indexOf(STATE_KEY); |
| // See if we can find any trace of the saved state. |
| // If so, we need to perform token replacement |
| if (end >= 0) |
| { |
| // save state |
| int start = 0; |
| |
| while (end != -1) |
| { |
| origWriter.write(content, start, end - start); |
| |
| String stateStr; |
| if (view.isTransient()) |
| { |
| // Force state saving |
| stateMgr.writeState(context, stateObj); |
| stateStr = stateWriter.getAndResetBuffer(); |
| } |
| else if (stateObj == null) |
| { |
| stateStr = null; |
| } |
| else |
| { |
| stateMgr.writeState(context, stateObj); |
| stateStr = stateWriter.getAndResetBuffer(); |
| } |
| |
| if (stateStr != null) |
| { |
| origWriter.write(stateStr); |
| } |
| start = end + STATE_KEY_LEN; |
| end = content.indexOf(STATE_KEY, start); |
| } |
| |
| origWriter.write(content, start, content.length() - start); |
| // No trace of any saved state, so we just need to flush the buffer |
| } |
| else |
| { |
| origWriter.write(content); |
| } |
| } |
| else if (stateWriter.isStateWrittenWithoutWrapper()) |
| { |
| // The state token has been written but the state has not been |
| // saved yet. |
| stateMgr.saveView(context); |
| } |
| else |
| { |
| // GET case without any form that trigger state saving. |
| // Try to store it into cache. |
| if (viewPoolProcessor != null && |
| viewPoolProcessor.isViewPoolEnabledForThisView(context, view)) |
| { |
| ViewDeclarationLanguage vdl = context.getApplication().getViewHandler() |
| .getViewDeclarationLanguage(context, view.getViewId()); |
| |
| if (ViewDeclarationLanguage.FACELETS_VIEW_DECLARATION_LANGUAGE_ID.equals(vdl.getId())) |
| { |
| StateManagementStrategy sms = vdl.getStateManagementStrategy(context, view.getId()); |
| if (sms != null) |
| { |
| context.getAttributes().put(ViewPoolProcessor.FORCE_HARD_RESET, Boolean.TRUE); |
| |
| // Force indirectly to store the map in the cache |
| try |
| { |
| Object state = sms.saveView(context); |
| } |
| finally |
| { |
| context.getAttributes().remove(ViewPoolProcessor.FORCE_HARD_RESET); |
| } |
| |
| // Clear the calculated value from the application map |
| context.getAttributes().remove(SERIALIZED_VIEW_REQUEST_ATTR); |
| } |
| } |
| } |
| } |
| } |
| finally |
| { |
| // The Facelets implementation must close the writer used to write the response |
| writer.close(); |
| } |
| } |
| finally |
| { |
| stateWriter.release(context); |
| } |
| } |
| catch (FileNotFoundException e) |
| { |
| handleFaceletNotFound(context, view.getViewId()); |
| } |
| catch (Exception e) |
| { |
| handleRenderException(context, e); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public UIViewRoot createView(FacesContext context, String viewId) |
| { |
| checkNull(viewId, "viewId"); |
| // we have to check for a possible debug request |
| if (UIDebug.debugRequest(context)) |
| { |
| // the current request is a debug request, so we don't need |
| // to create a view, since the output has already been written |
| // in UIDebug.debugRequest() and facesContext.responseComplete() |
| // has been called. |
| return null; |
| } |
| else |
| { |
| UIViewRoot root = super.createView(context, viewId); |
| if (root != null) |
| { |
| //Ensure calculateResourceLibraryContracts() can be decorated |
| ViewDeclarationLanguage vdl = context.getApplication().getViewHandler() |
| .getViewDeclarationLanguage(context, viewId); |
| List<String> contracts = vdl.calculateResourceLibraryContracts( |
| context, root.getViewId() != null ? root.getViewId() : viewId); |
| context.setResourceLibraryContracts(contracts); |
| } |
| return root; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public UIViewRoot restoreView(FacesContext context, String viewId) |
| { |
| checkNull(viewId, "viewId"); |
| // Currently there is no way, in which UIDebug.debugRequest(context) |
| // can create debug information and return true at this point, |
| // because this method is only accessed if the current request |
| // is a postback, which will never be true for a debug page. |
| // The only point where valid debug output can be produced by now |
| // is in createView() -= Jakob Korherr =- |
| |
| Application application = context.getApplication(); |
| ViewHandler viewHandler = application.getViewHandler(); |
| |
| // When partial state saving is not used, createView() is not called. To ensure |
| // everything is in place it is necessary to calculate the resource library |
| // contracts and set them into facesContext. |
| ViewDeclarationLanguage vdl = viewHandler.getViewDeclarationLanguage(context, viewId); |
| List<String> contracts = vdl.calculateResourceLibraryContracts(context, viewId); |
| context.setResourceLibraryContracts(contracts); |
| |
| // JSF 2.2 stateless views |
| // We need to check if the incoming view is stateless or not and if that so rebuild it here |
| // note we cannot do this in DefaultFaceletsStateManagementStrategy because it is only used |
| // when PSS is enabled, but stateless views can be used without PSS. If the view is stateless, |
| // there is no need to ask to the StateManager. |
| String renderKitId = viewHandler.calculateRenderKitId(context); |
| |
| ResponseStateManager manager = getRenderKitFactory().getRenderKit(context, renderKitId) |
| .getResponseStateManager(); |
| |
| if (manager.isStateless(context, viewId)) |
| { |
| // Per the spec: build the view. |
| UIViewRoot view = null; |
| try |
| { |
| ViewMetadata metadata = vdl.getViewMetadata(context, viewId); |
| if (metadata != null) |
| { |
| view = metadata.createMetadataView(context); |
| } |
| if (view == null) |
| { |
| view = viewHandler.createView(context, viewId); |
| } |
| context.setViewRoot (view); |
| boolean oldContextEventState = context.isProcessingEvents(); |
| try |
| { |
| context.setProcessingEvents (true); |
| vdl.buildView (context, view); |
| |
| // If the view is not transient, then something is wrong. Throw an exception. |
| if (!view.isTransient()) |
| { |
| throw new FacesException("unable to create view \"" + viewId + "\""); |
| } |
| } |
| finally |
| { |
| context.setProcessingEvents(oldContextEventState); |
| } |
| } |
| catch (Throwable e) |
| { |
| throw new FacesException("unable to create view \"" + viewId + '"', e); |
| } |
| FaceletsViewDeclarationLanguageUtils.markRenderedResources(context, view); |
| return view; |
| } |
| else |
| { |
| UIViewRoot root = super.restoreView(context, viewId); |
| FaceletsViewDeclarationLanguageUtils.markRenderedResources(context, root); |
| return root; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected String calculateViewId(FacesContext context, String viewId) |
| { |
| return viewIdSupport.deriveLogicalViewId(context, viewId); |
| } |
| |
| /** |
| * Creates the Facelet page compiler. |
| * |
| * @param context |
| * the current FacesContext |
| * |
| * @return the application's Facelet page compiler |
| */ |
| protected Compiler createCompiler(FacesContext context) |
| { |
| Compiler compiler = new SAXCompiler(); |
| |
| compiler.setDevelopmentProjectStage(context.isProjectStage(ProjectStage.Development)); |
| |
| loadLibraries(context, compiler); |
| loadDecorators(context, compiler); |
| loadOptions(context, compiler); |
| |
| return compiler; |
| } |
| |
| /** |
| * Creates a FaceletFactory instance using the specified compiler. |
| * |
| * @param context |
| * the current FacesContext |
| * @param compiler |
| * the compiler to be used by the factory |
| * |
| * @return the factory used by this VDL to load pages |
| */ |
| protected FaceletFactory createFaceletFactory(FacesContext context, Compiler compiler) |
| { |
| ExternalContext eContext = context.getExternalContext(); |
| |
| // refresh period |
| long refreshPeriod; |
| if (context.isProjectStage(ProjectStage.Production)) |
| { |
| refreshPeriod = WebConfigParamUtils.getLongInitParameter(eContext, |
| ViewHandler.FACELETS_REFRESH_PERIOD_PARAM_NAME, DEFAULT_REFRESH_PERIOD_PRODUCTION); |
| } |
| else |
| { |
| refreshPeriod = WebConfigParamUtils.getLongInitParameter(eContext, |
| ViewHandler.FACELETS_REFRESH_PERIOD_PARAM_NAME, DEFAULT_REFRESH_PERIOD); |
| } |
| |
| // resource resolver |
| ResourceResolver resolver = new DefaultResourceResolver(); |
| ArrayList<String> classNames = new ArrayList<>(); |
| |
| String faceletsResourceResolverClassName = config.getResourceResolver(); |
| if (faceletsResourceResolverClassName != null) |
| { |
| classNames.add(faceletsResourceResolverClassName); |
| } |
| |
| List<String> resourceResolversFromAnnotations = RuntimeConfig.getCurrentInstance( |
| context.getExternalContext()).getResourceResolvers(); |
| if (!resourceResolversFromAnnotations.isEmpty()) |
| { |
| classNames.addAll(resourceResolversFromAnnotations); |
| } |
| |
| if (!classNames.isEmpty()) |
| { |
| resolver = ClassUtils.buildApplicationObject(ResourceResolver.class, classNames, resolver); |
| } |
| |
| _resourceResolver = resolver; |
| |
| return new DefaultFaceletFactory(compiler, resolver, refreshPeriod); |
| } |
| |
| |
| protected ResponseWriter createResponseWriter(FacesContext context) throws IOException, FacesException |
| { |
| ExternalContext extContext = context.getExternalContext(); |
| RenderKit renderKit = context.getRenderKit(); |
| // Avoid a cryptic NullPointerException when the renderkit ID |
| // is incorrectly set |
| if (renderKit == null) |
| { |
| String id = context.getViewRoot().getRenderKitId(); |
| throw new IllegalStateException("No render kit was available for id \"" + id + '"'); |
| } |
| |
| // set the buffer for content |
| int faceletsBufferSize = config.getFaceletsBufferSize(); |
| if (faceletsBufferSize != -1) |
| { |
| extContext.setResponseBufferSize(faceletsBufferSize); |
| } |
| |
| // get our content type |
| String contentType = (String) context.getAttributes().get("facelets.ContentType"); |
| |
| // get the encoding |
| String encoding = (String) context.getAttributes().get(PARAM_ENCODING); |
| |
| // -= Leonardo Uribe =- Add */* to the contentType is a fix done from FaceletViewHandler |
| // to make old RI versions work, but since this is for JSF 2.0 it is not necessary that code. |
| ResponseWriter writer = renderKit.createResponseWriter(FaceletsVDLUtils.NullWriter.INSTANCE, |
| contentType, encoding); |
| |
| // Override the JSF provided content type if necessary |
| contentType = getResponseContentType(context, writer.getContentType()); |
| encoding = getResponseEncoding(context, writer.getCharacterEncoding()); |
| |
| // apply them to the response |
| extContext.setResponseContentType(contentType + "; charset=" + encoding); |
| |
| // removed 2005.8.23 to comply with J2EE 1.3 |
| // response.setCharacterEncoding(encoding); |
| |
| // Now, clone with the real writer |
| writer = writer.cloneWithWriter(extContext.getResponseOutputWriter()); |
| |
| return writer; |
| } |
| |
| /** |
| * Generate the content type |
| * |
| * @param context |
| * @param orig |
| * @return |
| */ |
| protected String getResponseContentType(FacesContext context, String orig) |
| { |
| String contentType = orig; |
| |
| // see if we need to override the contentType |
| Map<Object, Object> m = context.getAttributes(); |
| if (m.containsKey("facelets.ContentType")) |
| { |
| contentType = (String) m.get("facelets.ContentType"); |
| if (log.isLoggable(Level.FINEST)) |
| { |
| log.finest("Facelet specified alternate contentType '" + contentType + '\''); |
| } |
| } |
| |
| // safety check |
| if (contentType == null) |
| { |
| contentType = "text/html"; |
| log.finest("ResponseWriter created had a null ContentType, defaulting to text/html"); |
| } |
| |
| return contentType; |
| } |
| |
| /** |
| * Generate the encoding |
| * |
| * @param context |
| * @param orig |
| * @return |
| */ |
| protected String getResponseEncoding(FacesContext context, String orig) |
| { |
| String encoding = orig; |
| |
| // see if we need to override the encoding |
| Map<Object, Object> m = context.getAttributes(); |
| |
| Object session = context.getExternalContext().getSession(false); |
| |
| // 1. check the request attribute |
| if (m.containsKey(PARAM_ENCODING)) |
| { |
| encoding = (String) m.get(PARAM_ENCODING); |
| if (encoding != null && log.isLoggable(Level.FINEST)) |
| { |
| log.finest("Facelet specified alternate encoding '" + encoding + '\''); |
| } |
| |
| if (session != null) |
| { |
| context.getExternalContext().getSessionMap().put(ViewHandler.CHARACTER_ENCODING_KEY, |
| encoding); |
| } |
| } |
| |
| // 2. get it from request |
| if (encoding == null) |
| { |
| encoding = context.getExternalContext().getRequestCharacterEncoding(); |
| } |
| |
| // 3. get it from the session |
| if (encoding == null) |
| { |
| if (session != null) |
| { |
| encoding = (String) context.getExternalContext().getSessionMap().get( |
| ViewHandler.CHARACTER_ENCODING_KEY); |
| if (encoding != null && log.isLoggable(Level.FINEST)) |
| { |
| log.finest("Session specified alternate encoding '" + encoding + '\''); |
| } |
| } |
| } |
| |
| // 4. default it |
| if (encoding == null) |
| { |
| encoding = DEFAULT_CHARACTER_ENCODING; |
| if (log.isLoggable(Level.FINEST)) |
| { |
| log.finest("ResponseWriter created had a null CharacterEncoding, defaulting to " + encoding); |
| } |
| } |
| |
| return encoding; |
| } |
| |
| protected void handleFaceletNotFound(FacesContext context, String viewId) throws FacesException, IOException |
| { |
| String actualId = context.getApplication().getViewHandler().getActionURL(context, viewId); |
| context.getExternalContext().responseSendError(HttpServletResponse.SC_NOT_FOUND, actualId); |
| context.responseComplete(); |
| } |
| |
| protected void handleRenderException(FacesContext context, Exception e) |
| throws IOException, ELException, FacesException |
| { |
| // rethrow the Exception to be handled by the ExceptionHandler |
| if (e instanceof RuntimeException) |
| { |
| throw (RuntimeException) e; |
| } |
| else if (e instanceof IOException) |
| { |
| throw (IOException) e; |
| } |
| else |
| { |
| throw new FacesException(e.getMessage(), e); |
| } |
| } |
| |
| /** |
| * Load the various decorators for Facelets. |
| * |
| * @param context the current FacesContext |
| * @param compiler the page compiler |
| */ |
| protected void loadDecorators(FacesContext context, Compiler compiler) |
| { |
| faceletsCompilerSupport.loadDecorators(context, compiler); |
| } |
| |
| /** |
| * Load the various tag libraries for Facelets. |
| * |
| * @param context the current FacesContext |
| * @param compiler the page compiler |
| */ |
| protected void loadLibraries(FacesContext context, Compiler compiler) |
| { |
| faceletsCompilerSupport.loadLibraries(context, compiler); |
| } |
| |
| /** |
| * Load the various options for Facelets compiler. Currently only comment skipping is supported. |
| * |
| * @param context the current FacesContext |
| * @param compiler the page compiler |
| */ |
| protected void loadOptions(FacesContext context, Compiler compiler) |
| { |
| faceletsCompilerSupport.loadOptions(context, compiler); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void sendSourceNotFound(FacesContext context, String message) |
| { |
| try |
| { |
| context.responseComplete(); |
| context.getExternalContext().responseSendError(HttpServletResponse.SC_NOT_FOUND, message); |
| } |
| catch (IOException ioe) |
| { |
| throw new FacesException(ioe); |
| } |
| } |
| |
| /** |
| * Gets the Facelet representing the specified view identifier. |
| * |
| * @param viewId |
| * the view identifier |
| * |
| * @return the Facelet representing the specified view identifier |
| * |
| * @throws IOException |
| * if a read or parsing error occurs |
| */ |
| private Facelet _getFacelet(FacesContext context, String viewId) throws IOException |
| { |
| // grab our FaceletFactory and create a Facelet |
| FaceletFactory.setInstance(faceletFactory); |
| try |
| { |
| return faceletFactory.getFacelet(context, viewId); |
| } |
| finally |
| { |
| FaceletFactory.setInstance(null); |
| } |
| } |
| |
| private Facelet _getViewMetadataFacelet(FacesContext context, String viewId) throws IOException |
| { |
| // grab our FaceletFactory and create a Facelet used to create view metadata |
| FaceletFactory.setInstance(faceletFactory); |
| try |
| { |
| return faceletFactory.getViewMetadataFacelet(context, viewId); |
| } |
| finally |
| { |
| FaceletFactory.setInstance(null); |
| } |
| } |
| |
| |
| private void _initializeContractMappings(ExternalContext context) |
| { |
| RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(context); |
| List<String> prefixWildcardKeys = new ArrayList<String>(); |
| Map<String, List<String>> contractMappings = new HashMap<String, List<String>>(); |
| |
| for (Map.Entry<String, List<String>> entry : runtimeConfig.getContractMappings().entrySet()) |
| { |
| String urlPattern = entry.getKey().trim(); |
| if (urlPattern.endsWith(ASTERISK)) |
| { |
| prefixWildcardKeys.add(urlPattern); |
| } |
| contractMappings.put(entry.getKey(), new ArrayList<String>(entry.getValue())); |
| } |
| |
| Collections.sort(prefixWildcardKeys, new FaceletsVDLUtils.KeyComparator()); |
| |
| this._prefixWildcardKeys = prefixWildcardKeys; |
| this._contractMappings = contractMappings; |
| } |
| |
| private boolean _usePartialStateSavingOnThisView(String viewId) |
| { |
| return config.isPartialStateSaving() |
| && !(fullStateSavingViewIds != null && fullStateSavingViewIds.contains(viewId)); |
| } |
| |
| @Override |
| public List<String> calculateResourceLibraryContracts(FacesContext context, String viewId) |
| { |
| List<String> contracts = this._contractMappings.get(viewId); |
| if (contracts == null) |
| { |
| //Check prefix mapping |
| for (String prefix : this._prefixWildcardKeys) |
| { |
| if (FaceletsVDLUtils.matchPattern(viewId, prefix)) |
| { |
| contracts = this._contractMappings.get(prefix); |
| break; |
| } |
| } |
| } |
| return contracts; |
| } |
| |
| private class FaceletViewMetadata extends ViewMetadataBase |
| { |
| /** |
| * Constructor |
| * |
| * Note that this viewId is not the one after calculateViewId() method |
| */ |
| public FaceletViewMetadata(String viewId) |
| { |
| super(viewId); |
| } |
| |
| @Override |
| public UIViewRoot createMetadataView(FacesContext context) |
| { |
| try |
| { |
| context.setProcessingEvents(false); |
| |
| // spec doesn't say that this is necessary, but we blow up later if |
| // the viewroot isn't available from the FacesContext. |
| // -= Leonardo Uribe =- since it is supposed when we apply view metadata |
| // facelet we don't apply components with renderers and we don't call getRenderKit() |
| // it is safe to let this one commented |
| // context.setViewRoot(view); |
| |
| // -= Leonardo Uribe =- This part is related to section 2.5.5 of jsf 2.0 spec. |
| // In theory what we need here is fill UIViewRoot.METADATA_FACET_NAME facet |
| // with UIViewParameter instances. Later, ViewHandlerImpl.getBookmarkableURL(), |
| // ViewHandlerImpl.getRedirectURL() and UIViewRoot.encodeEnd uses them. |
| // For now, the only way to do this is call buildView(context,view) method, but |
| // this is a waste of resources. We need to find another way to handle facelets view metadata. |
| // Call to buildView causes the view is not created on Render Response phase, |
| // if buildView is called from here all components pass through current lifecycle and only |
| // UIViewParameter instances should be taken into account. |
| // It should be an additional call to buildView on Render Response phase. |
| // buildView(context, view); |
| |
| context.getAttributes().put(BUILDING_VIEW_METADATA, Boolean.TRUE); |
| |
| // we have to invoke createView() on the application's ViewHandler |
| // here instead of invoking it directly in FaceletVDL, because |
| // the ViewHandler might be wrapped and wants to do some work |
| // in createView() (e.g. in Trinidad - see MYFACES-2641) |
| UIViewRoot view = context.getApplication().getViewHandler().createView(context, getViewId()); |
| |
| if (view != null) |
| { |
| // inside createView(context,viewId), calculateViewId() is called and |
| // the result is stored inside created UIViewRoot, so we can safely take the derived |
| // viewId from there. |
| Facelet facelet = null; |
| try |
| { |
| facelet = _getViewMetadataFacelet(context, view.getViewId()); |
| } |
| catch (FileNotFoundException e) |
| { |
| sendSourceNotFound(context, getViewId()); |
| return null; |
| } |
| |
| facelet.apply(context, view); |
| } |
| |
| return view; |
| } |
| catch (IOException ioe) |
| { |
| throw new FacesException(ioe); |
| } |
| finally |
| { |
| context.getAttributes().remove(BUILDING_VIEW_METADATA); |
| |
| context.setProcessingEvents(true); |
| } |
| } |
| } |
| |
| public FaceletFactory getFaceletFactory() |
| { |
| return faceletFactory; |
| } |
| |
| @Override |
| public UIComponent createComponent(FacesContext context, |
| String taglibURI, String tagName, Map<String, Object> attributes) |
| { |
| checkNull(context, "context"); |
| UIComponent createdComponent = null; |
| try |
| { |
| Facelet componentFacelet; |
| FaceletFactory.setInstance(faceletFactory); |
| try |
| { |
| componentFacelet |
| = faceletFactory.compileComponentFacelet(taglibURI, tagName, attributes); |
| } |
| finally |
| { |
| FaceletFactory.setInstance(null); |
| } |
| if (componentFacelet == null) |
| { |
| return null; |
| } |
| // Create a temporal component base class where all components will be put, but we are only |
| // interested in the inner UIComponent and if multiple are created, return this one. |
| boolean requiresDynamicRefresh = false; |
| boolean requiresFaceletDynamicRefresh = false; |
| UIPanel tempParent |
| = (UIPanel) context.getApplication().createComponent( |
| context, UIPanel.COMPONENT_TYPE, null); |
| tempParent.setId(context.getViewRoot().createUniqueId(context, null)); |
| String baseKey = tempParent.getId(); |
| baseKey = baseKey.startsWith(UIViewRoot.UNIQUE_ID_PREFIX) ? baseKey.substring(4) : baseKey; |
| |
| try |
| { |
| tempParent.pushComponentToEL(context, tempParent); |
| ((AbstractFacelet)componentFacelet).applyDynamicComponentHandler( |
| context, tempParent, baseKey); |
| } |
| finally |
| { |
| tempParent.popComponentFromEL(context); |
| // There are two cases: |
| // 1. If we are under facelets algorithm control (binding case), the refreshing logic will be done |
| // outside this block. We can check that condition easily with FaceletCompositionContext |
| // 2. If we are not under facelets algorithm control, check if the dynamic component requires refresh, |
| // if that so, mark the view to be refreshed and reset the flag, otherwise continue. This check |
| // allows us to decide if we add a third listener to refresh on transient build. |
| // Check if the current component requires dynamic refresh and if that so, |
| FaceletCompositionContext fcc = FaceletCompositionContext.getCurrentInstance(context); |
| if (fcc != null) |
| { |
| requiresFaceletDynamicRefresh = true; |
| } |
| else if (FaceletViewDeclarationLanguageBase.isDynamicComponentNeedsRefresh(context)) |
| { |
| FaceletViewDeclarationLanguageBase.activateDynamicComponentRefreshTransientBuild(context); |
| FaceletViewDeclarationLanguageBase.resetDynamicComponentNeedsRefreshFlag(context); |
| requiresDynamicRefresh = true; |
| } |
| } |
| if (tempParent.getChildCount() > 1) |
| { |
| // Multiple child. The tempParent will be returned. No need to |
| // save MARK_CREATED. |
| createdComponent = tempParent; |
| tempParent.getAttributes().put(DYN_WRAPPER, baseKey); |
| tempParent.subscribeToEvent(PostRestoreStateEvent.class, new |
| RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey)); |
| if (requiresFaceletDynamicRefresh) |
| { |
| FaceletViewDeclarationLanguageBase.dynamicComponentNeedsRefresh(context); |
| } |
| } |
| else if (tempParent.getChildCount() == 1) |
| { |
| createdComponent = tempParent.getChildren().get(0); |
| boolean requiresRefresh = false; |
| // One child. In that case there are three choices: |
| if (UIComponent.isCompositeComponent(createdComponent)) |
| { |
| // 1. Composite component. Needs special handling because |
| // facets will be added programatically. The algorithm that |
| // process the composite component content should occur |
| // after the component is added to the view (PostAddToViewEvent). |
| // Requires refresh. To do that, we need to save the MARK_CREATED |
| // value and set it only when the full component is refreshed after |
| // restore it. |
| createdComponent.getAttributes().put(GEN_MARK_ID, |
| createdComponent.getAttributes().get(ComponentSupport.MARK_CREATED)); |
| createdComponent.getAttributes().put(ComponentSupport.MARK_CREATED, null); |
| createdComponent.subscribeToEvent(PostAddToViewEvent.class, new |
| CreateDynamicCompositeComponentListener(taglibURI, tagName, attributes, baseKey)); |
| requiresRefresh = true; |
| if (requiresFaceletDynamicRefresh) |
| { |
| FaceletViewDeclarationLanguageBase.dynamicComponentNeedsRefresh(context); |
| } |
| } |
| else if (createdComponent.getChildCount() > 0) |
| { |
| // 2. Single component with children inside. |
| // Requires refresh. To do that, we need to save the MARK_CREATED |
| // value and set it only when the full component is refreshed after |
| // restore it. |
| createdComponent.getAttributes().put(GEN_MARK_ID, |
| createdComponent.getAttributes().get(ComponentSupport.MARK_CREATED)); |
| createdComponent.getAttributes().put(ComponentSupport.MARK_CREATED, null); |
| requiresRefresh = true; |
| if (requiresFaceletDynamicRefresh) |
| { |
| FaceletViewDeclarationLanguageBase.dynamicComponentNeedsRefresh(context); |
| } |
| } |
| else if (createdComponent.isTransient()) |
| { |
| // Just transient markup inside. It is necessary to wrap |
| // that content into a component. Requires refresh. No need to |
| // save MARK_CREATED. No requires dynamic refresh. |
| createdComponent = tempParent; |
| tempParent.getAttributes().put(DYN_WRAPPER, baseKey); |
| requiresRefresh = true; |
| } |
| else |
| { |
| // 4. Single component without children: |
| // Remove MARK_CREATED because it does not requires |
| // refresh on restore. When it is added to the component |
| // tree, it will be saved and restored as if was a programatically |
| // added component. |
| createdComponent.getAttributes().put(ComponentSupport.MARK_CREATED, null); |
| } |
| if (requiresRefresh) |
| { |
| createdComponent.subscribeToEvent(PostRestoreStateEvent.class, new |
| RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey)); |
| } |
| if (requiresDynamicRefresh) |
| { |
| createdComponent.subscribeToEvent(DynamicComponentRefreshTransientBuildEvent.class, new |
| RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey)); |
| createdComponent.getAttributes().put( |
| DynamicComponentRefreshTransientBuildEvent.DYN_COMP_REFRESH_FLAG, Boolean.TRUE); |
| } |
| if (requiresFaceletDynamicRefresh) |
| { |
| createdComponent.subscribeToEvent(FaceletDynamicComponentRefreshTransientBuildEvent.class, new |
| RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey)); |
| } |
| } |
| } |
| catch (IOException e) |
| { |
| throw new FacesException(e); |
| } |
| return createdComponent; |
| } |
| |
| @Override |
| public Stream<String> getViews(FacesContext facesContext, String path, int maxDepth, ViewVisitOption... options) |
| { |
| Stream<String> stream = super.getViews(facesContext, path, maxDepth, options); |
| RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(facesContext.getExternalContext()); |
| stream = stream.filter(f -> (strategy.handles(f) && |
| !FaceletsTemplateMappingUtils.matchTemplate(runtimeConfig, f) ) ); |
| if (options != null && |
| Arrays.binarySearch(options, ViewVisitOption.RETURN_AS_MINIMAL_IMPLICIT_OUTCOME) >= 0) |
| { |
| stream = stream.map(f -> strategy.getMinimalImplicitOutcome(f)); |
| } |
| return stream; |
| } |
| |
| } |