/* * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl.html * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Contributors: * Nuxeo - initial API and implementation * * $Id: $ */ package org.nuxeo.ecm.platform.ui.web.tag.handler; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.el.ELException; import javax.el.ValueExpression; import javax.el.VariableMapper; import javax.faces.FacesException; import javax.faces.component.UIComponent; import javax.faces.view.facelets.ComponentConfig; import javax.faces.view.facelets.FaceletContext; import javax.faces.view.facelets.FaceletException; import javax.faces.view.facelets.FaceletHandler; import javax.faces.view.facelets.TagAttribute; import javax.faces.view.facelets.TagException; import org.apache.commons.lang.StringUtils; import org.nuxeo.ecm.platform.ui.web.binding.MetaValueExpression; import org.nuxeo.ecm.platform.ui.web.binding.alias.AliasTagHandler; import org.nuxeo.ecm.platform.ui.web.binding.alias.AliasVariableMapper; import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; /** * Tag handler that exposes a variable to the variable map. Behaviour is close * to the c:set tag handler except: * <ul> * <li>It allows caching a variable using cache parameter: variable will be * resolved the first time is is called and will be put in the context after</li> * <li>The resolved variable is removed from context when tag is closed to * avoid filling the context with it</li> * <li>Since 5.4, variables are made available in the request context after the * JSF component tree build thanks to a backing component.</li> * </ul> * * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> * @since 5.3.1 */ public class SetTagHandler extends AliasTagHandler { protected final TagAttribute var; protected final TagAttribute value; /** * @since 5.5 */ protected final TagAttribute resolveTwice; /** * @since 5.6 */ protected final TagAttribute blockPatterns; /** * @since 5.9.2 */ protected final TagAttribute blockMerge; public SetTagHandler(ComponentConfig config) { super(config, null); var = getRequiredAttribute("var"); value = getAttribute("value"); resolveTwice = getAttribute("resolveTwice"); blockPatterns = getAttribute("blockPatterns"); blockMerge = getAttribute("blockMerge"); } public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException, ELException { // make sure our parent is not null if (parent == null) { throw new TagException(this.tag, "Parent UIComponent was null"); } FaceletHandler nextHandler = this.nextHandler; VariableMapper orig = ctx.getVariableMapper(); AliasVariableMapper target = new AliasVariableMapper(); // generate id before applying (and before generating next handler, in // case of merge of variables, as parent aliases will be exposed to // request then). target.setId(ctx.generateUniqueId(this.tagId)); VariableMapper vm = target.getVariableMapperForBuild(orig); ctx.setVariableMapper(vm); try { nextHandler = getAliasVariableMapper(ctx, target); } finally { ctx.setVariableMapper(orig); } apply(ctx, parent, target, nextHandler); } public FaceletHandler getNextHandler() { return nextHandler; } public boolean isAcceptingMerge(FaceletContext ctx) { if (blockMerge != null) { if (blockMerge.getBoolean(ctx)) { return false; } } if (blockPatterns != null) { String blocked = blockPatterns.getValue(ctx); if (!StringUtils.isEmpty(blocked)) { return false; } } return true; } public FaceletHandler getAliasVariableMapper(FaceletContext ctx, AliasVariableMapper target) { String varStr = var.getValue(ctx); // avoid overriding variable already in the mapper if (target.hasVariables(varStr)) { return this.nextHandler; } // handle variable expression boolean cacheValue = false; if (cache != null) { cacheValue = cache.getBoolean(ctx); } boolean resolveTwiceBool = false; if (resolveTwice != null) { resolveTwiceBool = resolveTwice.getBoolean(ctx); } ValueExpression ve; if (cacheValue) { // resolve value and put it as is in variable mapper Object res = value.getObject(ctx); if (resolveTwiceBool && res instanceof String && ComponentTagUtils.isValueReference((String) res)) { ve = ctx.getExpressionFactory().createValueExpression(ctx, (String) res, Object.class); res = ve.getValue(ctx); } ve = ctx.getExpressionFactory().createValueExpression(res, Object.class); } else { ve = value.getValueExpression(ctx, Object.class); if (resolveTwiceBool) { ve = new MetaValueExpression(ve); } } target.setVariable(varStr, ve); if (blockPatterns != null) { String blockedValue = blockPatterns.getValue(ctx); if (!StringUtils.isEmpty(blockedValue)) { // split on "," character target.setBlockedPatterns(resolveBlockPatterns(blockedValue)); } } FaceletHandler nextHandler = this.nextHandler; if (nextHandler instanceof SetTagHandler) { // try merging with next handler SetTagHandler next = (SetTagHandler) nextHandler; if (next.isAcceptingMerge(ctx)) { // make sure referenced vars will be resolved in this context ctx.getVariableMapper().setVariable(varStr, ve); try { AliasVariableMapper.exposeAliasesToRequest( ctx.getFacesContext(), target); nextHandler = next.getAliasVariableMapper(ctx, target); } finally { AliasVariableMapper.removeAliasesExposedToRequest( ctx.getFacesContext(), target.getId()); } } } return nextHandler; } protected List<String> resolveBlockPatterns(String value) { List<String> res = new ArrayList<String>(); if (value != null) { String[] split = StringUtils.split(value, ','); if (split != null) { for (String item : split) { res.add(item.trim()); } } } return res; } }