/* * � 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.dojo.layout; import java.io.IOException; import java.util.List; import java.util.Map; import javax.faces.FacesException; import javax.faces.component.NamingContainer; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.el.ValueBinding; import com.ibm.commons.util.StringUtil; import com.ibm.commons.util.io.json.JsonGenerator; import com.ibm.commons.util.io.json.JsonJavaFactory; import com.ibm.commons.util.io.json.JsonJavaObject; import com.ibm.xsp.FacesExceptionEx; import com.ibm.xsp.ajax.AjaxUtil; import com.ibm.xsp.component.FacesAjaxComponent; import com.ibm.xsp.component.FacesComponent; import com.ibm.xsp.context.FacesContextEx; import com.ibm.xsp.extlib.component.dynamiccontent.FacesDynamicContainer; 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.JavaScriptUtil; import com.ibm.xsp.util.TypedUtil; /** * Dojo Tab Container. * * @author Philippe Riand */ public class UIDojoTabContainer extends UIDojoStackContainer implements FacesComponent, FacesAjaxComponent, NamingContainer, FacesDynamicContainer { public static final String DEFAULT_DOJO_TYPE = "extlib.dijit.TabContainer"; // $NON-NLS-1$ public static final String XSPCONTENT_PARAM = "content"; // $NON-NLS-1$ public static final String XSPTABTITLE_PARAM = "tabTitle"; // $NON-NLS-1$ public static final String XSPTABUNIQUEKEY_PARAM = "tabUniqueKey"; // $NON-NLS-1$ public static final String RENDERER_TYPE = "com.ibm.xsp.extlib.dojo.layout.TabContainer"; //$NON-NLS-1$ private int tabNextIndex; private String tabPosition; private Boolean tabStrip; private Boolean useMenu; private Boolean useSlider; // XPages specific private String defaultTabContent; // FacesDynamicContainer private String sourcePageName; // Temporary holder for the newly created tab private transient UIDojoTabContainer _constructingParentContainer; private transient UIDojoTabPane _constructedTabPane; public static class Action { private UIDojoTabContainer container; private UIDojoTabPane pane; private String refreshId; private Map<String,Object> refreshParams; Action(FacesContext context, UIDojoTabContainer container, UIDojoTabPane pane, String refreshId, Map<String,Object> refreshParams) { this.container = container; this.pane = pane; this.refreshId = refreshId; this.refreshParams = refreshParams; } public String generateClientScript() { FacesContext context = FacesContext.getCurrentInstance(); StringBuilder b = new StringBuilder(); // TabContainer.js has: // _removeTab: function(id,refreshId,params) b.append("dijit.byId("); // $NON-NLS-1$ JavaScriptUtil.addString(b, container.getClientId(context)); b.append(")._removeTab("); // $NON-NLS-1$ JavaScriptUtil.addString(b, pane.getClientId(context)); String rid=ExtLibUtil.getClientId(context,container,refreshId,true); if(StringUtil.isNotEmpty(rid)) { b.append(","); JavaScriptUtil.addString(b, rid); Object params = refreshParams; if(params!=null) { b.append(",{"); try { String json = JsonGenerator.toJson(JsonJavaFactory.instance,params,true); b.append(json); } catch(Exception ex) { throw new FacesExceptionEx(ex); } b.append("}"); } } b.append(");"); if(b.length()>0) { String script = b.toString(); return script; } return null; } } public UIDojoTabContainer() { setRendererType(RENDERER_TYPE); } public String getTabPosition() { if (null != this.tabPosition) { return this.tabPosition; } ValueBinding _vb = getValueBinding("tabPosition"); //$NON-NLS-1$ if (_vb != null) { return (java.lang.String) _vb.getValue(FacesContext.getCurrentInstance()); } else { return null; } } public void setTabPosition(String tabPosition) { this.tabPosition = tabPosition; } public boolean isTabStrip() { if (null != this.tabStrip) { return this.tabStrip; } ValueBinding _vb = getValueBinding("tabStrip"); //$NON-NLS-1$ if (_vb != null) { Boolean val = (java.lang.Boolean) _vb.getValue(FacesContext.getCurrentInstance()); if(val!=null) { return val; } } return false; } public void setTabStrip(boolean tabStrip) { this.tabStrip = tabStrip; } public boolean isUseMenu() { if (null != this.useMenu) { return this.useMenu; } ValueBinding _vb = getValueBinding("useMenu"); //$NON-NLS-1$ if (_vb != null) { Boolean val = (java.lang.Boolean) _vb.getValue(FacesContext.getCurrentInstance()); if(val!=null) { return val; } } return true; } public void setUseMenu(boolean useMenu) { this.useMenu = useMenu; } public boolean isUseSlider() { if (null != this.useSlider) { return this.useSlider; } ValueBinding _vb = getValueBinding("useSlider"); //$NON-NLS-1$ if (_vb != null) { Boolean val = (java.lang.Boolean) _vb.getValue(FacesContext.getCurrentInstance()); if(val!=null) { return val; } } return true; } public void setUseSlider(boolean useSlider) { this.useSlider = useSlider; } public String getDefaultTabContent() { if (null != this.defaultTabContent) { return this.defaultTabContent; } ValueBinding _vb = getValueBinding("defaultTabContent"); //$NON-NLS-1$ if (_vb != null) { return (java.lang.String) _vb.getValue(FacesContext.getCurrentInstance()); } else { return null; } } public void setDefaultTabContent(String defaultTabContent) { this.defaultTabContent = defaultTabContent; } // State management @Override public void restoreState(FacesContext _context, Object _state) { Object _values[] = (Object[]) _state; super.restoreState(_context, _values[0]); this.tabNextIndex = (Integer)_values[1]; this.tabPosition = (String)_values[2]; this.tabStrip = (Boolean)_values[3]; this.useMenu = (Boolean)_values[4]; this.useSlider = (Boolean)_values[5]; this.defaultTabContent = (String)_values[6]; this.sourcePageName = (String)_values[7]; } @Override public Object saveState(FacesContext _context) { Object _values[] = new Object[8]; _values[0] = super.saveState(_context); _values[1] = tabNextIndex; _values[2] = tabPosition; _values[3] = tabStrip; _values[4] = useMenu; _values[5] = useSlider; _values[6] = defaultTabContent; _values[7] = sourcePageName; return _values; } // ============================================================= // Dynamic tab management // ============================================================= public void initBeforeContents(FacesContext context) throws FacesException { } public void initAfterContents(FacesContext context) throws FacesException { } public void buildContents(FacesContext context, FacesComponentBuilder builder) throws FacesException { // 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. _constructingParentContainer = (UIDojoTabContainer)DynamicUIUtil.getDynamicallyConstructedComponent(context); DynamicUIUtil.setDynamicallyConstructing(context, null); try { buildTab(context, builder); return; } finally { DynamicUIUtil.setDynamicallyConstructing(context, _constructingParentContainer); } } // Find the source page name for this component from the closest source sourcePageName = DynamicUIUtil.getSourcePageName(builder); // Else, do not build the facets, only the children builder.buildChildren(context, this); } public String getSourcePageName() { return sourcePageName; } protected void buildTab(FacesContext context, FacesComponentBuilder builder) throws FacesException { // Build the facet if passed as a parameter String xspFacet = ExtLibUtil.readParameter(context,XSPCONTENT_PARAM); if(StringUtil.isEmpty(xspFacet)) { // Get the default facet xspFacet = getDefaultTabContent(); } buildFacetAsTab(context, builder, xspFacet); } @SuppressWarnings("unchecked") // $NON-NLS-1$ protected void buildFacetAsTab(FacesContext context, FacesComponentBuilder builder, String xspFacet) throws FacesException { // If there is a facet, construct it if(StringUtil.isNotEmpty(xspFacet)) { builder.buildFacet(context, this, xspFacet); // And make its content the children UIComponent c = (UIComponent)getFacets().get(xspFacet); if(c instanceof UIDojoTabPane) { UIDojoTabPane pane = (UIDojoTabPane)c; _constructingParentContainer._constructedTabPane = pane; getChildren().add(c); } return; } } public UIDojoTabPane createTab() { return createTab(null); } public UIDojoTabPane createTab(Map<String,String> parameters) { FacesContextEx context = FacesContextEx.getCurrentInstance(); try { // Look if there is already a tab with this key // If we have it, then make it the selected one if(parameters!=null) { String uniqueKey = parameters.get(XSPTABUNIQUEKEY_PARAM); if(StringUtil.isNotEmpty(uniqueKey)) { for(UIComponent c: TypedUtil.getChildren(this)) { if(c instanceof UIDojoTabPane) { UIDojoTabPane pane = (UIDojoTabPane)c; if(StringUtil.equals(pane.getTabUniqueKey(), uniqueKey)) { if( pane.isDelayedRemoveTab() ){ // remove the old closed tab before creating // a new tab with the same key. pane.delayedRemove(); }else{ // switch to the existing tab setSelectedTab(pane.getTabUniqueKey()); return null; } } } } } } ExtLibUtil.pushParameters(context, parameters); // Create the new tab DynamicUIUtil.createChildren(context,this,getId()); if(_constructedTabPane!=null) { // Create a new id to the tab, to avoid any collision with its future parent if(StringUtil.isEmpty(_constructedTabPane.getTabUniqueKey())) { _constructedTabPane.setUniqueTabIndex(++tabNextIndex); } // Add the parameters from the context String tabTitle = ExtLibUtil.readParameter(context,XSPTABTITLE_PARAM); if(StringUtil.isNotEmpty(tabTitle)) { _constructedTabPane.setTitle(tabTitle); } // Finally, apply the styles DynamicUIUtil.applyStyleKit(context,_constructedTabPane); // And make it the selected tab setSelectedTab(_constructedTabPane.getTabUniqueKey()); } return _constructedTabPane; } finally { _constructingParentContainer = null; _constructedTabPane = null; } } // ============================================================= // AJAX commands // ============================================================= public boolean handles(FacesContext context) { // no path info handling - only client id based ones return false; } public void processAjaxRequest(FacesContext context) throws IOException { // Note, the errorCode is never written to the response header // because this method always gives 200 OK. //int errorCode = 200; // OK StringBuilder b = new StringBuilder(); // Find the command Map<String, String> params = TypedUtil.getRequestParameterMap(context.getExternalContext()); // Create a new tab String command = params.get("_action"); // $NON-NLS-1$ if(StringUtil.equals(command, "createTab")) { // $NON-NLS-1$ // errorCode = axCreateTab(context, b, params); } // Return the Javascript snippet // TODO: add the header... AjaxUtil.initRender(context); ResponseWriter writer = context.getResponseWriter(); writer.write(b.toString()); } @SuppressWarnings("rawtypes") //$NON-NLS-1$ protected int axCreateTab(FacesContext context, StringBuilder b, Map params) throws IOException { int errorCode = 200; // OK // Create the new tab in the JSF tree UIDojoTabPane pane = createTab(); if(pane!=null) { JsonJavaObject json = new JsonJavaObject(); String id = pane.getClientId(context); if(id!=null) { json.putString("id", id); // $NON-NLS-1$ } try { // append {id="view:...:tabPane1"} to b JsonGenerator.toJson(JsonJavaFactory.instance, b, json, true); } catch(Exception ex) {} ExtLibUtil.saveViewState(context); } return errorCode; } @Override public void _xspCleanTransientData() { super._xspCleanTransientData(); delayedRemoveChildren(); } /* (non-Javadoc) * @see javax.faces.component.UIComponentBase#processRestoreState(javax.faces.context.FacesContext, java.lang.Object) */ @Override public void processRestoreState(FacesContext context, Object state) { super.processRestoreState(context, state); // TODO shouldn't need this here as _xspCleanTransientData should have occurred, but persist mode file is not cleaning. // Note if the control tree is being serialized containing delayed remove children // there's no opportunity in the saveState phase to delete the child, // because the control tree structure is saved before the individual control states // and changing the child count would lead to invalid array indexes in the control state trees. // However, here, after the children's structure and state have been restored, // here there's an opportunity to remove the delayed-remove children. // Note, if the clean starts to occur then the tabPane // will no longer need to serialize the delayedRemoveTab boolean. delayedRemoveChildren(); } private void delayedRemoveChildren() { if( this.getChildCount() > 0 ){ List<UIComponent> kids = TypedUtil.getChildren(this); // give the child an opportunity to remove itself from the children list. // note getChildCount will be changing. for (int i = 0; i < this.getChildCount(); i++) { UIComponent child = kids.get(i); if(child instanceof UIDojoTabPane){ UIDojoTabPane pane = (UIDojoTabPane) child; if( pane.isDelayedRemoveTab() ){ pane.delayedRemove(); if( (i < this.getChildCount()) && pane != kids.get(i) ){ i--; } } }// else probably xp:eventHandler } } } }