001/* 002 * (C) Copyright 2010 Nuxeo SAS (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 * Nuxeo - initial API and implementation 016 * 017 * $Id: $ 018 */ 019 020package org.nuxeo.ecm.platform.ui.web.tag.handler; 021 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.List; 025 026import javax.el.ELException; 027import javax.el.ValueExpression; 028import javax.el.VariableMapper; 029import javax.faces.FacesException; 030import javax.faces.component.UIComponent; 031import javax.faces.view.facelets.ComponentConfig; 032import javax.faces.view.facelets.FaceletContext; 033import javax.faces.view.facelets.FaceletException; 034import javax.faces.view.facelets.FaceletHandler; 035import javax.faces.view.facelets.TagAttribute; 036import javax.faces.view.facelets.TagException; 037 038import org.apache.commons.lang.StringUtils; 039import org.nuxeo.ecm.platform.ui.web.binding.MetaValueExpression; 040import org.nuxeo.ecm.platform.ui.web.binding.alias.AliasTagHandler; 041import org.nuxeo.ecm.platform.ui.web.binding.alias.AliasVariableMapper; 042import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; 043 044/** 045 * Tag handler that exposes a variable to the variable map. Behaviour is close to the c:set tag handler except: 046 * <ul> 047 * <li>It allows caching a variable using cache parameter: variable will be resolved the first time is is called and 048 * will be put in the context after</li> 049 * <li>The resolved variable is removed from context when tag is closed to avoid filling the context with it</li> 050 * <li>Since 5.4, variables are made available in the request context after the JSF component tree build thanks to a 051 * backing component.</li> 052 * </ul> 053 * 054 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 055 * @since 5.3.1 056 */ 057public class SetTagHandler extends AliasTagHandler { 058 059 protected final TagAttribute var; 060 061 protected final TagAttribute value; 062 063 /** 064 * @since 5.5 065 */ 066 protected final TagAttribute resolveTwice; 067 068 /** 069 * @since 5.6 070 */ 071 protected final TagAttribute blockPatterns; 072 073 /** 074 * @since 5.9.2 075 */ 076 protected final TagAttribute blockMerge; 077 078 public SetTagHandler(ComponentConfig config) { 079 super(config, null); 080 var = getRequiredAttribute("var"); 081 value = getAttribute("value"); 082 resolveTwice = getAttribute("resolveTwice"); 083 blockPatterns = getAttribute("blockPatterns"); 084 blockMerge = getAttribute("blockMerge"); 085 } 086 087 @Override 088 public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException, 089 ELException { 090 // make sure our parent is not null 091 if (parent == null) { 092 throw new TagException(tag, "Parent UIComponent was null"); 093 } 094 095 FaceletHandler nextHandler = this.nextHandler; 096 VariableMapper orig = ctx.getVariableMapper(); 097 AliasVariableMapper target = new AliasVariableMapper(); 098 // generate id before applying (and before generating next handler, in 099 // case of merge of variables, as parent aliases will be exposed to 100 // request then). 101 target.setId(ctx.generateUniqueId(tagId)); 102 103 VariableMapper vm = target.getVariableMapperForBuild(orig); 104 ctx.setVariableMapper(vm); 105 try { 106 nextHandler = getAliasVariableMapper(ctx, target); 107 } finally { 108 ctx.setVariableMapper(orig); 109 } 110 apply(ctx, parent, target, nextHandler); 111 } 112 113 public FaceletHandler getNextHandler() { 114 return nextHandler; 115 } 116 117 public boolean isAcceptingMerge(FaceletContext ctx) { 118 if (blockMerge != null) { 119 if (blockMerge.getBoolean(ctx)) { 120 return false; 121 } 122 } 123 if (blockPatterns != null) { 124 String blocked = blockPatterns.getValue(ctx); 125 if (!StringUtils.isEmpty(blocked)) { 126 return false; 127 } 128 } 129 return true; 130 } 131 132 public FaceletHandler getAliasVariableMapper(FaceletContext ctx, AliasVariableMapper target) { 133 String varStr = var.getValue(ctx); 134 // avoid overriding variable already in the mapper 135 if (target.hasVariables(varStr)) { 136 return nextHandler; 137 } 138 139 // handle variable expression 140 boolean cacheValue = false; 141 if (cache != null) { 142 cacheValue = cache.getBoolean(ctx); 143 } 144 boolean resolveTwiceBool = false; 145 if (resolveTwice != null) { 146 resolveTwiceBool = resolveTwice.getBoolean(ctx); 147 } 148 149 ValueExpression ve; 150 if (cacheValue) { 151 // resolve value and put it as is in variable mapper 152 Object res = value.getObject(ctx); 153 if (resolveTwiceBool && res instanceof String && ComponentTagUtils.isValueReference((String) res)) { 154 ve = ctx.getExpressionFactory().createValueExpression(ctx, (String) res, Object.class); 155 res = ve.getValue(ctx); 156 } 157 ve = ctx.getExpressionFactory().createValueExpression(res, Object.class); 158 } else { 159 ve = value.getValueExpression(ctx, Object.class); 160 if (resolveTwiceBool) { 161 ve = new MetaValueExpression(ve, ctx.getFunctionMapper(), ctx.getVariableMapper()); 162 } 163 } 164 165 target.setVariable(varStr, ve); 166 167 if (blockPatterns != null) { 168 String blockedValue = blockPatterns.getValue(ctx); 169 if (!StringUtils.isEmpty(blockedValue)) { 170 // split on "," character 171 target.setBlockedPatterns(resolveBlockPatterns(blockedValue)); 172 } 173 } 174 175 FaceletHandler nextHandler = this.nextHandler; 176 if (nextHandler instanceof SetTagHandler) { 177 // try merging with next handler 178 SetTagHandler next = (SetTagHandler) nextHandler; 179 if (next.isAcceptingMerge(ctx)) { 180 // make sure referenced vars will be resolved in this context 181 ctx.getVariableMapper().setVariable(varStr, ve); 182 try { 183 AliasVariableMapper.exposeAliasesToRequest(ctx.getFacesContext(), target); 184 nextHandler = next.getAliasVariableMapper(ctx, target); 185 } finally { 186 AliasVariableMapper.removeAliasesExposedToRequest(ctx.getFacesContext(), target.getId()); 187 } 188 } 189 } 190 191 return nextHandler; 192 } 193 194 protected List<String> resolveBlockPatterns(String value) { 195 List<String> res = new ArrayList<String>(); 196 if (value != null) { 197 String[] split = StringUtils.split(value, ','); 198 if (split != null) { 199 for (String item : split) { 200 res.add(item.trim()); 201 } 202 } 203 } 204 return res; 205 } 206 207}