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