001/**
002 * Copyright 2005-2018 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.uif.view;
017
018import org.kuali.rice.krad.datadictionary.uif.UifDictionaryBean;
019
020import java.util.List;
021import java.util.Map;
022
023/**
024 * Provides evaluation of expression language statements against a given context
025 *
026 * <p>
027 * Used within the UI framework to allow conditional logic to be configured through
028 * the XML which can alter the values of component properties
029 * </p>
030 *
031 * @author Kuali Rice Team (rice.collab@kuali.org)
032 */
033public interface ExpressionEvaluator {
034
035    /**
036     * Indicator that can be added to a property name to indicate the expression result should be added to the
037     * property (assumed to be a collection) instead of replaced
038     */
039    String EMBEDDED_PROPERTY_NAME_ADD_INDICATOR = ".add";
040
041    /**
042     * Initializes the expression context for the given expression context object
043     *
044     * <p>
045     * The object given here will form the default context for expression terms (terms without any
046     * variable prefix)
047     * </p>
048     *
049     * @param contextObject instance of an Object
050     */
051    void initializeEvaluationContext(Object contextObject);
052
053    /**
054     * Evaluates any el expressions that are found as a string property value
055     * for the object
056     *
057     * <p>
058     * Using reflection the properties for the object are retrieved and if of
059     * <code>String</code> type the corresponding value is retrieved. If the
060     * value is not empty and contains the el placeholder see
061     * {@link #containsElPlaceholder(String)} then the expression is evaluated
062     * using the given context object and parameters. The evaluated string is
063     * then set as the new property value, or in the case of a template
064     * (expression contained within a literal string), the expression part is
065     * replaced in the property value.
066     * </p>
067     *
068     * <p>
069     * In addition to evaluating any property expressions, any configured
070     * <code>PropertyReplacer</code> for the object are also evaluated and if a
071     * match occurs those property replacements are made
072     * </p>
073     *
074     * @param view view instance being rendered
075     * @param expressionConfigurable object whose properties should be checked for expressions
076     * and evaluated
077     * @param evaluationParameters map of parameters that may appear in expressions, the map
078     * key gives the parameter name that may appear in the expression, and the map value is the object that expression
079     * should evaluate against when that name is found
080     */
081    void evaluateExpressionsOnConfigurable(View view, UifDictionaryBean expressionConfigurable,
082            Map<String, Object> evaluationParameters);
083
084    /**
085     * Evaluates the given expression template string against the context object
086     * and map of parameters
087     *
088     * <p>
089     * If the template string contains one or more el placeholders (see
090     * {@link #containsElPlaceholder(String)}), the expression contained within
091     * the placeholder will be evaluated and the corresponding value will be
092     * substituted back into the property value where the placeholder occurred.
093     * If no placeholders are found, the string will be returned unchanged
094     * </p>
095     *
096     * @param evaluationParameters map of parameters that may appear in expressions, the map
097     * key gives the parameter name that may appear in the expression, and the map value is the object that expression
098     * should evaluate against when that name is found
099     * @param expressionTemplate string that should be evaluated for el expressions
100     * @return String formed by replacing any el expressions in the original expression template with
101     *         their corresponding evaluation results
102     */
103    String evaluateExpressionTemplate(Map<String, Object> evaluationParameters, String expressionTemplate);
104
105    /**
106     * Evaluates the configured expression for the given property name (if not exists) on the given configurable
107     *
108     * @param view view instance the configurable is associated with, used to adjust binding prefixes
109     * @param evaluationParameters map that will be exposed as EL parameters
110     * @param expressionConfigurable configurable object to pull and evaluate the expression on
111     * @param propertyName name of the property whose expression should be evaluated
112     * @param removeExpression boolean that indicates whether the expression should be removed after evaluation
113     */
114    void evaluatePropertyExpression(View view, Map<String, Object> evaluationParameters,
115            UifDictionaryBean expressionConfigurable, String propertyName, boolean removeExpression);
116
117    /**
118     * Evaluates the given el expression against the content object and
119     * parameters, and returns the result of the evaluation
120     *
121     * <p>
122     * The given expression string is assumed to be one el expression and should
123     * not contain the el placeholders. The returned result depends on the
124     * evaluation and what type is returns, for instance a boolean will be
125     * return for a boolean expression, or a string for string expression
126     * </p>
127     *
128     * @param evaluationParameters map of parameters that may appear in expressions, the map
129     * key gives the parameter name that may appear in the expression, and the map value is the object that expression
130     * should evaluate against when that name is found
131     * @param expression el expression to evaluate
132     * @return Object result of the expression evaluation
133     */
134    Object evaluateExpression(Map<String, Object> evaluationParameters, String expression);
135
136    /**
137     * Indicates whether or not the given string contains the el placeholder
138     * (begin and end delimiters)
139     *
140     * @param value String to check for contained placeholders
141     * @return boolean true if the string contains one or more placeholders, false if it contains none
142     * @see org.kuali.rice.krad.uif.UifConstants#EL_PLACEHOLDER_PREFIX
143     * @see org.kuali.rice.krad.uif.UifConstants#EL_PLACEHOLDER_SUFFIX
144     */
145    boolean containsElPlaceholder(String value);
146
147    /**
148     * Adjusts the property expressions for a given object
149     *
150     * <p>
151     * The {@link org.kuali.rice.krad.uif.UifConstants#NO_BIND_ADJUST_PREFIX}  prefix will be removed
152     * as this is a placeholder indicating that the property is directly on the form.
153     * The {@link org.kuali.rice.krad.uif.UifConstants#FIELD_PATH_BIND_ADJUST_PREFIX} prefix will be replaced by
154     * the object's field path - this is only applicable to DataFields. The
155     * {@link org.kuali.rice.krad.uif.UifConstants#DEFAULT_PATH_BIND_ADJUST_PREFIX} prefix will be replaced
156     * by the view's default path if it is set.
157     * </p>
158     *
159     * @param view the parent view of the object
160     * @param object Object to adjust property expressions on
161     * @param expression The expression to adjust
162     * @return the adjusted expression String
163     */
164    String replaceBindingPrefixes(View view, Object object, String expression);
165
166    /**
167     * Pulls expressions within the expressionConfigurable's expression graph and moves them to the property
168     * expressions
169     * map for the expressionConfigurable or a nested expressionConfigurable (for the case of nested expression property
170     * names)
171     *
172     * <p>
173     * Expressions that are configured on properties and pulled out by the {@link org.kuali.rice.krad.uif.util.UifBeanFactoryPostProcessor}
174     * and put in the {@link org.kuali.rice.krad.datadictionary.uif.UifDictionaryBean#getExpressionGraph()} for the bean
175     * that is
176     * at root (non nested) level. Before evaluating the expressions, they need to be moved to the
177     * {@link org.kuali.rice.krad.datadictionary.uif.UifDictionaryBean#getPropertyExpressions()} map for the
178     * expressionConfigurable that
179     * property
180     * is on.
181     * </p>
182     *
183     * @param expressionConfigurable expressionConfigurable instance to process expressions for
184     * @param buildRefreshGraphs indicates whether the expression graphs for component refresh should be built
185     */
186    void populatePropertyExpressionsFromGraph(UifDictionaryBean expressionConfigurable,
187            boolean buildRefreshGraphs);
188
189    /**
190     * Takes in an expression and a list to be filled in with names(property names)
191     * of controls found in the expression.
192     *
193     * <p>This method returns a js expression which can
194     * be executed on the client to determine if the original exp was satisfied before
195     * interacting with the server - ie, this js expression is equivalent to the one passed in.</p>
196     *
197     * <p>There are limitations on the Spring expression language that can be used as this method.
198     * It is only used to parse expressions which are valid case statements for determining if
199     * some action/processing should be performed.  ONLY Properties, comparison operators, booleans,
200     * strings, matches expression, and boolean logic are supported.  Server constants and calls will be evaluated
201     * early.  The isValueEmpty, listContains, and emptyList custom KRAD functions, however, will be converted
202     * to a js equivalent function.  Properties must be a valid property on the form, and should have a visible control
203     * within the view. </p>
204     *
205     * <p>Example valid exp: "account.name == 'Account Name'"</p>
206     *
207     * @param exp the expression to convert to a js condition
208     * @param controlNames the list to populate with control names found in the expression (these may later be used
209     * to add js change handlers)
210     * @return the converted expression into an equivalent js condition
211     */
212    String parseExpression(String exp, List<String> controlNames, Map<String, Object> context);
213
214    /**
215     * Find the control names (ie, propertyNames) used in the passed in expression
216     *
217     * @param exp the expression to search
218     * @return the list of control names found (ie, propertyNames)
219     */
220    List<String> findControlNamesInExpression(String exp);
221}