/* * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * Anahide Tchertchian */ package org.nuxeo.ecm.platform.ui.web.component.holder; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.el.ELException; import javax.el.ExpressionFactory; import javax.el.ValueExpression; import javax.el.VariableMapper; import javax.faces.FacesException; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.event.PhaseId; import javax.faces.view.facelets.ComponentConfig; import javax.faces.view.facelets.FaceletContext; import javax.faces.view.facelets.TagAttribute; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.platform.ui.web.binding.alias.AliasVariableMapper; import org.nuxeo.ecm.platform.ui.web.tag.handler.GenericHtmlComponentHandler; import org.nuxeo.ecm.platform.ui.web.util.FaceletDebugTracer; import com.sun.faces.facelets.tag.jsf.ComponentSupport; /** * Tag handler for a {@link UIValueHolder} component, that exposes the value kept by the component at build time for * children components. * * @since 5.5 */ public class ValueHolderTagHandler extends GenericHtmlComponentHandler { protected final Log log = LogFactory.getLog(ValueHolderTagHandler.class); protected final TagAttribute var; /** * @since 8.2 */ protected final TagAttribute skip; public ValueHolderTagHandler(ComponentConfig config) { super(config); var = getAttribute("var"); skip = getAttribute("skip"); } @Override public void apply(FaceletContext ctx, UIComponent parent) throws IOException { boolean skipValue = false; if (skip != null) { skipValue = skip.getBoolean(ctx); } if (skipValue) { super.applyNextHandler(ctx, parent); } else { super.apply(ctx, parent); } } @Override public void applyNextHandler(FaceletContext ctx, UIComponent c) throws IOException, FacesException, ELException { long start = FaceletDebugTracer.start(); String varName = null; try { boolean varSet = false; if (var != null) { varName = var.getValue(ctx); } VariableMapper orig = ctx.getVariableMapper(); AliasVariableMapper alias = new AliasVariableMapper(); // XXX: reuse the component id as the alias variable mapper id so that // the value holder JSF component can reuse it at render time to expose // the value it keeps String aliasId = (String) c.getAttributes().get(ComponentSupport.MARK_CREATED); alias.setId(aliasId); if (!StringUtils.isBlank(varName)) { varSet = true; List<String> blockedPatterns = new ArrayList<String>(); blockedPatterns.add(varName); alias.setBlockedPatterns(blockedPatterns); } try { if (varSet) { Object valueToExpose = retrieveValueToExpose(ctx, c); ExpressionFactory eFactory = ctx.getExpressionFactory(); ValueExpression valueVe = eFactory.createValueExpression(valueToExpose, Object.class); alias.setVariable(varName, valueVe); VariableMapper vm = alias.getVariableMapperForBuild(orig); ctx.setVariableMapper(vm); AliasVariableMapper.exposeAliasesToRequest(ctx.getFacesContext(), alias); } super.applyNextHandler(ctx, c); } finally { if (varSet) { AliasVariableMapper.removeAliasesExposedToRequest(ctx.getFacesContext(), aliasId); ctx.setVariableMapper(orig); } } } finally { FaceletDebugTracer.trace(start, getTag(), varName); } } /** * Returns the value to expose at build time for this tag handler. * <p> * Value can be retrieved directly from component in most of cases, but should be retrieved from view-scoped managed * bean when the restore phase is called (as component has not been restored yet, so its value is not available to * be exposed in the tree view being built). * * @since 6.0 */ protected Object retrieveValueToExpose(FaceletContext context, UIComponent comp) { if (comp instanceof UIValueHolder) { UIValueHolder c = (UIValueHolder) comp; FacesContext faces = context.getFacesContext(); if (PhaseId.RESTORE_VIEW.equals(faces.getCurrentPhaseId())) { // lookup backing bean NuxeoValueHolderBean bean = c.lookupBean(faces); if (bean != null) { String fid = c.getFaceletId(); if (fid != null && bean.hasState(fid)) { return bean.getState(fid); } } } return c.getValueToExpose(); } else { String className = null; if (comp != null) { className = comp.getClass().getName(); } log.error("Associated component with class '" + className + "' is not a UIValueHolder instance => cannot retrieve value to expose."); } return null; } }