001/*
002 * (C) Copyright 2007 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: ComponentTagUtils.java 28610 2008-01-09 17:13:52Z sfermigier $
018 */
019
020package org.nuxeo.ecm.platform.ui.web.util;
021
022import javax.el.ELContext;
023import javax.el.ELException;
024import javax.el.ExpressionFactory;
025import javax.el.ValueExpression;
026import javax.faces.application.Application;
027import javax.faces.context.FacesContext;
028import javax.faces.view.facelets.FaceletContext;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032
033/**
034 * Component tag utils.
035 *
036 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
037 */
038public final class ComponentTagUtils {
039
040    private static final Log log = LogFactory.getLog(ComponentTagUtils.class);
041
042    // Utility class.
043    private ComponentTagUtils() {
044    }
045
046    /**
047     * Returns true if the specified value contains a value expression, e.g the start and end of EL markers.
048     *
049     * @param value the value to evaluate, returns false if null
050     */
051    public static boolean isValueReference(String value) {
052        if (value == null) {
053            return false;
054        }
055        return value.contains("#{") && value.indexOf("#{") < value.indexOf('}') || value.contains("${")
056                && value.indexOf("${") < value.indexOf('}');
057    }
058
059    /**
060     * Returns true if the specified value is a value expression, e.g starting and ending with EL markers after being
061     * trimmed.
062     *
063     * @param value the value to evaluate, returns false if null
064     * @since 5.6
065     */
066    public static boolean isStrictValueReference(String value) {
067        if (value == null) {
068            return false;
069        }
070        value = value.trim();
071        return (value.startsWith("#{") && value.indexOf("#{") < value.indexOf('}') && value.endsWith("}"))
072                || (value.startsWith("${") && value.indexOf("${") < value.indexOf('}') && value.endsWith("}"));
073    }
074
075    /**
076     * Returns a value name for given strict value reference. If reference is #{foo} or ${foo}, will return "foo".
077     *
078     * @since 5.6
079     * @throws IllegalArgumentException if reference is null or {@link #isStrictValueReference(String)} returns false.
080     * @param valueReference
081     */
082    public static String getBareValueName(String valueReference) {
083        if (!isStrictValueReference(valueReference)) {
084            throw new IllegalArgumentException(String.format("Invalid value reference '%s'", valueReference));
085        }
086        return valueReference.substring(2, valueReference.length() - 1);
087    }
088
089    /**
090     * Returns true if the specified value conforms to the syntax requirements of a method binding expression.
091     * <p>
092     * The method can have parameters and the expression must use parentheses even if no parameters are needed.
093     *
094     * @param value the value to evaluate (not null)
095     * @deprecated since 5.5: method and value references are now equivalent with jboss EL
096     */
097    @Deprecated
098    public static boolean isMethodReference(String value) {
099        boolean isValue = isValueReference(value);
100        if (isValue) {
101            if (value.contains("(") && value.indexOf('(') < value.indexOf(')')
102            // make sure it's not a function
103                    && (!value.contains(":") || value.indexOf(':') > value.indexOf('('))) {
104                return true;
105            }
106        }
107        return false;
108    }
109
110    /**
111     * Resolves an expression from a given faces context.
112     * <p>
113     * Resolves the expression a second time when first resolution gives a String value using the EL Expression syntax.
114     * <p>
115     * Does not throw any error when resolution fails (only logs an error message).
116     *
117     * @see #resolveElExpression(FaceletContext, String)
118     */
119    public static Object resolveElExpression(FacesContext context, String elExpression) {
120        if (!isValueReference(elExpression)) {
121            // literal
122            return elExpression;
123        } else {
124            if (context == null) {
125                log.error(String.format("FacesContext is null => cannot resolve el expression '%s'", elExpression));
126                return null;
127            }
128            // expression => evaluate
129            Application app = context.getApplication();
130            try {
131                return app.evaluateExpressionGet(context, elExpression, Object.class);
132            } catch (ELException e) {
133                log.error(String.format("Faces context: Error processing expression '%s'", elExpression), e);
134                return null;
135            }
136        }
137    }
138
139    /**
140     * Resolves given value expression as string and sets given value on it.
141     *
142     * @since 6.0
143     */
144    public static void applyValueExpression(FacesContext context, String elExpression, Object value) {
145        if (!isStrictValueReference(elExpression)) {
146            log.warn(String.format("Cannot set value '%s' for expression '%s'", value, elExpression));
147        } else {
148            if (context == null) {
149                log.error(String.format("FacesContext is null => cannot resolve el expression '%s'", elExpression));
150                return;
151            }
152            Application app = context.getApplication();
153            ExpressionFactory eFactory = app.getExpressionFactory();
154            ELContext elContext = context.getELContext();
155            try {
156                ValueExpression vExpression = eFactory.createValueExpression(elContext, elExpression, Object.class);
157                vExpression.setValue(elContext, value);
158            } catch (ELException e) {
159                log.error(String.format("Error setting value '%s' for expression '%s'", value, elExpression), e);
160            }
161        }
162    }
163
164    /**
165     * Resolves an expression from a given facelet context, using its {@link ExpressionFactory} that can hold a wider
166     * context than the faces context behind it.
167     * <p>
168     * Resolves the expression a second time when first resolution gives a String value using the EL Expression syntax.
169     * <p>
170     * Does not throw any error when resolution fails (only logs an error message).
171     */
172    public static Object resolveElExpression(FaceletContext faceletContext, String elExpression) {
173        if (!isValueReference(elExpression)) {
174            // literal
175            return elExpression;
176        } else {
177            if (faceletContext == null) {
178                log.error(String.format("FaceletContext is null => cannot resolve el expression '%s'", elExpression));
179                return null;
180            }
181            // expression => evaluate
182            ExpressionFactory eFactory = faceletContext.getExpressionFactory();
183            ELContext elContext = faceletContext.getFacesContext().getELContext();
184            ValueExpression expr = eFactory.createValueExpression(faceletContext, elExpression, Object.class);
185            try {
186                return expr.getValue(elContext);
187            } catch (ELException e) {
188                log.error(String.format("Facelet context: Error processing expression '%s'", elExpression), e);
189                return null;
190            }
191        }
192    }
193
194}