org.primefaces.extensions.component.masterdetail.MasterDetail.java Source code

Java tutorial

Introduction

Here is the source code for org.primefaces.extensions.component.masterdetail.MasterDetail.java

Source

/**
 * 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.util.Map;

import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.ResourceDependency;
import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import javax.faces.context.PartialViewContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.PostRestoreStateEvent;

import org.apache.commons.lang3.StringUtils;
import org.primefaces.component.breadcrumb.BreadCrumb;
import org.primefaces.model.menu.DefaultMenuItem;
import org.primefaces.model.menu.MenuModel;
import org.primefaces.util.Constants;

/**
 * <code>MasterDetail</code> component.
 *
 * @author Oleg Varaksin / last modified by $Author$
 * @version $Revision$
 * @since 0.2
 */
@ResourceDependency(library = "primefaces-extensions", name = "primefaces-extensions.css")
public class MasterDetail extends UIComponentBase {

    public static final String CONTEXT_VALUE_VALUE_EXPRESSION = "mdContextValueVE";
    public static final String SELECTED_LEVEL_VALUE_EXPRESSION = "selectedLevelVE";
    public static final String SELECTED_STEP_VALUE_EXPRESSION = "selectedStepVE";
    public static final String PRESERVE_INPUTS_VALUE_EXPRESSION = "preserveInputsVE";
    public static final String RESET_INPUTS_VALUE_EXPRESSION = "resetInputsVE";
    public static final String CONTEXT_VALUES = "mdContextValues";
    public static final String SELECT_DETAIL_REQUEST = "_selectDetailRequest";
    public static final String CURRENT_LEVEL = "_currentLevel";
    public static final String SELECTED_LEVEL = "_selectedLevel";
    public static final String SELECTED_STEP = "_selectedStep";
    public static final String PRESERVE_INPUTS = "_preserveInputs";
    public static final String RESET_INPUTS = "_resetInputs";
    public static final String CURRENT_CONTEXT_VALUE = "_curContextValue";
    public static final String RESOLVED_CONTEXT_VALUE = "contextValue_";
    public static final String BREADCRUMB_ID_PREFIX = "_bc";

    public static final String COMPONENT_TYPE = "org.primefaces.extensions.component.MasterDetail";
    public static final String COMPONENT_FAMILY = "org.primefaces.extensions.component";
    private static final String DEFAULT_RENDERER = "org.primefaces.extensions.component.MasterDetailRenderer";

    private MasterDetailLevel detailLevelToProcess;
    private MasterDetailLevel detailLevelToGo;
    private int levelPositionToProcess = -1;
    private int levelCount = -1;

    /**
     * Properties that are tracked by state saving.
     *
     * @author Oleg Varaksin / last modified by $Author$
     * @version $Revision$
     */
    protected enum PropertyKeys {

        level, contextValue, selectLevelListener, showBreadcrumb, showAllBreadcrumbItems, breadcrumbAboveHeader, style, styleClass;

        private String toString;

        PropertyKeys(String toString) {
            this.toString = toString;
        }

        PropertyKeys() {
        }

        @Override
        public String toString() {
            return ((this.toString != null) ? this.toString : super.toString());
        }
    }

    public MasterDetail() {
        setRendererType(DEFAULT_RENDERER);
    }

    @Override
    public String getFamily() {
        return COMPONENT_FAMILY;
    }

    public int getLevel() {
        return (Integer) getStateHelper().eval(PropertyKeys.level, 1);
    }

    public void setLevel(int level) {
        getStateHelper().put(PropertyKeys.level, level);
    }

    public Object getContextValue() {
        return getStateHelper().eval(PropertyKeys.contextValue, null);
    }

    public void setContextValue(Object contextValue) {
        getStateHelper().put(PropertyKeys.contextValue, contextValue);
    }

    public MethodExpression getSelectLevelListener() {
        return (MethodExpression) getStateHelper().eval(PropertyKeys.selectLevelListener, null);
    }

    public void setSelectLevelListener(MethodExpression selectLevelListener) {
        getStateHelper().put(PropertyKeys.selectLevelListener, selectLevelListener);
    }

    public boolean isShowBreadcrumb() {
        return (Boolean) getStateHelper().eval(PropertyKeys.showBreadcrumb, true);
    }

    public void setShowBreadcrumb(boolean showBreadcrumb) {
        getStateHelper().put(PropertyKeys.showBreadcrumb, showBreadcrumb);
    }

    public boolean isShowAllBreadcrumbItems() {
        return (Boolean) getStateHelper().eval(PropertyKeys.showAllBreadcrumbItems, false);
    }

    public void setShowAllBreadcrumbItems(boolean showAllBreadcrumbItems) {
        getStateHelper().put(PropertyKeys.showAllBreadcrumbItems, showAllBreadcrumbItems);
    }

    public boolean isBreadcrumbAboveHeader() {
        return (Boolean) getStateHelper().eval(PropertyKeys.breadcrumbAboveHeader, true);
    }

    public void setBreadcrumbAboveHeader(boolean breadcrumbAboveHeader) {
        getStateHelper().put(PropertyKeys.breadcrumbAboveHeader, breadcrumbAboveHeader);
    }

    public String getStyle() {
        return (String) getStateHelper().eval(PropertyKeys.style, null);
    }

    public void setStyle(String style) {
        getStateHelper().put(PropertyKeys.style, style);
    }

    public String getStyleClass() {
        return (String) getStateHelper().eval(PropertyKeys.styleClass, null);
    }

    public void setStyleClass(String styleClass) {
        getStateHelper().put(PropertyKeys.styleClass, styleClass);
    }

    @Override
    public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
        super.processEvent(event);

        FacesContext fc = FacesContext.getCurrentInstance();
        if (!(event instanceof PostRestoreStateEvent) || !isSelectDetailRequest(fc)) {
            return;
        }

        PartialViewContext pvc = fc.getPartialViewContext();
        if (pvc.getRenderIds().isEmpty()) {
            // update the MasterDetail component automatically
            pvc.getRenderIds().add(getClientId(fc));
        }

        MasterDetailLevel mdl = getDetailLevelToProcess(fc);
        Object contextValue = getContextValueFromFlow(fc, mdl, true);
        String contextVar = mdl.getContextVar();
        if (StringUtils.isNotBlank(contextVar) && contextValue != null) {
            Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
            requestMap.put(contextVar, contextValue);
        }
    }

    @Override
    public void processDecodes(FacesContext fc) {
        if (!isSelectDetailRequest(fc)) {
            super.processDecodes(fc);
        } else {
            getDetailLevelToProcess(fc).processDecodes(fc);
        }
    }

    @Override
    public void processValidators(FacesContext fc) {
        if (!isSelectDetailRequest(fc)) {
            super.processValidators(fc);
        } else {
            getDetailLevelToProcess(fc).processValidators(fc);
        }
    }

    @Override
    public void processUpdates(FacesContext fc) {
        if (!isSelectDetailRequest(fc)) {
            super.processUpdates(fc);
        } else {
            getDetailLevelToProcess(fc).processUpdates(fc);
        }
    }

    public MasterDetailLevel getDetailLevelToProcess(FacesContext fc) {
        if (detailLevelToProcess == null) {
            initDataForLevels(fc);
        }

        return detailLevelToProcess;
    }

    public MasterDetailLevel getDetailLevelToGo(FacesContext fc) {
        if (detailLevelToGo != null) {
            return detailLevelToGo;
        }

        final String strSelectedLevel = fc.getExternalContext().getRequestParameterMap()
                .get(getClientId(fc) + SELECTED_LEVEL);
        final String strSelectedStep = fc.getExternalContext().getRequestParameterMap()
                .get(getClientId(fc) + SELECTED_STEP);

        // selected level != null
        if (strSelectedLevel != null) {
            int selectedLevel = Integer.parseInt(strSelectedLevel);
            detailLevelToGo = getDetailLevelByLevel(selectedLevel);
            if (detailLevelToGo != null) {
                return detailLevelToGo;
            }

            throw new FacesException("MasterDetailLevel for selected level = " + selectedLevel + " not found.");
        }

        int step;
        if (strSelectedStep != null) {
            // selected step != null
            step = Integer.parseInt(strSelectedStep);
        } else {
            // selected level and selected step are null ==> go to the next level
            step = 1;
        }

        detailLevelToGo = getDetailLevelByStep(step);

        return detailLevelToGo;
    }

    public MasterDetailLevel getDetailLevelByLevel(int level) {
        for (UIComponent child : getChildren()) {
            if (child instanceof MasterDetailLevel) {
                MasterDetailLevel mdl = (MasterDetailLevel) child;
                if (mdl.getLevel() == level) {
                    return mdl;
                }
            }
        }

        return null;
    }

    public boolean isSelectDetailRequest(FacesContext fc) {
        return fc.getPartialViewContext().isAjaxRequest() && fc.getExternalContext().getRequestParameterMap()
                .containsKey(getClientId(fc) + SELECT_DETAIL_REQUEST);
    }

    public String getPreserveInputs(FacesContext fc) {
        return fc.getExternalContext().getRequestParameterMap().get(getClientId(fc) + PRESERVE_INPUTS);
    }

    public String getResetInputs(FacesContext fc) {
        return fc.getExternalContext().getRequestParameterMap().get(getClientId(fc) + RESET_INPUTS);
    }

    public void updateModel(FacesContext fc, MasterDetailLevel mdlToGo) {
        final int levelToGo = mdlToGo.getLevel();
        ValueExpression levelVE = this.getValueExpression(PropertyKeys.level.toString());
        if (levelVE != null) {
            // update "level"
            levelVE.setValue(fc.getELContext(), levelToGo);
            getStateHelper().remove(PropertyKeys.level);
        }

        // get component caused this ajax request
        final String source = fc.getExternalContext().getRequestParameterMap()
                .get(Constants.RequestParams.PARTIAL_SOURCE_PARAM);
        MasterDetailLevel mdl = getDetailLevelToProcess(fc);

        // get resolved context value
        Object contextValue = null;
        Map<String, Object> contextValues = (Map<String, Object>) mdl.getAttributes().get(CONTEXT_VALUES);
        if (contextValues != null) {
            contextValue = contextValues.get(RESOLVED_CONTEXT_VALUE + source);
        }

        if (contextValue != null) {
            // update current context value for corresponding MasterDetailLevel
            mdlToGo.getAttributes().put(getClientId(fc) + CURRENT_CONTEXT_VALUE, contextValue);

            ValueExpression contextValueVE = this.getValueExpression(PropertyKeys.contextValue.toString());
            if (contextValueVE != null) {
                // update "contextValue"
                contextValueVE.setValue(fc.getELContext(), contextValue);
                getStateHelper().remove(PropertyKeys.contextValue);
            }
        }
    }

    public Object getContextValueFromFlow(FacesContext fc, MasterDetailLevel mdl, boolean includeModel) {
        // try to get context value from internal storage
        Object contextValue = mdl.getAttributes().get(this.getClientId(fc) + MasterDetail.CURRENT_CONTEXT_VALUE);
        if (contextValue != null) {
            return contextValue;
        }

        // try to get context value from external storage (e.g. managed bean)
        if (includeModel) {
            return this.getContextValue();
        }

        return null;
    }

    public BreadCrumb getBreadcrumb() {
        BreadCrumb breadCrumb = null;
        for (UIComponent child : this.getChildren()) {
            if (child instanceof BreadCrumb) {
                breadCrumb = (BreadCrumb) child;

                break;
            }
        }

        MenuModel model = (breadCrumb != null ? breadCrumb.getModel() : null);

        if (model != null && model.getElements().isEmpty()) {
            String clientId = getClientId();
            String menuItemIdPrefix = getId() + "_bcItem_";

            for (UIComponent child : getChildren()) {
                if (child instanceof MasterDetailLevel) {
                    int level = ((MasterDetailLevel) child).getLevel();

                    // create menu item to detail level
                    DefaultMenuItem menuItem = new DefaultMenuItem();
                    menuItem.setId(menuItemIdPrefix + level);
                    menuItem.setAjax(true);
                    menuItem.setImmediate(true);
                    menuItem.setProcess("@none");
                    menuItem.setUpdate("@parent");

                    // add UIParameter
                    menuItem.setParam(clientId + MasterDetail.SELECT_DETAIL_REQUEST, true);
                    menuItem.setParam(clientId + MasterDetail.CURRENT_LEVEL, -1);
                    menuItem.setParam(clientId + MasterDetail.SELECTED_LEVEL, level);

                    model.addElement(menuItem);
                }
            }
        }

        return breadCrumb;
    }

    public void resetCalculatedValues() {
        detailLevelToProcess = null;
        detailLevelToGo = null;
        levelPositionToProcess = -1;
        levelCount = -1;
    }

    private void initDataForLevels(FacesContext fc) {
        final String strCurrentLevel = fc.getExternalContext().getRequestParameterMap()
                .get(getClientId(fc) + CURRENT_LEVEL);
        if (strCurrentLevel == null) {
            throw new FacesException("Current level is missing in request.");
        }

        int currentLevel = Integer.parseInt(strCurrentLevel);
        int count = 0;

        for (UIComponent child : getChildren()) {
            if (child instanceof MasterDetailLevel) {
                MasterDetailLevel mdl = (MasterDetailLevel) child;
                count++;

                if (detailLevelToProcess == null && mdl.getLevel() == currentLevel) {
                    detailLevelToProcess = mdl;
                    levelPositionToProcess = count;
                }
            }
        }

        levelCount = count;

        if (detailLevelToProcess == null) {
            throw new FacesException("Current MasterDetailLevel to process not found.");
        }
    }

    private MasterDetailLevel getDetailLevelByStep(int step) {
        int levelPositionToGo = getLevelPositionToProcess() + step;
        if (levelPositionToGo < 1) {
            levelPositionToGo = 1;
        } else if (levelPositionToGo > getLevelCount()) {
            levelPositionToGo = getLevelCount();
        }

        int pos = 0;
        for (UIComponent child : getChildren()) {
            if (child instanceof MasterDetailLevel) {
                MasterDetailLevel mdl = (MasterDetailLevel) child;
                pos++;

                if (pos == levelPositionToGo) {
                    return mdl;
                }
            }
        }

        // should not happen
        return null;
    }

    private int getLevelPositionToProcess() {
        if (levelPositionToProcess == -1) {
            initDataForLevels(FacesContext.getCurrentInstance());
        }

        return levelPositionToProcess;
    }

    private int getLevelCount() {
        if (levelCount == -1) {
            initDataForLevels(FacesContext.getCurrentInstance());
        }

        return levelCount;
    }
}