001/*
002 * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Anahide Tchertchian
016 */
017package org.nuxeo.ecm.platform.ui.web.component.holder;
018
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.List;
022
023import javax.el.ELException;
024import javax.el.ExpressionFactory;
025import javax.el.ValueExpression;
026import javax.el.VariableMapper;
027import javax.faces.FacesException;
028import javax.faces.component.UIComponent;
029import javax.faces.context.FacesContext;
030import javax.faces.event.PhaseId;
031import javax.faces.view.facelets.ComponentConfig;
032import javax.faces.view.facelets.FaceletContext;
033import javax.faces.view.facelets.TagAttribute;
034
035import org.apache.commons.lang.StringUtils;
036import org.apache.commons.logging.Log;
037import org.apache.commons.logging.LogFactory;
038import org.nuxeo.ecm.platform.ui.web.binding.alias.AliasVariableMapper;
039import org.nuxeo.ecm.platform.ui.web.tag.handler.GenericHtmlComponentHandler;
040
041import com.sun.faces.facelets.tag.jsf.ComponentSupport;
042
043/**
044 * Tag handler for a {@link UIValueHolder} component, that exposes the value kept by the component at build time for
045 * children components.
046 *
047 * @since 5.5
048 */
049public class ValueHolderTagHandler extends GenericHtmlComponentHandler {
050
051    protected final Log log = LogFactory.getLog(ValueHolderTagHandler.class);
052
053    protected final TagAttribute var;
054
055    public ValueHolderTagHandler(ComponentConfig config) {
056        super(config);
057        var = getAttribute("var");
058    }
059
060    @Override
061    public void applyNextHandler(FaceletContext ctx, UIComponent c) throws IOException, FacesException, ELException {
062        String varName = null;
063        boolean varSet = false;
064        if (var != null) {
065            varName = var.getValue(ctx);
066        }
067
068        VariableMapper orig = ctx.getVariableMapper();
069        AliasVariableMapper alias = new AliasVariableMapper();
070        // XXX: reuse the component id as the alias variable mapper id so that
071        // the value holder JSF component can reuse it at render time to expose
072        // the value it keeps
073        String aliasId = (String) c.getAttributes().get(ComponentSupport.MARK_CREATED);
074        alias.setId(aliasId);
075
076        if (!StringUtils.isBlank(varName)) {
077            varSet = true;
078            List<String> blockedPatterns = new ArrayList<String>();
079            blockedPatterns.add(varName);
080            alias.setBlockedPatterns(blockedPatterns);
081        }
082
083        try {
084            if (varSet) {
085                Object valueToExpose = retrieveValueToExpose(ctx, c);
086                ExpressionFactory eFactory = ctx.getExpressionFactory();
087                ValueExpression valueVe = eFactory.createValueExpression(valueToExpose, Object.class);
088                alias.setVariable(varName, valueVe);
089                VariableMapper vm = alias.getVariableMapperForBuild(orig);
090                ctx.setVariableMapper(vm);
091                AliasVariableMapper.exposeAliasesToRequest(ctx.getFacesContext(), alias);
092            }
093            super.applyNextHandler(ctx, c);
094        } finally {
095            if (varSet) {
096                AliasVariableMapper.removeAliasesExposedToRequest(ctx.getFacesContext(), aliasId);
097                ctx.setVariableMapper(orig);
098            }
099        }
100    }
101
102    /**
103     * Returns the value to expose at build time for this tag handler.
104     * <p>
105     * Value can be retrieved directly from component in most of cases, but should be retrieved from view-scoped managed
106     * bean when the restore phase is called (as component has not been restored yet, so its value is not available to
107     * be exposed in the tree view being built).
108     *
109     * @since 6.0
110     */
111    protected Object retrieveValueToExpose(FaceletContext context, UIComponent comp) {
112        if (comp instanceof UIValueHolder) {
113            UIValueHolder c = (UIValueHolder) comp;
114            FacesContext faces = context.getFacesContext();
115            if (PhaseId.RESTORE_VIEW.equals(faces.getCurrentPhaseId())) {
116                // lookup backing bean
117                NuxeoValueHolderBean bean = c.lookupBean(faces);
118                if (bean != null) {
119                    String fid = c.getFaceletId();
120                    if (fid != null && bean.hasState(fid)) {
121                        return bean.getState(fid);
122                    }
123                }
124            }
125            return c.getValueToExpose();
126        } else {
127            String className = null;
128            if (comp != null) {
129                className = comp.getClass().getName();
130            }
131            log.error(String.format("Associated component with class '%s' is not"
132                    + " a UIValueHolder instance => cannot " + "retrieve value to expose.", className));
133        }
134        return null;
135    }
136
137}