001/*
002 * (C) Copyright 2015 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-2.1.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.binding;
018
019import java.util.ArrayList;
020import java.util.LinkedHashMap;
021import java.util.List;
022import java.util.Map;
023
024import javax.el.ELException;
025import javax.el.ValueExpression;
026import javax.el.VariableMapper;
027
028import org.apache.commons.lang.StringUtils;
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.jboss.el.ValueExpressionLiteral;
032import org.nuxeo.ecm.platform.ui.web.binding.alias.AliasVariableMapper;
033
034/**
035 * Alternative to {@link AliasVariableMapper} optimized behavior.
036 * <p>
037 * Keeps variables in the current context, but without aliasing variables for efficiency. Compared to the standard
038 * variable mappers, adds blocking features, given patterns, to allow compartmenting variables contexts (inside layout
039 * widget trees for instance).
040 *
041 * @since 8.2
042 */
043public class BlockingVariableMapper extends VariableMapper {
044
045    private static final Log log = LogFactory.getLog(BlockingVariableMapper.class);
046
047    protected final VariableMapper orig;
048
049    protected Map<String, ValueExpression> vars;
050
051    protected List<String> blockedPatterns;
052
053    public BlockingVariableMapper(VariableMapper orig) {
054        super();
055        this.orig = orig;
056    }
057
058    @Override
059    public ValueExpression resolveVariable(String variable) {
060        ValueExpression ve = null;
061        try {
062            if (hasVariable(variable)) {
063                ve = (ValueExpression) vars.get(variable);
064            } else {
065                // resolve to a value expression resolving to null if variable
066                // is supposed to be blocked
067                if (variable != null && blockedPatterns != null) {
068                    for (String blockedPattern : blockedPatterns) {
069                        if (StringUtils.isBlank(blockedPattern)) {
070                            continue;
071                        }
072                        boolean doBlock = false;
073                        if (blockedPattern.endsWith("*")) {
074                            String pattern = blockedPattern.substring(0, blockedPattern.length() - 1);
075                            if (variable.startsWith(pattern)) {
076                                doBlock = true;
077                            }
078                        } else if (blockedPattern.equals(variable)) {
079                            doBlock = true;
080                        }
081                        if (doBlock) {
082                            if (log.isDebugEnabled()) {
083                                log.debug(String.format("Blocked expression var='%s'", variable));
084                            }
085                            return getNullValueExpression();
086                        }
087                    }
088                }
089                return orig.resolveVariable(variable);
090            }
091            return ve;
092        } catch (StackOverflowError e) {
093            throw new ELException("Could not Resolve Variable [Overflow]: " + variable, e);
094        }
095    }
096
097    @Override
098    public ValueExpression setVariable(String variable, ValueExpression expression) {
099        if (vars == null) {
100            vars = new LinkedHashMap<String, ValueExpression>();
101        }
102        return vars.put(variable, expression);
103    }
104
105    public boolean hasVariable(String variable) {
106        return vars != null && vars.containsKey(variable);
107    }
108
109    protected ValueExpression getNullValueExpression() {
110        return new ValueExpressionLiteral(null, Object.class);
111    }
112
113    public Map<String, ValueExpression> getVariables() {
114        return vars;
115    }
116
117    public List<String> getBlockedPatterns() {
118        return blockedPatterns;
119    }
120
121    public void setBlockedPatterns(List<String> blockedPatterns) {
122        if (blockedPatterns != null) {
123            this.blockedPatterns = new ArrayList<String>();
124            this.blockedPatterns.addAll(blockedPatterns);
125        } else {
126            this.blockedPatterns = null;
127        }
128    }
129
130    public void addBlockedPattern(String blockedPattern) {
131        if (StringUtils.isBlank(blockedPattern)) {
132            return;
133        }
134        if (this.blockedPatterns == null) {
135            this.blockedPatterns = new ArrayList<String>();
136        }
137        this.blockedPatterns.add(blockedPattern);
138    }
139
140    @Override
141    public String toString() {
142        final StringBuilder buf = new StringBuilder();
143
144        buf.append(getClass().getSimpleName());
145        buf.append(" {");
146        buf.append("vars=");
147        buf.append(vars);
148        buf.append('}');
149
150        return buf.toString();
151    }
152
153}