/* * Copyright 2011 PrimeFaces Extensions. * * 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. * * $Id: MasterDetailRenderer.java 555 2011-12-08 20:52:00Z Zoigln@googlemail.com $ */ package org.primefaces.extensions.component.masterdetail; import java.io.IOException; import java.util.List; import java.util.Map; import javax.faces.FacesException; import javax.faces.component.EditableValueHolder; import javax.faces.component.UIComponent; import javax.faces.component.UIParameter; import javax.faces.component.visit.VisitContext; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import org.apache.commons.lang.StringUtils; import org.primefaces.component.breadcrumb.BreadCrumb; import org.primefaces.component.menuitem.MenuItem; import org.primefaces.extensions.component.reseteditablevalues.EditableValueHoldersVisitCallback; import org.primefaces.model.DefaultMenuModel; import org.primefaces.model.MenuModel; import org.primefaces.renderkit.CoreRenderer; /** * Renderer for the {@link MasterDetail} component. * * @author Oleg Varaksin / last modified by $Author: Zoigln@googlemail.com $ * @version $Revision: 555 $ */ public class MasterDetailRenderer extends CoreRenderer { private static final String FACET_HEADER = "header"; private static final String FACET_FOOTER = "footer"; @Override public void encodeEnd(final FacesContext fc, UIComponent component) throws IOException { MasterDetail masterDetail = (MasterDetail) component; MasterDetailLevel mdl; if (masterDetail.isSelectDetailRequest(fc)) { // component has been navigated via SelectDetailLevel MasterDetailLevel mdlToProcess = masterDetail.getDetailLevelToProcess(fc); if (fc.isValidationFailed()) { mdl = mdlToProcess; } else { mdl = masterDetail.getDetailLevelToGo(fc); boolean changeLevelAllowed = true; if (masterDetail.getFlowListener() != null) { FlowLevelEvent flowLevelEvent = new FlowLevelEvent(masterDetail, mdlToProcess.getLevel(), mdl.getLevel()); changeLevelAllowed = (Boolean) masterDetail.getFlowListener().invoke(fc.getELContext(), new Object[] {flowLevelEvent}); if (!changeLevelAllowed) { mdl = mdlToProcess; } } if (changeLevelAllowed) { // reset last saved validation state and stored values of editable components EditableValueHoldersVisitCallback visitCallback = new EditableValueHoldersVisitCallback(); mdlToProcess.visitTree(VisitContext.createVisitContext(fc), visitCallback); final List<EditableValueHolder> editableValueHolders = visitCallback.getEditableValueHolders(); for (EditableValueHolder editableValueHolder : editableValueHolders) { editableValueHolder.resetValue(); } } } masterDetail.updateModel(fc, mdl); } else { // component has been navigated from outside, e.g. GET request or POST update from another component mdl = masterDetail.getDetailLevelByLevel(masterDetail.getLevel()); } // render MasterDetailLevel encodeMarkup(fc, masterDetail, mdl); // reset calculated values masterDetail.resetCalculatedValues(); } protected void encodeMarkup(final FacesContext fc, final MasterDetail masterDetail, final MasterDetailLevel mdl) throws IOException { ResponseWriter writer = fc.getResponseWriter(); String clientId = masterDetail.getClientId(fc); String styleClass = masterDetail.getStyleClass() == null ? "ui-master-detail" : "ui-master-detail " + masterDetail.getStyleClass(); writer.startElement("div", masterDetail); writer.writeAttribute("id", clientId, "id"); writer.writeAttribute("class", styleClass, "styleClass"); if (masterDetail.getStyle() != null) { writer.writeAttribute("style", masterDetail.getStyle(), "style"); } // render header encodeFacet(fc, masterDetail, FACET_HEADER); if (masterDetail.isShowBreadcrumb()) { // get breadcrumb and its current model BreadCrumb breadcrumb = getBreadcrumb(masterDetail); if (breadcrumb == null) { throw new FacesException("BreadCrumb component was not found below MasterDetail."); } MenuModel model = buildBreadcrumbModel(fc, masterDetail, mdl); // remove not up-to-date children int kidsCount = breadcrumb.getChildCount(); for (int i = 0; i < kidsCount; i++) { breadcrumb.getChildren().remove(0); } // add new children for (UIComponent kid : model.getMenuItems()) { breadcrumb.getChildren().add(kid); } // render breadcrumb breadcrumb.encodeAll(fc); } // render container for MasterDetailLevel writer.startElement("div", null); writer.writeAttribute("id", clientId + "_detaillevel", "id"); writer.writeAttribute("class", "ui-master-detail-level", null); // try to get context value if contextVar exists Object contextValue = null; String contextVar = mdl.getContextVar(); if (StringUtils.isNotBlank(contextVar)) { contextValue = masterDetail.getContextValueFromFlow(fc, mdl); } if (contextValue != null) { Map<String, Object> requestMap = fc.getExternalContext().getRequestMap(); requestMap.put(contextVar, contextValue); } // render MasterDetailLevel mdl.encodeAll(fc); if (contextValue != null) { fc.getExternalContext().getRequestMap().remove(contextVar); } writer.endElement("div"); // render footer encodeFacet(fc, masterDetail, FACET_FOOTER); writer.endElement("div"); } protected void encodeFacet(final FacesContext fc, final UIComponent component, final String name) throws IOException { final UIComponent facet = component.getFacet(name); if (facet != null) { facet.encodeAll(fc); } } protected MenuModel buildBreadcrumbModel(final FacesContext fc, final MasterDetail masterDetail, final MasterDetailLevel mdlToRender) { // create model from scratch MenuModel model = new DefaultMenuModel(); for (UIComponent child : masterDetail.getChildren()) { if (child instanceof MasterDetailLevel) { MasterDetailLevel mdl = (MasterDetailLevel) child; // create a new menu item and add to the model if (child.isRendered()) { MenuItem menuItem = createMenuItem(fc, masterDetail, mdl, masterDetail.getContextValueFromFlow(fc, mdl), mdlToRender.getLevel()); model.addMenuItem(menuItem); } if (mdl.getLevel() == mdlToRender.getLevel()) { break; } } } return model; } protected MenuItem createMenuItem(final FacesContext fc, final MasterDetail masterDetail, final MasterDetailLevel mdl, final Object contextValue, final int currentLevel) { String clientId = masterDetail.getClientId(fc); MenuItem menuItem = new MenuItem(); menuItem.setId(masterDetail.getId() + "_bcItem_" + mdl.getLevel()); String contextVar = mdl.getContextVar(); boolean putContext = (StringUtils.isNotBlank(contextVar) && contextValue != null); if (putContext) { Map<String, Object> requestMap = fc.getExternalContext().getRequestMap(); requestMap.put(contextVar, contextValue); } menuItem.setValue(mdl.getLevelLabel()); menuItem.setDisabled(mdl.isLevelDisabled()); if (putContext) { fc.getExternalContext().getRequestMap().remove(contextVar); } menuItem.setAjax(true); menuItem.setImmediate(true); menuItem.setProcess("@none"); menuItem.setUpdate(null); final String menuItemId = menuItem.getId(); UIParameter uiParameter = new UIParameter(); uiParameter.setId(menuItemId + "_sdr"); uiParameter.setName(clientId + MasterDetail.SELECT_DETAIL_REQUEST); uiParameter.setValue(true); menuItem.getChildren().add(uiParameter); uiParameter = new UIParameter(); uiParameter.setId(menuItemId + "_cl"); uiParameter.setName(clientId + MasterDetail.CURRENT_LEVEL); uiParameter.setValue(currentLevel); menuItem.getChildren().add(uiParameter); uiParameter = new UIParameter(); uiParameter.setId(menuItemId + "_sl"); uiParameter.setName(clientId + MasterDetail.SELECTED_LEVEL); uiParameter.setValue(mdl.getLevel()); menuItem.getChildren().add(uiParameter); uiParameter = new UIParameter(); uiParameter.setId(menuItemId + "_sp"); uiParameter.setName(clientId + MasterDetail.SKIP_PROCESSING_REQUEST); uiParameter.setValue(true); menuItem.getChildren().add(uiParameter); return menuItem; } protected BreadCrumb getBreadcrumb(final MasterDetail masterDetail) { for (UIComponent child : masterDetail.getChildren()) { if (child instanceof BreadCrumb) { return (BreadCrumb) child; } } return null; } @Override public void encodeChildren(final FacesContext fc, UIComponent component) throws IOException { // rendering happens on encodeEnd } @Override public boolean getRendersChildren() { return true; } }