/*
* � 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.util;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import com.ibm.xsp.application.ViewHandlerEx;
import com.ibm.xsp.component.FacesPageIncluder;
import com.ibm.xsp.component.UIViewRootEx;
import com.ibm.xsp.context.FacesContextEx;
import com.ibm.xsp.extlib.builder.DynamicComponentFactory;
import com.ibm.xsp.extlib.component.dynamiccontent.FacesDynamicContainer;
import com.ibm.xsp.page.FacesComponentBuilder;
import com.ibm.xsp.page.FacesPage;
import com.ibm.xsp.page.FacesPageDispatcher;
import com.ibm.xsp.page.FacesPageDriver;
import com.ibm.xsp.page.compiled.CompiledComponentBuilder;
import com.ibm.xsp.page.compiled.CompiledViewPage;
import com.ibm.xsp.stylekit.StyleKit;
import com.ibm.xsp.util.TypedUtil;
/**
* Utility methods for managing dynamic XPages UI.
*/
public class DynamicUIUtil {
public interface IDynamicContainer {
public void addDynamicChild(UIComponent c);
}
// ================================================================
// Children collection utilities
// ================================================================
/**
* Remove all the children from a component.
* @param component the parent component
* @param facets indicates if the facets should be removed as well
*/
public static void removeChildren(UIComponent component, boolean facets) {
if(component.getChildCount()>0) {
TypedUtil.getChildren(component).clear();
}
if(component.getFacetCount()>0) {
TypedUtil.getFacets(component).clear();
}
}
/**
* Move all the children from one component to another.
* @param from the component containing the children
* @param to the destination component
* @param facets indicates if the facets should be moved as well
*/
public static void moveChildren(UIComponent from, UIComponent to, boolean facets) {
if(from.getChildCount()>0) {
if(to instanceof IDynamicContainer) {
IDynamicContainer c = (IDynamicContainer)to;
List<UIComponent> fromChildren = TypedUtil.getChildren(from);
while (from.getChildCount() > 0) {
c.addDynamicChild(fromChildren.get(0));
}
} else {
List<UIComponent> toChildren = TypedUtil.getChildren(to);
List<UIComponent> fromChildren = TypedUtil.getChildren(from);
while (from.getChildCount() > 0) {
toChildren.add(fromChildren.get(0));
}
}
}
if(from.getFacetCount()>0) {
Map<String,UIComponent> toFacets = TypedUtil.getFacets(to);
Map<String,UIComponent> fromFacets = TypedUtil.getFacets(from);
// not using facets.putAll, as there was an issue
// where the last control wouldn't get transferred.
Set<String> fromFacetNames = fromFacets.keySet();
while( ! fromFacets.isEmpty() ){
String facetName = fromFacetNames.iterator().next();
UIComponent facet = fromFacets.remove(facetName);
toFacets.put(facetName, facet);
}
}
}
// ================================================================
// Access to the XPages page driver
// ================================================================
/**
* Create the children of a component, based on its id.
* @param context
* @param rootComponent
* @param createId the JSF id of the component parent of the children
* @return
*/
public static void createChildren(FacesContextEx context, UIComponent rootComponent, String createId) {
setDynamicallyConstructing(context, rootComponent);
try {
// the driver to use to load the page
FacesPageDriver driver = findPageDriver(context, rootComponent);
// fix the issue with the builder that do not add the component early enough in the
// hierarchy. Just propagate here the fact that this component is disallowed to publish
// data while constructing.
// Load the current page
String pageName = DynamicUIUtil.getPageName(context, driver, rootComponent);
FacesPage page = loadPage(context, driver, pageName);
// Create the components that are children of this one, including a copy of this component
// Historically, the component id are stored in lower case...
page.addComponent(context, null, rootComponent, createId.toLowerCase());
// Now, remove the intermediate component
List<UIComponent> children = TypedUtil.getChildren(rootComponent);
if(!children.isEmpty()) {
UIComponent temp = (UIComponent)children.get(children.size()-1);
children.remove(temp);
// And move its children to the root component
moveChildren(temp, rootComponent, true);
}
} finally {
setDynamicallyConstructing(context, null);
}
}
public static FacesPageDriver findPageDriver(FacesContextEx context, UIComponent rootComponent) {
// Look if there is a control factory in the hierarchy
FacesPageDriver pd = findDynamicPageDriver(context, rootComponent);
if(pd!=null) {
return pd;
}
// Return the default compiled page driver assigned to the view handler
FacesPageDriver driver = findDefaultPageDriver(context);
if( null == driver ){
// Should not happen...
throw new IllegalStateException("Cannot find the current page driver"); // $NLX-DynamicUIUtil.Cannotfindthecurrentpagedriver-1$
}
return driver;
}
protected static FacesPageDriver findDynamicPageDriver(FacesContextEx context, UIComponent rootComponent) {
for(UIComponent c=rootComponent; c!=null; c=c.getParent()) {
if(c instanceof DynamicComponentFactory) {
FacesPageDriver pd = ((DynamicComponentFactory)c).getPageDriver();
return pd;
}
if(c instanceof FacesPageIncluder) {
return null; // Should use the default
}
}
return null;
}
public static FacesPageDriver findDefaultPageDriver(FacesContext context) {
ViewHandlerEx viewHandler = (ViewHandlerEx) context.getApplication().getViewHandler();
FacesPageDriver driver = viewHandler.getPageDriver();
return driver;
}
/**
* Loads the page using the page driver.
* @param context
* @param driver
* @return
*/
public static FacesPage loadPage(FacesContextEx context, FacesPageDriver driver, String pageName) {
FacesPageDispatcher dispatcher = driver.loadPage(context, pageName);
UIViewRoot viewRoot = context.getViewRoot(); // FacesUtil.getViewRoot(this);
FacesPage page = dispatcher.loadPage(context, viewRoot.getRenderKitId(), viewRoot.getLocale());
return page;
}
/**
* Gets the page name to load.
* @param context
* @param driver
* @return
*/
public static String getPageName(FacesContextEx context, FacesPageDriver driver, UIComponent rootComponent) {
// Find the page to which the root component belongs to
for( UIComponent c=rootComponent; c!=null; c=c.getParent()) {
if(c instanceof FacesDynamicContainer) {
return ((FacesDynamicContainer)c).getSourcePageName();
}
}
// Return the current page
UIViewRootEx v = (UIViewRootEx)context.getViewRoot();
return v.getPageName();
}
/**
* Return the source page for a partcular builder.
* @param builder the current builder
* @return the name of the source page
*/
public static String getSourcePageName(FacesComponentBuilder builder) {
// General case of the compiled builder (page edited within Designer)
if(builder instanceof CompiledComponentBuilder) {
CompiledComponentBuilder cb = (CompiledComponentBuilder)builder;
final CompiledViewPage pg = cb.getPage();
return pg.getSourcePageName();
}
// Dynamic UI builder, for other sources
if(builder instanceof DynamicComponentFactory) {
DynamicComponentFactory f = (DynamicComponentFactory)builder;
return f.getSourceComponentRef();
}
return null;
}
// ================================================================
// Theme management.
// ================================================================
public static void applyStyleKit(FacesContextEx context, UIComponent root) {
StyleKit kit = context.getStyleKit();
if(kit!=null) {
kit.applyStyles(context, root);
}
}
// ================================================================
// Context management.
// ================================================================
public static final String KEY_CONSTRUCTING = "_xsp.inplace.constructing"; // $NON-NLS-1$
public static boolean isDynamicallyConstructing(FacesContext context) {
ExternalContext ec = context.getExternalContext();
return ec.getRequestMap().containsKey(DynamicUIUtil.KEY_CONSTRUCTING);
}
public static UIComponent getDynamicallyConstructedComponent(FacesContext context) {
ExternalContext ec = context.getExternalContext();
return (UIComponent)ec.getRequestMap().get(DynamicUIUtil.KEY_CONSTRUCTING);
}
@SuppressWarnings("unchecked") // $NON-NLS-1$
public static void setDynamicallyConstructing(FacesContext context, UIComponent c) {
ExternalContext ec = context.getExternalContext();
if(c!=null) {
ec.getRequestMap().put(DynamicUIUtil.KEY_CONSTRUCTING,c);
} else {
ec.getRequestMap().remove(DynamicUIUtil.KEY_CONSTRUCTING);
}
}
}