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.util;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.config.property.ConfigContext;
020import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
021import org.kuali.rice.kim.api.identity.Person;
022import org.kuali.rice.kim.api.services.KimApiServiceLocator;
023import org.kuali.rice.krad.data.platform.MaxValueIncrementerFactory;
024import org.kuali.rice.krad.service.KRADServiceLocator;
025import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
026import org.kuali.rice.krad.service.KualiModuleService;
027import org.kuali.rice.krad.service.ModuleService;
028import org.kuali.rice.krad.util.GlobalVariables;
029import org.kuali.rice.krad.util.KRADConstants;
030
031import javax.sql.DataSource;
032import java.util.ArrayList;
033import java.util.Arrays;
034import java.util.Date;
035import java.util.List;
036import java.util.Map;
037
038/**
039 * Defines functions that can be used in el expressions within
040 * the UIF dictionary files
041 *
042 * @author Kuali Rice Team (rice.collab@kuali.org)
043 */
044public class ExpressionFunctions {
045
046    /**
047     * Checks whether the given class parameter is assignable from the given object class
048     * parameter
049     *
050     * @param assignableClass class to use for assignable to
051     * @param objectClass class to use for assignable from
052     * @return true if the object class is of type assignable class, false if not
053     */
054    public static boolean isAssignableFrom(Class<?> assignableClass, Class<?> objectClass) {
055        return assignableClass.isAssignableFrom(objectClass);
056    }
057
058    /**
059     * Checks whether the given value is null or blank string
060     *
061     * @param value property value to check
062     * @return true if value is null or blank, false if not
063     */
064    public static boolean empty(Object value) {
065        return (value == null) || (StringUtils.isBlank(value.toString()));
066    }
067
068    /**
069     * Checks to see if the list is empty.  Throws a RuntimeException if list is not a List.
070     *
071     * @param list the list
072     * @return true if the list is null or empty, false otherwise
073     */
074    public static boolean emptyList(List<?> list) {
075        return (list == null) || list.isEmpty();
076    }
077
078    /**
079     * Returns the service indicated by {@code serviceName}.
080     *
081     * @param serviceName the name of the service to obtain
082     * @param <T> the type of the service
083     *
084     * @return the service indicated by {@code serviceName}.
085     */
086    public static <T extends Object> T getService(String serviceName) {
087        return KRADServiceLocatorWeb.getService(serviceName);
088    }
089
090    /**
091     * Check to see if the list contains the values passed in.
092     *
093     * <p>In the SpringEL call values can be single item or array due to the way the EL converts values.
094     * The values can be string or numeric and should match
095     * the content type being stored in the list.  If the list is String and the values passed in are not string,
096     * toString() conversion will be used.  Returns true if the values are in the list and both lists are non-empty,
097     * false otherwise.
098     * </p>
099     *
100     * @param list the list to be evaluated
101     * @param values the values to be to check for in the list
102     * @return true if all values exist in the list and both values and list are non-null/not-empty, false otherwise
103     */
104    public static boolean listContains(List<?> list, Object[] values) {
105        if (list != null && values != null && values.length > 0 && !list.isEmpty()) {
106            //conversion for if the values are non-string but the list is string (special case)
107            if (list.get(0) instanceof String && !(values[0] instanceof String)) {
108                String[] stringValues = new String[values.length];
109                for (int i = 0; i < values.length; i++) {
110                    stringValues[i] = values[i].toString();
111                }
112                return list.containsAll(Arrays.asList(stringValues));
113            } else if (list.get(0) instanceof Date && values[0] instanceof String) {
114                //TODO date conversion
115                return false;
116            } else if (!(list.get(0) instanceof String) && values[0] instanceof String) {
117                //values passed in are string but the list is of objects, use object's toString method
118                List<String> stringList = new ArrayList<String>();
119                for (Object value : list) {
120                    stringList.add(value.toString());
121                }
122                return stringList.containsAll(Arrays.asList(values));
123            } else {
124                //no conversion for if neither list is String, assume matching types (numeric case)
125                return list.containsAll(Arrays.asList(values));
126            }
127        }
128
129        //no cases satisfied, return false
130        return false;
131
132    }
133
134    /**
135     * Returns the name for the given class
136     *
137     * @param clazz class object to return name for
138     * @return class name or empty string if class is null
139     */
140    public static String getName(Class<?> clazz) {
141        if (clazz == null) {
142            return "";
143        } else {
144            return clazz.getName();
145        }
146    }
147
148    /**
149     * Retrieves the value of the parameter identified with the given namespace, component, and name
150     *
151     * @param namespaceCode namespace code for the parameter to retrieve
152     * @param componentCode component code for the parameter to retrieve
153     * @param parameterName name of the parameter to retrieve
154     * @return String value of parameter as a string or null if parameter does not exist
155     */
156    public static String getParam(String namespaceCode, String componentCode, String parameterName) {
157        return CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(namespaceCode, componentCode,
158                parameterName);
159    }
160
161    /**
162     * Retrieves the value of the parameter identified with the given namespace, component, and name and converts
163     * to a Boolean
164     *
165     * @param namespaceCode namespace code for the parameter to retrieve
166     * @param componentCode component code for the parameter to retrieve
167     * @param parameterName name of the parameter to retrieve
168     * @return Boolean value of parameter as a boolean or null if parameter does not exist
169     */
170    public static Boolean getParamAsBoolean(String namespaceCode, String componentCode, String parameterName) {
171        return CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(namespaceCode,
172                componentCode, parameterName);
173    }
174
175    /**
176     * Retrieves the value of the parameter identified with the given namespace, component, and name and converts
177     * to a Integer
178     *
179     * @param namespaceCode namespace code for the parameter to retrieve
180     * @param componentCode component code for the parameter to retrieve
181     * @param parameterName name of the parameter to retrieve
182     * @return Boolean value of parameter as a Integer or null if parameter does not exist
183     */
184    public static Integer getParamAsInteger(String namespaceCode, String componentCode, String parameterName) {
185        String param = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(namespaceCode,
186                        componentCode, parameterName);
187        if (param == null) {
188            return null;
189        }
190
191        return Integer.valueOf(CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(namespaceCode,
192                componentCode, parameterName));
193    }
194
195    /**
196     * Retrieves the value of the parameter identified with the given namespace, component, and name and converts
197     * to a Double
198     *
199     * @param namespaceCode namespace code for the parameter to retrieve
200     * @param componentCode component code for the parameter to retrieve
201     * @param parameterName name of the parameter to retrieve
202     * @return Boolean value of parameter as a Double or null if parameter does not exist
203     */
204    public static Double getParamAsDouble(String namespaceCode, String componentCode, String parameterName) {
205        String param = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(namespaceCode,
206                        componentCode, parameterName);
207        if (param == null) {
208            return null;
209        }
210
211        return Double.valueOf(CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(namespaceCode,
212                componentCode, parameterName));
213    }
214
215    /**
216     * Indicates whether the current user has the permission identified by the given namespace and permission name
217     *
218     * @param namespaceCode namespace code for the permission to check
219     * @param permissionName name of the permission to check
220     * @return true if the current user has the permission, false if not or the permission does not exist
221     */
222    public static boolean hasPerm(String namespaceCode, String permissionName) {
223        Person user = GlobalVariables.getUserSession().getPerson();
224
225        return KimApiServiceLocator.getPermissionService().hasPermission(user.getPrincipalId(), namespaceCode,
226                permissionName);
227    }
228
229    /**
230     * Indicates whether the current user has the permission identified by the given namespace and permission name
231     * and with the given details and role qualification
232     *
233     * @param namespaceCode namespace code for the permission to check
234     * @param permissionName name of the permission to check
235     * @param permissionDetails details for the permission check
236     * @param roleQualifiers qualification for assigned roles
237     * @return true if the current user has the permission, false if not or the permission does not exist
238     */
239    public static boolean hasPermDtls(String namespaceCode, String permissionName,
240            Map<String, String> permissionDetails, Map<String, String> roleQualifiers) {
241        Person user = GlobalVariables.getUserSession().getPerson();
242
243        return KimApiServiceLocator.getPermissionService().isAuthorized(user.getPrincipalId(), namespaceCode,
244                permissionName, roleQualifiers);
245    }
246
247    /**
248     * Indicates whether the current user has the permission identified by the given namespace and template name
249     * and with the given details and role qualification
250     *
251     * @param namespaceCode namespace code for the permission to check
252     * @param templateName name of the permission template to find permissions for
253     * @param permissionDetails details for the permission check
254     * @param roleQualifiers qualification for assigned roles
255     * @return true if the current user has a permission with the given template, false if not or
256     *         the permission does not exist
257     */
258    public static boolean hasPermTmpl(String namespaceCode, String templateName, Map<String, String> permissionDetails,
259            Map<String, String> roleQualifiers) {
260        Person user = GlobalVariables.getUserSession().getPerson();
261
262        return KimApiServiceLocator.getPermissionService().isAuthorizedByTemplate(user.getPrincipalId(), namespaceCode,
263                templateName, permissionDetails, roleQualifiers);
264    }
265
266    /**
267     * Gets the next available number from a sequence
268     *
269     * @param sequenceName name of the sequence to retrieve from
270     * @return next sequence value
271     */
272    public static Long sequence(String sequenceName) {
273        DataSource dataSource = (DataSource) ConfigContext.getCurrentContextConfig().getObject(KRADConstants.KRAD_APPLICATION_DATASOURCE);
274        if (dataSource == null) {
275            dataSource = KRADServiceLocator.getKradApplicationDataSource();
276        }
277        return Long.valueOf(MaxValueIncrementerFactory.getIncrementer(dataSource, sequenceName).nextLongValue());
278    }
279
280    /**
281     * Get the a primary key (valid for inquiry/maintenance view retrieval) for the dataObject by class name passed in
282     *
283     * @param dataObjectClassName the class name to get the key for
284     * @return a key valid for use as a request parameter for retrieving an inquiry or maintenance doc
285     */
286    public static String getDataObjectKey(String dataObjectClassName) {
287
288        if (StringUtils.isBlank(dataObjectClassName)) {
289            throw new RuntimeException("getDataObjectKey SpringEL function failed because the class name was blank");
290        }
291
292        Class dataObjectClass = null;
293
294        try {
295            dataObjectClass = Class.forName(dataObjectClassName);
296        } catch (ClassNotFoundException e) {
297            throw new RuntimeException(
298                    "getDataObjectKey SpringEL function failed when trying to find class " + dataObjectClassName, e);
299        }
300
301        // build list of key values from the map parameters
302        List<String> pkPropertyNames = KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(dataObjectClass);
303
304        //return first primary key found
305        if (pkPropertyNames != null && !pkPropertyNames.isEmpty()) {
306            return pkPropertyNames.get(0);
307        }
308
309        //this likely won't be reached, as most should have a primary key (assumption)
310        KualiModuleService kualiModuleService = KRADServiceLocatorWeb.getKualiModuleService();
311        ModuleService moduleService = kualiModuleService.getResponsibleModuleService(dataObjectClass);
312
313        // some classes might have alternate keys defined for retrieving
314        List<List<String>> altKeys = null;
315        if (moduleService != null) {
316            altKeys = moduleService.listAlternatePrimaryKeyFieldNames(dataObjectClass);
317        }
318
319        if (altKeys != null && !altKeys.isEmpty()) {
320            for (List<String> list : altKeys) {
321                if (list != null && !list.isEmpty()) {
322                    //return any key first found
323                    return list.get(0);
324                }
325            }
326        }
327
328        return null;
329    }
330
331    /**
332     * Determines if running in a production environment.
333     *
334     * @return true if running in a production environment
335     */
336    public static boolean isProductionEnvironment() {
337        return ConfigContext.getCurrentContextConfig().isProductionEnvironment();
338    }
339}