/** * (C) 2013-2016 Stephan Rauh http://www.beyondjava.net * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.bootsfaces.beans; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.el.ELContext; import javax.el.ExpressionFactory; import javax.el.MethodExpression; import javax.el.PropertyNotFoundException; import javax.el.ValueExpression; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; /** Collection of helper methods dealing with the JSF Expression language. */ public class ELTools { private final static Pattern EL_EXPRESSION = Pattern.compile("#\\{\\{([A-Z_$€]|[a-z_0-9$€]|\\.)+\\}"); private static Map<String, NGBeanAttributeInfo> beanAttributeInfos = new HashMap<String, NGBeanAttributeInfo>(); /** Caching */ // private static Map<String, Field> fields = new HashMap<String, Field>(); /** Caching */ // private static Map<String, Method> getters = new HashMap<String, Method>(); private static final Logger LOGGER = Logger.getLogger("net.bootsfaces.beans.ELTools"); /** Caching */ // private static Map<String, List<String>> propertyLists = new HashMap<String, List<String>>(); /** * Utility method to create a JSF Value expression from the p_expression string * @param p_expression * @return */ public static ValueExpression createValueExpression(String p_expression) { FacesContext context = FacesContext.getCurrentInstance(); ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory(); ELContext elContext = context.getELContext(); ValueExpression vex = expressionFactory.createValueExpression(elContext, p_expression, Object.class); return vex; } /** * Utility method to create a JSF Value expression from p_expression with exprectedType class as return * @param p_expression * @param expectedType * @return */ public static ValueExpression createValueExpression(String p_expression, Class<?> expectedType) { FacesContext context = FacesContext.getCurrentInstance(); ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory(); ELContext elContext = context.getELContext(); if (null == expectedType) { LOGGER.severe("The expected type of " + p_expression + " is null. Defaulting to String."); expectedType = String.class; } ValueExpression vex = expressionFactory.createValueExpression(elContext, p_expression, expectedType); return vex; } /** * Utility method to create a JSF Method expression * @param p_expression * @param returnType * @param parameterTypes * @return */ public static MethodExpression createMethodExpression(String p_expression, Class<?> returnType, Class<?>... parameterTypes) { FacesContext context = FacesContext.getCurrentInstance(); ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory(); ELContext elContext = context.getELContext(); MethodExpression mex = expressionFactory.createMethodExpression(elContext, p_expression, returnType, parameterTypes); return mex; } /** * Evaluates an EL expression into an object. * * @param p_expression * the expression * @throws PropertyNotFoundException * if the attribute doesn't exist at all (as opposed to being * null) * @return the object */ public static Object evalAsObject(String p_expression) throws PropertyNotFoundException { FacesContext context = FacesContext.getCurrentInstance(); ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory(); ELContext elContext = context.getELContext(); ValueExpression vex = expressionFactory.createValueExpression(elContext, p_expression, Object.class); Object result = vex.getValue(elContext); if (null == result) { // check whether the JSF attributes exists vex.getValueReference(elContext); } return result; } /** * Evaluates an EL expression into a string. * * @param p_expression * the el expression * @return the value */ public static String evalAsString(String p_expression) { FacesContext context = FacesContext.getCurrentInstance(); ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory(); ELContext elContext = context.getELContext(); ValueExpression vex = expressionFactory.createValueExpression(elContext, p_expression, String.class); String result = (String) vex.getValue(elContext); return result; } /** * Get the bean attributes info * @param c * @return */ public static NGBeanAttributeInfo getBeanAttributeInfos(UIComponent c) { String core = getCoreValueExpression(c); synchronized (beanAttributeInfos) { if (beanAttributeInfos.containsKey(c)) { return beanAttributeInfos.get(c); } } NGBeanAttributeInfo info = new NGBeanAttributeInfo(c); synchronized (beanAttributeInfos) { beanAttributeInfos.put(core, info); } return info; } /** * Return the core value expression of a specified component * @param component * @return */ public static String getCoreValueExpression(UIComponent component) { ValueExpression valueExpression = component.getValueExpression("value"); if (null != valueExpression) { String v = valueExpression.getExpressionString(); if (null != v) { Matcher matcher = EL_EXPRESSION.matcher(v); if (matcher.find()) { String exp = matcher.group(); return exp.substring(2, exp.length() - 1); } return v; } } return null; } private static Field getField(String p_expression) { if (p_expression.startsWith("#{") && p_expression.endsWith("}")) { int delimiterPos = p_expression.lastIndexOf('.'); if (delimiterPos < 0) { LOGGER.log(Level.WARNING, "There's no field to access: #{" + p_expression + "}"); return null; } String beanExp = p_expression.substring(0, delimiterPos) + "}"; String fieldName = p_expression.substring(delimiterPos + 1, p_expression.length() - 1); Object container = evalAsObject(beanExp); if (null == container) { LOGGER.severe("Can't read the bean '" + beanExp + "'. Thus JSR 303 annotations can't be read, let alone used by the AngularJS / AngularDart client."); return null; } Class<? extends Object> c = container.getClass(); while (c != null) { Field declaredField; try { declaredField = c.getDeclaredField(fieldName); // synchronized (fields) { // fields.put(p_expression, declaredField); // } return declaredField; } catch (NoSuchFieldException e) { // let\"s try with the super class c = c.getSuperclass(); } catch (SecurityException e) { LOGGER.log(Level.SEVERE, "Unable to access a field", e); e.printStackTrace(); return null; } } } return null; } private static Method getGetter(String p_expression) { // synchronized (getters) { // if (getters.containsKey(p_expression)) { // return getters.get(p_expression); // } // } if (p_expression.startsWith("#{") && p_expression.endsWith("}")) { int delimiterPos = p_expression.lastIndexOf('.'); if (delimiterPos < 0) { LOGGER.log(Level.WARNING, "There's no getter to access: #{" + p_expression + "}"); return null; } String beanExp = p_expression.substring(0, delimiterPos) + "}"; String fieldName = p_expression.substring(delimiterPos + 1, p_expression.length() - 1); String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); String booleanGetterName = "is" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); Object container = evalAsObject(beanExp); if (null == container) { LOGGER.severe("Can't read the bean '" + beanExp + "'. Thus JSR 303 annotations can't be read, let alone used by the AngularJS / AngularDart client."); return null; } Method declaredMethod = findMethod(container, getterName); if (null == declaredMethod) declaredMethod = findMethod(container, booleanGetterName); // synchronized (getters) { // getters.put(p_expression, declaredMethod); // } return declaredMethod; } return null; } private static Method findMethod(Object container, String getterName) { Class<? extends Object> c = container.getClass(); Method declaredField; try { declaredField = c.getMethod(getterName); return declaredField; } catch (NoSuchMethodException e) { // let\"s try with the super class c = c.getSuperclass(); } catch (SecurityException e) { LOGGER.log(Level.SEVERE, "Unable to access a getter for security reasons", e); e.printStackTrace(); } return null; } public static String getNGModel(UIComponent p_component) { String id = ELTools.getCoreValueExpression(p_component); if (id.contains(".")) { int index = id.lastIndexOf("."); id = id.substring(index + 1); } return id; } /** * Yields the type of the variable given by an expression. * * @param p_expression * the expression * @return the type (as class) */ public static Class<?> getType(String p_expression) { Method declaredField = getGetter(p_expression); if (null != declaredField) { return declaredField.getReturnType(); } return null; } /** * Yields the type of the variable displayed by a component. * * @param p_component * the UIComponent * @return the type (as class) */ public static Class<?> getType(UIComponent p_component) { ValueExpression valueExpression = p_component.getValueExpression("value"); if (valueExpression != null) { return getType(valueExpression.getExpressionString()); } return null; } public static boolean hasValueExpression(UIComponent component) { ValueExpression valueExpression = component.getValueExpression("value"); return null != valueExpression; } /** * Is the parameter passed a primitive type (such as int, long, etc) or a * type considered primitive by most programmers (such as String)? * * @param c * the object * @return true if c is a de-facto-primitive */ @SuppressWarnings("unused") private static boolean isPrimitive(Class<? extends Object> c) { return (null == c) || (Class.class == c) || (String.class == c) || c.isPrimitive() || (Integer.class == c) || (Long.class == c) || (Short.class == c) || (Byte.class == c) || (Character.class == c) || (Float.class == c) || (Double.class == c) || (Void.class == c) || (Boolean.class == c); } /** * Which annotations are given to an object described by an EL expression? * * @param p_expression * EL expression of the JSF bean attribute * @return null if there are no annotations, or if they cannot be accessed */ public static Annotation[] readAnnotations(String p_expression) { Field declaredField = getField(p_expression); if (null != declaredField) { if (declaredField.getAnnotations() != null) return declaredField.getAnnotations(); } Method getter = getGetter(p_expression); if (null != getter) { return getter.getAnnotations(); } return null; } /** * Which annotations are given to an object displayed by a JSF component? * * @param p_component * the component * @return null if there are no annotations, or if they cannot be accessed */ public static Annotation[] readAnnotations(UIComponent p_component) { ValueExpression valueExpression = p_component.getValueExpression("value"); if (valueExpression != null) { return readAnnotations(valueExpression.getExpressionString()); } return null; } }