Java tutorial
/** * Copyright 2011-2018 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. */ 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.visit.VisitContext; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.convert.Converter; import javax.faces.render.Renderer; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.primefaces.component.breadcrumb.BreadCrumb; import org.primefaces.model.menu.DefaultMenuItem; import org.primefaces.model.menu.MenuElement; import org.primefaces.model.menu.MenuItem; import org.primefaces.renderkit.CoreRenderer; import org.primefaces.util.ComponentUtils; import org.primefaces.util.FastStringWriter; /** * 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(final FacesContext fc, final UIComponent component) throws IOException { final MasterDetail masterDetail = (MasterDetail) component; MasterDetailLevel mdl; if (masterDetail.isSelectDetailRequest(fc)) { // component has been navigated via SelectDetailLevel final 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 final MasterDetailLevelVisitCallback visitCallback = new MasterDetailLevelVisitCallback(); mdlToProcess.visitTree(VisitContext.createVisitContext(fc), visitCallback); final String preserveInputs = masterDetail.getPreserveInputs(fc); final String resetInputs = masterDetail.getResetInputs(fc); final String[] piIds = preserveInputs != null ? preserveInputs.split("[\\s,]+") : null; final String[] riIds = resetInputs != null ? resetInputs.split("[\\s,]+") : null; final boolean preserveAll = ArrayUtils.contains(piIds, "@all"); final boolean resetAll = ArrayUtils.contains(riIds, "@all"); final List<EditableValueHolder> editableValueHolders = visitCallback.getEditableValueHolders(); for (final EditableValueHolder editableValueHolder : editableValueHolders) { final String clientId = ((UIComponent) editableValueHolder).getClientId(fc); if (resetAll || ArrayUtils.contains(riIds, clientId)) { editableValueHolder.resetValue(); } else if (preserveAll || ArrayUtils.contains(piIds, clientId)) { editableValueHolder.setValue(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()); } if (mdl == null) { throw new FacesException("MasterDetailLevel [Level=" + String.valueOf(masterDetail.getLevel()) + "] must be nested inside a MasterDetail component!"); } // render MasterDetailLevel encodeMarkup(fc, masterDetail, mdl); // reset calculated values masterDetail.resetCalculatedValues(); } protected MasterDetailLevel getDetailLevelToEncode(final FacesContext fc, final MasterDetail masterDetail, final MasterDetailLevel mdlToProcess, final MasterDetailLevel mdlToGo) { if (masterDetail.getSelectLevelListener() != null) { final SelectLevelEvent selectLevelEvent = new SelectLevelEvent(masterDetail, mdlToProcess.getLevel(), mdlToGo.getLevel()); final 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(final FacesContext fc, final MasterDetail masterDetail, final MasterDetailLevel mdl) throws IOException { if (mdl == null) { throw new FacesException("MasterDetailLevel must be nested inside a MasterDetail component!"); } final ResponseWriter writer = fc.getResponseWriter(); final String clientId = masterDetail.getClientId(fc); final 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; final String contextVar = mdl.getContextVar(); if (StringUtils.isNotBlank(contextVar)) { contextValue = masterDetail.getContextValueFromFlow(fc, mdl, true); } if (contextValue != null) { final 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 renderBreadcrumb(final FacesContext fc, final MasterDetail masterDetail, final MasterDetailLevel mdl) throws IOException { // get breadcrumb and its current model final BreadCrumb breadcrumb = masterDetail.getBreadcrumb(); // update breadcrumb items updateBreadcrumb(fc, breadcrumb, masterDetail, mdl); // render breadcrumb breadcrumb.encodeAll(fc); } 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 void updateBreadcrumb(final FacesContext fc, final BreadCrumb breadcrumb, final MasterDetail masterDetail, final MasterDetailLevel mdlToRender) throws IOException { boolean lastMdlFound = false; final int levelToRender = mdlToRender.getLevel(); final boolean isShowAllBreadcrumbItems = masterDetail.isShowAllBreadcrumbItems(); for (final UIComponent child : masterDetail.getChildren()) { if (child instanceof MasterDetailLevel) { final MasterDetailLevel mdl = (MasterDetailLevel) child; final 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); final Object contextValue = masterDetail.getContextValueFromFlow(fc, mdl, mdl.getLevel() == mdlToRender.getLevel()); final String contextVar = mdl.getContextVar(); final boolean putContext = StringUtils.isNotBlank(contextVar) && contextValue != null; if (putContext) { final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap(); requestMap.put(contextVar, contextValue); } final UIComponent facet = mdl.getFacet(FACET_LABEL); if (facet != null) { // swap writers final ResponseWriter writer = fc.getResponseWriter(); final FastStringWriter fsw = new FastStringWriter(); final 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(StringEscapeUtils.unescapeHtml4(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(final BreadCrumb breadcrumb, final MasterDetail masterDetail, final MasterDetailLevel mdl) { final String menuItemId = masterDetail.getId() + "_bcItem_" + mdl.getLevel(); for (final MenuElement child : breadcrumb.getModel().getElements()) { if (menuItemId.equals(child.getId())) { return (DefaultMenuItem) child; } } return null; } protected void updateUIParameter(final MenuItem menuItem, final String name, final Object value) { final Map<String, List<String>> params = menuItem.getParams(); if (params == null) { return; } for (final String key : params.keySet()) { if (key.equals(name)) { params.remove(key); menuItem.setParam(name, value); break; } } } @Override public void encodeChildren(final FacesContext fc, final UIComponent component) throws IOException { // rendering happens on encodeEnd } @Override public boolean getRendersChildren() { return true; } public static Object getConvertedSubmittedValue(final FacesContext fc, final EditableValueHolder evh) { final Object submittedValue = evh.getSubmittedValue(); if (submittedValue == null) { return null; } try { final UIComponent component = (UIComponent) evh; final Renderer renderer = getRenderer(fc, component); if (renderer != null) { // convert submitted value by renderer return renderer.getConvertedValue(fc, component, submittedValue); } else if (submittedValue instanceof String) { // convert submitted value by registred (implicit or explicit) // converter final Converter converter = ComponentUtils.getConverter(fc, component); if (converter != null) { return converter.getAsObject(fc, component, (String) submittedValue); } } } catch (final Exception e) { // an conversion error occured return null; } return submittedValue; } public static Renderer getRenderer(final FacesContext fc, final UIComponent component) { final String rendererType = component.getRendererType(); if (rendererType != null) { return fc.getRenderKit().getRenderer(component.getFamily(), rendererType); } return null; } }