/* * � Copyright IBM Corp. 2010 * * Licensed 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 com.ibm.xsp.extlib.component.dynamiccontent; import java.io.IOException; import javax.faces.FacesException; import javax.faces.component.ContextCallback; import javax.faces.component.NamingContainer; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import com.ibm.xsp.context.FacesContextEx; import com.ibm.xsp.extlib.component.util.DynamicUIUtil; import com.ibm.xsp.extlib.util.ExtLibUtil; import com.ibm.xsp.page.FacesComponentBuilder; import com.ibm.xsp.util.FacesUtil; /** * Abstract base class that defines a in-place component. * Note that this does not have a tag-name, and is not intended * to be used directly in an XPage; generally {@link UIDynamicContent} * is used for dynamic behaviors instead. * <p> * </p> */ public abstract class UIDynamicControl extends AbstractDynamicContent implements FacesDynamicContainer, NamingContainer{ // Note this is implementing NamingContainer to work around an issue (SPR#MKEE8P4L8D) in // the xspClientDojo.js validateAll implementation, where it expects // the execId control to be a NamingContainer, and otherwise will not validate // the content of that control. public static final String COMPONENT_FAMILY = "com.ibm.xsp.extlib.dynamiccontent.Dynamic"; // $NON-NLS-1$ public static final String COMPONENT_TYPE = "com.ibm.xsp.extlib.dynamiccontent.Dynamic"; // $NON-NLS-1$ // FacesDynamicContainer private String sourcePageName; private transient UIComponent oldSubTree; private transient UIComponent componentBeingConstructed; /** * */ public UIDynamicControl() { } @Override public boolean isAutoCreate() { // The children are not auto-created when the component is created return false; } @Override public String getFamily() { return COMPONENT_FAMILY; } // ======================================================== // FacesComponent implementation // ======================================================== /** * Build the children. */ @Override public void buildContents(FacesContext context, FacesComponentBuilder builder) throws FacesException { if(isDynamicContent()) { // If we are building a dynamic component, then we should create the children if(DynamicUIUtil.isDynamicallyConstructing(context)) { // Temporarily reset this flag so another InPlace container, child of this one, won't be constructed. this.componentBeingConstructed = DynamicUIUtil.getDynamicallyConstructedComponent(context); DynamicUIUtil.setDynamicallyConstructing(context, null); try { buildDynamicContents(context, builder); return; } finally { DynamicUIUtil.setDynamicallyConstructing(context, componentBeingConstructed); this.componentBeingConstructed = null; } } else { // We are simply building the component out side onBeforeContent(context); //[dc] If the content parameter is present as parameter that means the URL was of the form http://my.web/myxsp.xsp?content=key // in this case we build the actual "key" facet, so the whole page with the correct content will be returned to the client. // This is needed when a web crawler visit the page following the link. String xspFacet = ExtLibUtil.readParameter(context,UIDynamicContent.XSPCONTENT_PARAM); if(xspFacet != null && builder.isFacetAvailable(context, this, xspFacet)) { buildDynamicContents(context, builder); }else { buildDefaultContents(context, builder); } onAfterContent(context); } } // Normal stuff here... super.buildContents(context, builder); } protected UIComponent getComponentBeingConstructed() { return componentBeingConstructed; } public void buildDefaultContents(FacesContext context, FacesComponentBuilder builder) throws FacesException { // No default content being built... } protected void buildDynamicContents(FacesContext context, FacesComponentBuilder builder) throws FacesException { builder.buildAll(context, this, true); } // ======================================================== // FacesDynamicContainer implementation // ======================================================== public String getSourcePageName() { return sourcePageName; } public void setSourcePageName(String sourcePageName) { this.sourcePageName = sourcePageName; } // ====================================================================== // Dynamically build the content // ====================================================================== /** * Check if the component is valid in context. */ protected boolean isValidInContext(FacesContext context) { return isContentCreated(); } /** * Dynamically create the children. * <p> * The children are generally added to this component, using the definition of this * component. But this behavior can be overridden for more complex use cases, like * the UIDialog. * </p> */ @Override public void createChildren(FacesContextEx context) { UIComponent rootComponent = getRootComponent(); DynamicUIUtil.createChildren(context,rootComponent,getCreateId()); } /** * Get the id of the component to create. * <p> * It is generally the current component is, but can be in fact another one, like * in the UIDialog implementation. * </p> * @return */ protected String getCreateId() { // return the current id return getId(); } /** * Get the root component. * <p> * This is the root component to which the children will be dynamically added. It is * generally the current component, but can be overridden, like in the UIDialog implementation. * </p> * @return */ protected UIComponent getRootComponent() { return this; } /** * Get the sub tree component for partial execute/refresh. * <p> * This automatically enables partial execute/refresh for this component, unless the * returned component is empty (the whole tree will then be processed). * </p> * @return */ protected UIComponent getSubTreeComponent() { return isDynamicContent() ? this : null; } @Override public void encodeBegin(FacesContext context) throws IOException { UIComponent subTree = getSubTreeComponent(); if(subTree!=null) { // The InPlace container pushes itself as the current subtree // component so the event handler only do partial execute if(isValidInContext(context)) { FacesContextEx ctx = (FacesContextEx)context; oldSubTree = ctx.getSubTreeComponent(); if(oldSubTree==null) oldSubTree = this; ctx.setSubTreeComponent(subTree); } } super.encodeBegin(context); } @Override public void encodeEnd(FacesContext context) throws IOException { super.encodeEnd(context); if(oldSubTree!=null) { FacesContextEx ctx = (FacesContextEx)context; ctx.setSubTreeComponent(oldSubTree!=this?(UIComponent)oldSubTree:null); oldSubTree = null; } } @Override public void encodeChildren(FacesContext context) throws IOException { if(isDynamicContent()) { if(!isValidInContext(context)) { return; } FacesUtil.renderChildren(context, this); } else { super.encodeChildren(context); } } @Override public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback) throws FacesException { // Handle partial refresh here FacesContextEx ctx = (FacesContextEx)context; if(ctx.isRenderingPhase()) { UIComponent subTree = getSubTreeComponent(); if(subTree!=null) { this.oldSubTree = ctx.getSubTreeComponent(); ctx.setSubTreeComponent(subTree); try { return super.invokeOnComponent(context, clientId, callback); } finally { ctx.setSubTreeComponent(oldSubTree instanceof UIComponent?(UIComponent)oldSubTree:null); oldSubTree = null; } } } return super.invokeOnComponent(context, clientId, callback); } @Override public boolean getRendersChildren() { if(isDynamicContent()) { return true; } else { return super.getRendersChildren(); } } // // State handling // @Override public void restoreState(FacesContext _context, Object _state) { Object _values[] = (Object[]) _state; super.restoreState(_context, _values[0]); sourcePageName = (String)_values[1]; } @Override public Object saveState(FacesContext _context) { Object _values[] = new Object[2]; _values[0] = super.saveState(_context); _values[1] = sourcePageName; return _values; } }