/* * � Copyright IBM Corp. 2010, 2013 * * 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 java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Map; import javax.faces.FacesException; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.el.MethodBinding; import javax.faces.el.ValueBinding; import com.ibm.commons.util.StringUtil; import com.ibm.xsp.FacesExceptionEx; 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.StateHolderUtil; import com.ibm.xsp.util.TypedUtil; /** * Dynamic panel that selects a facet to display. * <p> * </p> */ public class UIDynamicContent extends UIDynamicControl { public static final String COMPONENT_TYPE = "com.ibm.xsp.extlib.dynamiccontent.DynamicContent"; // $NON-NLS-1$ public static final String RENDERER_TYPE = "com.ibm.xsp.extlib.dynamiccontent.DynamicContent"; //$NON-NLS-1$ public static final String XSPCONTENT_PARAM = "content"; // $NON-NLS-1$ public static final String PSEUDO_FACET_EMPTY = "-empty-"; // $NON-NLS-1$ public static final String PSEUDO_FACET_CHILDREN = "-children-"; // $NON-NLS-1$ public static final String PSEUDO_FACET_DEFAULT = "-default-"; // $NON-NLS-1$ private Boolean partialEvents; private Boolean useHash; private String defaultFacet; // The currently displayed facet, not a property in the All Properties. // This does not vary for different clientIds, as it reflects the // structure of the UIComponent tree. private String currentFacet; // Server side events events private MethodBinding beforeContentLoad; private MethodBinding afterContentLoad; private transient String hashString; /** * */ public UIDynamicContent() { setRendererType(RENDERER_TYPE); // note, for SPR#PHAN8LEFDX, the property autoCreate was // removed from this control. // Since 2010-Nov-15, this control defaults to auto-creating the defaultFacet // during the page load phase. Before, by default the control was empty. // If you need the control to appear initially empty, set defaultFacet="-empty-" // using the PSEUDO_FACET_EMPTY. // As a partial fix for SPR#PHAN8LEFDX: // Bridgetown, Dynamic Content, autoCreate=true gives IllegalArgumentException // The fix is to remove the autoCreate property, // although that is a change that may break existing applications, // and so needs to have an associated tech note. } public boolean isUseHash() { if (null != this.useHash) { return this.useHash; } ValueBinding _vb = getValueBinding("useHash"); //$NON-NLS-1$ if (_vb != null) { Boolean val = (java.lang.Boolean) _vb.getValue(FacesContext.getCurrentInstance()); if(val!=null) { return val; } } return false; } public void setUseHash(boolean useHash) { this.useHash = useHash; } public boolean isPartialEvents() { if (null != this.partialEvents) { return this.partialEvents; } ValueBinding _vb = getValueBinding("partialEvents"); //$NON-NLS-1$ if (_vb != null) { Boolean val = (java.lang.Boolean) _vb.getValue(FacesContext.getCurrentInstance()); if(val!=null) { return val; } } return false; } public void setPartialEvents(boolean partialEvents) { this.partialEvents = partialEvents; } public String getDefaultFacet() { if (null != this.defaultFacet) { return this.defaultFacet; } ValueBinding _vb = getValueBinding("defaultFacet"); //$NON-NLS-1$ if (_vb != null) { return (java.lang.String) _vb.getValue(getFacesContext()); } else { return null; } } public void setDefaultFacet(String defaultFacet) { this.defaultFacet = defaultFacet; } @Override public MethodBinding getBeforeContentLoad() { return beforeContentLoad; } public void setBeforeContentLoad(MethodBinding beforeContentLoad) { this.beforeContentLoad = beforeContentLoad; } @Override public MethodBinding getAfterContentLoad() { return afterContentLoad; } public void setAfterContentLoad(MethodBinding afterContentLoad) { this.afterContentLoad = afterContentLoad; } @Override protected UIComponent getSubTreeComponent() { if(isPartialEvents()) { return this; } return null; } /** * Hide the current facet. */ public void hide() { show(PSEUDO_FACET_EMPTY); } /** * Show a facet. */ public void show(String facet) { show(facet,null); } public void show(String facet, Map<String,String> parameters) { FacesContextEx context = FacesContextEx.getCurrentInstance(); if( StringUtil.isEmpty(facet) ){ // when the empty string is passed in, load -default- facet = PSEUDO_FACET_DEFAULT; } if ( PSEUDO_FACET_DEFAULT.equals(facet) ){ // recompute the default facet and load that facet = getDefaultFacet(); if( StringUtil.isEmpty(facet) || PSEUDO_FACET_DEFAULT.equals(facet) ){ // when the defaultFacet is empty, load -children- facet = PSEUDO_FACET_CHILDREN; } } // facet is non-empty here if( !StringUtil.equals(PSEUDO_FACET_EMPTY, facet) ) { pushParameters(context, parameters); TypedUtil.getRequestMap(context.getExternalContext()).put(XSPCONTENT_PARAM,facet); createContent(context); } else { deleteContent(context); currentFacet = facet; } updateHash(facet, parameters); } protected void updateHash(String facet, Map<String,String> parameters) { StringBuilder b = new StringBuilder(); if(StringUtil.isNotEmpty(facet)) { try { b.append(XSPCONTENT_PARAM); b.append('='); b.append(URLEncoder.encode(facet,"UTF-8")); // $NON-NLS-1$ if(parameters!=null) { for(Map.Entry<String, String> e: parameters.entrySet()) { b.append('&'); b.append(URLEncoder.encode(e.getKey().toString(), "UTF-8")); // $NON-NLS-1$ b.append('='); b.append(URLEncoder.encode(e.getValue().toString(),"UTF-8")); // $NON-NLS-1$ } } } catch(UnsupportedEncodingException ex) {} } hashString = b.toString(); } public String getHashString() { return hashString; } /** * The name of the currently displayed facet, not a property in the All Properties. * This does not vary for different clientIds, as it reflects the * structure of the UIComponent tree. * * @return the currentFacet */ public String getCurrentFacet() { return currentFacet; } /** * Check if this request asks to create the content. */ public boolean isCreateRequest(FacesContextEx ctx) { // This should be a partial refresh request on this component // if(ctx.isAjaxPartialRefresh()) { String id = ctx.getPartialRefreshId(); if(StringUtil.equals(id, getClientId(ctx))) { // We should check that this is the initial request String xspContent = (String)ctx.getExternalContext().getRequestParameterMap().get(XSPCONTENT_PARAM); return StringUtil.isNotEmpty(xspContent); } } return false; } @Override public void encodeBegin(FacesContext context) throws IOException { // Create the children if this request is a create request FacesContextEx ctx = (FacesContextEx)context; if(isCreateRequest(ctx)) { createContent(ctx); } super.encodeBegin(context); } @Override public void buildDefaultContents(FacesContext context, FacesComponentBuilder builder) throws FacesException { String facet = getDefaultFacet(); if( StringUtil.isEmpty(facet) || PSEUDO_FACET_DEFAULT.equals(facet) ){ // when the defaultFacet is empty it loads -children- facet = PSEUDO_FACET_CHILDREN; } if( StringUtil.equals(PSEUDO_FACET_EMPTY, facet) ){ // do nothing }else { buildFacet(context, builder, facet); } } @Override protected void buildDynamicContents(FacesContext context, FacesComponentBuilder builder) throws FacesException { // Build the facet if passed as a parameter String xspFacet = ExtLibUtil.readParameter(context,XSPCONTENT_PARAM); // the pseudo-facet will have been pre-processed by the show(..) method, // so it will not be "", -empty- or -default-, but it may still be -children- buildFacet(context, builder, xspFacet); } protected void buildFacet(FacesContext context, FacesComponentBuilder builder, String xspFacet) throws FacesException { if( StringUtil.isEmpty(xspFacet) ){ // programmatic error - this should never be called with a null or empty argument, // need to explicitly indicate whether -children- or -empty- is intended. throw new IllegalArgumentException("buildFacet should not be called with an empty facet"); //$NON-NLS-1$ } // Set the current facet UIDynamicContent dyn = (UIDynamicContent)getComponentBeingConstructed(); if(dyn==null) { dyn = this; } dyn.currentFacet = xspFacet; if( StringUtil.equalsIgnoreCase(PSEUDO_FACET_EMPTY, xspFacet) ){ return; } if( StringUtil.equalsIgnoreCase(PSEUDO_FACET_CHILDREN, xspFacet) ){ builder.buildChildren(context, this); return; } // If there is a facet, construct it if( ! builder.isFacetAvailable(context, this, xspFacet) ){ String msg = "Cannot find the facet named {0} in the Dynamic Content with ID {1}."; // $NLX-UIDynamicContent.Cannotfindthefacetnamed0intheDyna-1$ String facetOut = null == xspFacet? null : '"'+xspFacet+'"'; String idOut = getId(); idOut = null == idOut? null : '"'+idOut+'"'; msg = StringUtil.format(msg, facetOut, getId()); throw new FacesExceptionEx(msg); } builder.buildFacet(context, this, xspFacet); // And make its content the children UIComponent c = (UIComponent)getFacets().get(xspFacet); if(c!=null) { TypedUtil.getChildren(this).add(c); } // else loaded evaluated to false on the facet control } @Override public void buildContents(FacesContext context, FacesComponentBuilder builder) throws FacesException { // Ensure the source page name is properly stored setSourcePageName(DynamicUIUtil.getSourcePageName(builder)); // Normal stuff here... super.buildContents(context, builder); } // // State handling // @Override public void restoreState(FacesContext _context, Object _state) { Object _values[] = (Object[]) _state; super.restoreState(_context, _values[0]); partialEvents = (Boolean)_values[1]; useHash = (Boolean)_values[2]; defaultFacet = (String)_values[3]; currentFacet = (String)_values[4]; beforeContentLoad = StateHolderUtil.restoreMethodBinding(_context, this, _values[5]); afterContentLoad = StateHolderUtil.restoreMethodBinding(_context, this, _values[6]); } @Override public Object saveState(FacesContext _context) { Object _values[] = new Object[7]; _values[0] = super.saveState(_context); _values[1] = partialEvents; _values[2] = useHash; _values[3] = defaultFacet; _values[4] = currentFacet; _values[5] = StateHolderUtil.saveMethodBinding(_context, beforeContentLoad); _values[6] = StateHolderUtil.saveMethodBinding(_context, afterContentLoad); return _values; } }