/* * � Copyright IBM Corp. 2010, 2012 * * 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.util.List; import java.util.Map; import javax.faces.FacesException; import javax.faces.component.UIComponentBase; import javax.faces.context.FacesContext; import javax.faces.el.MethodBinding; import com.ibm.commons.util.StringUtil; import com.ibm.xsp.component.FacesComponent; import com.ibm.xsp.component.UIViewRootEx; import com.ibm.xsp.context.FacesContextEx; import com.ibm.xsp.extlib.beans.ViewStateBean; import com.ibm.xsp.extlib.component.util.DynamicUIUtil; import com.ibm.xsp.extlib.controls.ExtlibControlsLogger; import com.ibm.xsp.extlib.util.ExtLibUtil; import com.ibm.xsp.page.FacesComponentBuilder; import com.ibm.xsp.resource.DojoModulePathResource; import com.ibm.xsp.resource.DojoModuleResource; import com.ibm.xsp.resource.Resource; import com.ibm.xsp.resource.ScriptResource; import com.ibm.xsp.resource.StyleSheetResource; import com.ibm.xsp.util.FacesUtil; import com.ibm.xsp.util.TypedUtil; /** * Dynamic XPage content. * <p> * </p> */ public abstract class AbstractDynamicContent extends UIComponentBase implements FacesComponent { // Phil: ths is not enabled for now as it requires some changes in core // Basically, only the dojo resources are currently rendered correctly by partial refresh // Moreover, I think that the resources should only be added to a control *IF* they are // not already added, thus preventing 2 instances of a same control to add the same resources // Currently, we only ensure that the resources are not rendered twice, but they are // still added many times. public static final boolean USE_DYNAMIC_RESOURCES = false; public static final String DYNAMIC_RESOURCES = "_extlib.dynamiccontent.oldrccount"; //$NON-NLS-1$ /** * Check if the component manages dynamic content. * Dynamic means that the content is created dynamically (default for this * component). This can be disabled, for example, when the component has a * static content (ex: the tooltip can be static). */ protected boolean isDynamicContent() { return true; } /** * Utility function that pushes the parameters to the request. * @param context * @param parameters */ public void pushParameters(FacesContextEx context, Map<String,String> parameters) { ExtLibUtil.pushParameters(context, parameters); } // ======================================================== // FacesComponent implementation // ======================================================== public boolean isAutoCreate() { return true; } public void buildContents(FacesContext context, FacesComponentBuilder builder) throws FacesException { if(isAutoCreate()) { createContent((FacesContextEx)context); } } public void initAfterContents(FacesContext context) throws FacesException { } public void initBeforeContents(FacesContext context) throws FacesException { } // ======================================================== // Creation of the components // ======================================================== public boolean isContentCreated() { return getChildCount()>0; } public void createContent(FacesContextEx context) { if(isDynamicContent()) { // First, delete the existing content if(isContentCreated()) { deleteContent(context); } // Find if resources had been added to the view root by CC List<Resource> resources = ((UIViewRootEx)context.getViewRoot()).getResources(); int rc = resources.size(); if(onBeforeContent(context)) { return; } // And then create the children createChildren(context); // Finally, apply the styles DynamicUIUtil.applyStyleKit(context,this); // And update the views, if necessary ViewStateBean.get().initFromState(); onAfterContent(context); if(AbstractDynamicContent.USE_DYNAMIC_RESOURCES) { int newrc = resources.size(); if(newrc>rc) { // Some resources had been added - remove them if they are duplicated for(int i=newrc-1; i>=rc; i--) { removeIfDuplicated(resources, rc, i); } // If some are left, then we should add them if(resources.size()>rc) { TypedUtil.getRequestMap(context.getExternalContext()).put(DYNAMIC_RESOURCES, rc); } } } } } protected void removeIfDuplicated(List<Resource> resources, int rc, int idx) { Resource added = resources.get(idx); for(int i=0; i<rc; i++) { Resource r = resources.get(i); if(equals(r,added)) { resources.remove(idx); return; } } if( ExtlibControlsLogger.CONTROLS.isTraceDebugEnabled() ){ ExtlibControlsLogger.CONTROLS.traceDebugp(this, "removeIfDuplicated", //$NON-NLS-1$ StringUtil.format("Added resource #{0}:{1}",idx,added.getClass()) ); //$NON-NLS-1$ } } protected boolean equals(Resource r1, Resource r2) { // TODO this is re-evaluating the resource property value-bindings too often // see ScriptResourceRenderer computing: String id = "resource_"+ // for an example of computing a unique ID and comparing to that Class<?> c = r1.getClass(); if(c!=r2.getClass()) { return false; } if(c==ScriptResource.class) { return equals((ScriptResource)r1,(ScriptResource)r2); } if(c==StyleSheetResource.class) { return equals((StyleSheetResource)r1,(StyleSheetResource)r2); } if(c==DojoModuleResource.class) { return equals((DojoModuleResource)r1,(DojoModuleResource)r2); } if(c==DojoModulePathResource.class) { return equals((DojoModulePathResource)r1,(DojoModulePathResource)r2); } return false; } protected boolean equals(ScriptResource r1, ScriptResource r2) { return r1.isClientSide()==r2.isClientSide() && StringUtil.equals(r1.getType(), r2.getType()) && StringUtil.equals(r1.getSrc(), r2.getSrc()) && StringUtil.equals(r1.getCharset(), r2.getCharset()) && equals(r1.getAttributes(),r2.getAttributes()) ; } protected boolean equals(Map<String,String> m1, Map<String,String> m2) { int m1c = m1!=null ? m1.size() : 0; int m2c = m2!=null? m2.size() : 0; if(m1c==0 && m2c==0) { return true; } if(m1c==m2c) { return m1.equals(m2); } return false; } protected boolean equals(StyleSheetResource r1, StyleSheetResource r2) { return StringUtil.equals(r1.getHref(), r2.getHref()) && StringUtil.equals(r1.getContents(), r2.getCharset()) ; } protected boolean equals(DojoModuleResource r1, DojoModuleResource r2) { return StringUtil.equals(r1.getName(), r2.getName()) && StringUtil.equals(r1.getCondition(), r2.getCondition()) ; } protected boolean equals(DojoModulePathResource r1, DojoModulePathResource r2) { return StringUtil.equals(r1.getPrefix(), r2.getPrefix()) && StringUtil.equals(r1.getUrl(), r2.getUrl()) ; } protected boolean onBeforeContent(FacesContext context) { MethodBinding beforeContent = getBeforeContentLoad(); if(beforeContent!=null) { return FacesUtil.isCancelled(beforeContent.invoke(context, null)); } return false; } protected MethodBinding getBeforeContentLoad() { return null; } protected void onAfterContent(FacesContext context) { MethodBinding afterContent = getAfterContentLoad(); if (afterContent != null) { afterContent.invoke(context, null); } } protected MethodBinding getAfterContentLoad() { return null; } protected void deleteContent(FacesContextEx context) { if(isDynamicContent()) { if(isContentCreated()) { // Clean up the children cleanupChildren(context); // And remove them from the hierarchy DynamicUIUtil.removeChildren(this,true); } } } protected void cleanupChildren(FacesContext context) { // Nothing to do... } public abstract void createChildren(FacesContextEx context); }