All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.primefaces.extensions.component.masterdetail.MasterDetailRenderer Maven / Gradle / Ivy

/*
 * Copyright 2011-2015 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$
 */

package org.primefaces.extensions.component.masterdetail;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.visit.VisitContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import org.primefaces.component.breadcrumb.BreadCrumb;
import org.primefaces.extensions.util.ComponentUtils;
import org.primefaces.extensions.util.FastStringWriter;
import org.primefaces.model.menu.DefaultMenuItem;
import org.primefaces.model.menu.MenuElement;
import org.primefaces.model.menu.MenuItem;
import org.primefaces.renderkit.CoreRenderer;

/**
 * Renderer for the {@link MasterDetail} component.
 *
 * @author  Oleg Varaksin / last modified by $Author$
 * @version $Revision$
 * @since   0.2
 */
public class MasterDetailRenderer extends CoreRenderer {

	private static final String FACET_HEADER = "header";
	private static final String FACET_FOOTER = "footer";
	private static final String FACET_LABEL = "label";

	@Override
	public void encodeEnd(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 = getDetailLevelToEncode(fc, masterDetail, mdlToProcess, masterDetail.getDetailLevelToGo(fc));

				// reset last saved validation state and stored values of editable components
				MasterDetailLevelVisitCallback visitCallback = new MasterDetailLevelVisitCallback();
				mdlToProcess.visitTree(VisitContext.createVisitContext(fc), visitCallback);

				String preserveInputs = masterDetail.getPreserveInputs(fc);
				String resetInputs = masterDetail.getResetInputs(fc);
				String[] piIds = preserveInputs != null ? preserveInputs.split("[\\s,]+") : null;
				String[] riIds = resetInputs != null ? resetInputs.split("[\\s,]+") : null;
				boolean preserveAll = ArrayUtils.contains(piIds, "@all");
				boolean resetAll = ArrayUtils.contains(riIds, "@all");

				final List editableValueHolders = visitCallback.getEditableValueHolders();
				for (EditableValueHolder editableValueHolder : editableValueHolders) {
					String clientId = ((UIComponent) editableValueHolder).getClientId(fc);
					if (resetAll || (ArrayUtils.contains(riIds, clientId))) {
						editableValueHolder.resetValue();
					} else if (preserveAll || (ArrayUtils.contains(piIds, clientId))) {
						editableValueHolder.setValue(ComponentUtils.getConvertedSubmittedValue(fc, editableValueHolder));
					} else {
						// default behavior
						editableValueHolder.resetValue();
					}
				}
			}

			masterDetail.updateModel(fc, mdl);
		} else {
			// component has been navigated from the 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 MasterDetailLevel getDetailLevelToEncode(FacesContext fc, MasterDetail masterDetail, MasterDetailLevel mdlToProcess,
	                                                   MasterDetailLevel mdlToGo) {
		if (masterDetail.getSelectLevelListener() != null) {
			SelectLevelEvent selectLevelEvent = new SelectLevelEvent(masterDetail, mdlToProcess.getLevel(), mdlToGo.getLevel());
			int levelToEncode =
			    (Integer) masterDetail.getSelectLevelListener().invoke(fc.getELContext(), new Object[] {selectLevelEvent});
			if (levelToEncode != mdlToGo.getLevel()) {
				// new MasterDetailLevel to go
				return masterDetail.getDetailLevelByLevel(levelToEncode);
			}
		}

		return mdlToGo;
	}

	protected void encodeMarkup(FacesContext fc, MasterDetail masterDetail, MasterDetailLevel mdl) throws IOException {
		ResponseWriter writer = fc.getResponseWriter();
		String clientId = masterDetail.getClientId(fc);
		String styleClass =
		    masterDetail.getStyleClass() == null ? "pe-master-detail" : "pe-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");
		}

		if (masterDetail.isShowBreadcrumb()) {
			if (masterDetail.isBreadcrumbAboveHeader()) {
				// render breadcrumb and then header
				renderBreadcrumb(fc, masterDetail, mdl);
				encodeFacet(fc, masterDetail, FACET_HEADER);
			} else {
				// render header and then breadcrumb
				encodeFacet(fc, masterDetail, FACET_HEADER);
				renderBreadcrumb(fc, masterDetail, mdl);
			}
		} else {
			// render header without breadcrumb
			encodeFacet(fc, masterDetail, FACET_HEADER);
		}

		// render container for MasterDetailLevel
		writer.startElement("div", null);
		writer.writeAttribute("id", clientId + "_detaillevel", "id");
		writer.writeAttribute("class", "pe-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, true);
		}

		if (contextValue != null) {
			Map 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 renderBreadcrumb(FacesContext fc, MasterDetail masterDetail, MasterDetailLevel mdl) throws IOException {
		// get breadcrumb and its current model
		BreadCrumb breadcrumb = masterDetail.getBreadcrumb();

		// update breadcrumb items
		updateBreadcrumb(fc, breadcrumb, masterDetail, mdl);

		// render breadcrumb
		breadcrumb.encodeAll(fc);
	}

	protected void encodeFacet(FacesContext fc, UIComponent component, String name) throws IOException {
		final UIComponent facet = component.getFacet(name);
		if (facet != null) {
			facet.encodeAll(fc);
		}
	}

	protected void updateBreadcrumb(FacesContext fc, BreadCrumb breadcrumb, MasterDetail masterDetail,
	                                MasterDetailLevel mdlToRender) throws IOException {
		boolean lastMdlFound = false;
		int levelToRender = mdlToRender.getLevel();
		boolean isShowAllBreadcrumbItems = masterDetail.isShowAllBreadcrumbItems();

		for (UIComponent child : masterDetail.getChildren()) {
			if (child instanceof MasterDetailLevel) {
				MasterDetailLevel mdl = (MasterDetailLevel) child;
				DefaultMenuItem menuItem = getMenuItemByLevel(breadcrumb, masterDetail, mdl);
				if (menuItem == null) {
					// note: don't throw exception because menuItem can be null when MasterDetail is within DataTable
					//throw new FacesException("MenuItem to master detail level " + mdl.getLevel() + " was not found");
					return;
				}

				if (!child.isRendered()) {
					menuItem.setRendered(false);
					if (!lastMdlFound) {
						lastMdlFound = mdl.getLevel() == mdlToRender.getLevel();
					}

					continue;
				}

				if (lastMdlFound && !isShowAllBreadcrumbItems) {
					menuItem.setRendered(false);
				} else {
					menuItem.setRendered(true);

					Object contextValue = masterDetail.getContextValueFromFlow(fc, mdl, mdl.getLevel() == mdlToRender.getLevel());
					String contextVar = mdl.getContextVar();
					boolean putContext = (StringUtils.isNotBlank(contextVar) && contextValue != null);

					if (putContext) {
						Map requestMap = fc.getExternalContext().getRequestMap();
						requestMap.put(contextVar, contextValue);
					}

					final UIComponent facet = mdl.getFacet(FACET_LABEL);
					if (facet != null) {
						// swap writers
						ResponseWriter writer = fc.getResponseWriter();
						FastStringWriter fsw = new FastStringWriter();
						ResponseWriter clonedWriter = writer.cloneWithWriter(fsw);
						fc.setResponseWriter(clonedWriter);

						// render facet's children
						facet.encodeAll(fc);

						// restore the original writer
						fc.setResponseWriter(writer);

						// set menuitem label from facet
						menuItem.setValue(fsw.toString());
					} else {
						// set menuitem label from tag attribute
						menuItem.setValue(mdl.getLevelLabel());
					}

					if (isShowAllBreadcrumbItems && lastMdlFound) {
						menuItem.setDisabled(true);
					} else {
						menuItem.setDisabled(mdl.isLevelDisabled());
					}

					if (putContext) {
						fc.getExternalContext().getRequestMap().remove(contextVar);
					}

					if (!menuItem.isDisabled()) {
						// set current level parameter
						updateUIParameter(menuItem, masterDetail.getClientId(fc) + MasterDetail.CURRENT_LEVEL, levelToRender);
					}
				}

				if (!lastMdlFound) {
					lastMdlFound = mdl.getLevel() == mdlToRender.getLevel();
				}
			}
		}
	}

	protected DefaultMenuItem getMenuItemByLevel(BreadCrumb breadcrumb, MasterDetail masterDetail, MasterDetailLevel mdl) {
		String menuItemId = masterDetail.getId() + "_bcItem_" + mdl.getLevel();
		for (MenuElement child : breadcrumb.getModel().getElements()) {
			if (menuItemId.equals(child.getId())) {
				return (DefaultMenuItem) child;
			}
		}

		return null;
	}

	protected void updateUIParameter(MenuItem menuItem, String name, Object value) {
		Map> params = menuItem.getParams();
		if (params == null) {
			return;
		}

		for (String key : params.keySet()) {
			if (key.equals(name)) {
				params.remove(key);
				menuItem.setParam(name, value);

				break;
			}
		}
	}

	@Override
	public void encodeChildren(FacesContext fc, UIComponent component) throws IOException {
		// rendering happens on encodeEnd
	}

	@Override
	public boolean getRendersChildren() {
		return true;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy