Author: nbelaevski
Date: 2009-12-07 14:36:23 -0500 (Mon, 07 Dec 2009)
New Revision: 16093
Modified:
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/ELParserUtils.java
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/node/AstFunctionTreeNode.java
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/node/ConstantValueTreeNode.java
Log:
https://jira.jboss.org/jira/browse/RF-77732
Modified:
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/ELParserUtils.java
===================================================================
---
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/ELParserUtils.java 2009-12-07
15:36:38 UTC (rev 16092)
+++
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/ELParserUtils.java 2009-12-07
19:36:23 UTC (rev 16093)
@@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+
+
package org.richfaces.cdk.parser.el;
import java.beans.BeanInfo;
@@ -33,6 +35,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -97,8 +100,8 @@
/**
* Class, that encapsulate all functionality, related to Reflection calls, such as
loading classes, get property
* descriptors etc...
+ * @author amarkhel
*
- * @author amarkhel
*/
public final class ELParserUtils {
@@ -151,8 +154,7 @@
private static Map<Class<?>, ClassDataHolder> classDataCache =
new WeakHashMap<Class<?>, ClassDataHolder>();
- private ELParserUtils() {
- }
+ private ELParserUtils() { }
public static void verifyClassPropertiesAndMethods(Class<?> initialClass)
throws ParsingException {
resolveClassPropertiesAndMethods(initialClass);
@@ -172,10 +174,9 @@
/**
* This method determine type of parsed node and create wrapper for them, that
extends AbstractTreeNode.
* If node type is not recognized - throws ParsingException.
- *
* @param child - parsed node
+ * @throws ParsingException - if node type is not recognized.
* @return wrapper for parsed node(if node type is recognized), that implement
ITreeNode interface.
- * @throws ParsingException - if node type is not recognized.
*/
public static ITreeNode determineNodeType(Node child) throws ParsingException {
ITreeNode treeNode = null;
@@ -255,14 +256,14 @@
/**
* This method return PropertyDescriptor by specified propertyName and clazz.
- *
- * @param clazz - class to search
+ * @param clazz - class to search
* @param propertyName - propertyName to search
* @return property descriptor if found.
* @throws ParsingException if error occured.
*/
- public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String
propertyName)
+ public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String
propertyName)
throws ParsingException {
+
if (clazz == null) {
return null;
}
@@ -273,9 +274,9 @@
/**
* Returns wrapper classes for passed-in class. If type is primitive, then
corresponding
- * wrapper class is returned (e.g. boolean -> Boolean), otherwise does nothing and
returns
+ * wrapper class is returned (e.g. boolean -> Boolean), otherwise does nothing and
returns
* passed-in class.
- *
+ *
* @return wrapper for primitive types, or passed-in class
*/
private static Class<?> getWrapperClass(Class<?> inClazz) {
@@ -294,13 +295,14 @@
* <p>Retrieve the property descriptors for the specified class,
* introspecting and caching them the first time a particular bean class
* is encountered.</p>
- * <p/>
+ *
* <p><strong>FIXME</strong> - Does not work with
DynaBeans.</p>
*
* @param beanClass Bean class for which property descriptors are requested
* @return the property descriptors
- * @throws ParsingException if error occured.
- * @throws IllegalArgumentException if <code>beanClass</code> is null
+ * @throws ParsingException if error occured.
+ *
+ * @exception IllegalArgumentException if <code>beanClass</code> is null
*/
private static PropertyDescriptor[] getPropertyDescriptors(Class<?> beanClass)
throws ParsingException {
if (beanClass == null) {
@@ -337,26 +339,26 @@
* the given parameters.
* In other words, it finds a method with the given name
* that will take the parameters given.<p>
- * <p/>
+ *
* <p>This method is slightly undeterminstic since it loops
* through methods names and return the first matching method.</p>
- * <p/>
+ *
* <p>This method is used by
* {@link
* #invokeMethod(Object object,String methodName,Object [] args,Class[]
parameterTypes)}.
- * <p/>
+ *
* <p>This method can match primitive parameter by passing in wrapper classes.
* For example, a <code>Boolean</code> will match a primitive
<code>boolean</code>
* parameter.
*
- * @param clazz find method in this class
- * @param methodName find method with this name
+ * @param clazz find method in this class
+ * @param methodName find method with this name
* @param parameterTypes find method with compatible parameters
* @return The accessible method
* @throws ParsingException if error occured.
*/
- public static Type getMatchingVisibleMethodReturnType(Class<?> clazz, final
String methodName,
- Type[] parameterTypes) throws
ParsingException {
+ public static Type getMatchingVisibleMethodReturnType(Class<?> clazz, final
String methodName,
+ Type[] parameterTypes) throws ParsingException {
if (clazz == null) {
return Object.class;
@@ -371,7 +373,7 @@
float bestMatchCost = Float.MAX_VALUE;
float myCost = Float.MAX_VALUE;
- for (Method resolvedMethod : resolvedMethods) {
+ for (Method resolvedMethod: resolvedMethods) {
if (!isMethodVisible(resolvedMethod)) {
continue;
}
@@ -416,8 +418,7 @@
/**
* Returns the sum of the object transformation cost for each class in the source
* argument list.
- *
- * @param srcArgs The source arguments
+ * @param srcArgs The source arguments
* @param destArgs The destination arguments
* @return The total transformation cost
*/
@@ -434,48 +435,78 @@
return totalCost;
}
+ private static Set<Class<?>> getAllInterfaces(Class<?> clazz)
throws ParsingException {
+ final Set<Class<?>> result = new
LinkedHashSet<Class<?>>();
+
+ new ClassWalkingLogic(clazz).walk(new ClassVisitor() {
+
+ @Override
+ public void visit(Class<?> clazz) throws ParsingException {
+ if (clazz.isInterface()) {
+ result.add(clazz);
+ }
+ }
+
+ });
+
+ return result;
+ }
+
/**
* Gets the number of steps required needed to turn the source class into the
* destination class. This represents the number of steps in the object hierarchy
* graph.
- *
- * @param srcClass The source class
+ * @param srcClass The source class
* @param destClass The destination class
* @return The cost of transforming an object
*/
private static float getObjectTransformationCost(Type destType, Type srcType) {
float cost = 0.0f;
- if (ELParserUtils.isNullType(srcType)) {
+ //no need to check if destType is primitive because the check has already been
done
+ //by isAssignmentCompatible() method
+ if (isNullType(srcType)) {
return cost;
}
- Class<?> srcClass = ELParserUtils.getRawType(srcType);
- Class<?> destClass = ELParserUtils.getRawType(destType);
+ Class<?> srcClass = getWrapperClass(getRawType(srcType));
+ Class<?> destClass = getWrapperClass(getRawType(destType));
+ if (srcClass != null && !srcClass.equals(destClass)) {
+ boolean interfaceMatched = false;
+ try {
+ Set<Class<?>> interfaces = getAllInterfaces(srcClass);
+ for (Class<?> iface : interfaces) {
+ if (isAssignmentCompatible(destClass, iface)) {
+ // slight penalty for interface match.
+ // we still want an exact match to override an interface match,
but
+ // an interface match should override anything where we have to
get a
+ // superclass.
+ cost += 0.25f;
+ interfaceMatched = true;
- while (srcClass != null &&
!getWrapperClass(destClass).equals(getWrapperClass(srcClass))) {
- if (destClass.isInterface() && isAssignmentCompatible(destClass,
srcClass)) {
+ break;
+ }
+ }
+ } catch (ParsingException e) {
+ // TODO: handle exception
+ }
- // slight penalty for interface match.
- // we still want an exact match to override an interface match, but
- // an interface match should override anything where we have to get a
- // superclass.
- cost += 0.25f;
+ if (!interfaceMatched) {
+ while (srcClass != null && !srcClass.equals(destClass)) {
+ cost++;
+ srcClass = srcClass.getSuperclass();
+ }
- break;
+ /*
+ * If the destination class is null, we've travelled all the way up
to
+ * an Object match. We'll penalize this by adding 1.5 to the cost.
+ */
+ if (srcClass == null) {
+ cost += 1.5f;
+ }
}
-
- cost++;
- srcClass = srcClass.getSuperclass();
}
- /*
- * If the destination class is null, we've travelled all the way up to
- * an Object match. We'll penalize this by adding 1.5 to the cost.
- */
- if (srcClass == null) {
- cost += 1.5f;
- }
return cost;
}
@@ -483,7 +514,7 @@
/**
* <p>Determine whether a type can be used as a parameter in a method
invocation.
* This method handles primitive conversions correctly.</p>
- * <p/>
+ *
* <p>In order words, it will match a <code>Boolean</code> to a
<code>boolean</code>,
* a <code>Long</code> to a <code>long</code>,
* a <code>Float</code> to a <code>float</code>,
@@ -492,19 +523,20 @@
* Now logic widening matches are allowed.
* For example, a <code>Long</code> will not match a
<code>int</code>.
*
- * @param parameterType the type of parameter accepted by the method
+ * @param parameterType the type of parameter accepted by the method
* @param parameterization the type of parameter being tested
+ *
* @return true if the assignement is compatible.
*/
public static boolean isAssignmentCompatible(Type parameterType, Type
parameterization) {
- Class<?> parameterClass = ELParserUtils.getRawType(parameterType);
+ Class<?> parameterClass = getRawType(parameterType);
- if (ELParserUtils.isNullType(parameterization)) {
+ if (isNullType(parameterization)) {
//null value can be assigned to any class except primitive
return !parameterClass.isPrimitive();
} else {
Class<?> parameterWrappedClass = getWrapperClass(parameterClass);
- Class<?> parameterizationWrappedClass =
getWrapperClass(ELParserUtils.getRawType(parameterization));
+ Class<?> parameterizationWrappedClass =
getWrapperClass(getRawType(parameterization));
return parameterWrappedClass.isAssignableFrom(parameterizationWrappedClass);
}
}
@@ -512,10 +544,9 @@
/**
* Gets the wrapper object class for the given primitive type class.
* For example, passing <code>boolean.class</code> returns
<code>Boolean.class</code>
- *
* @param primitiveType the primitive type class for which a match is to be found
* @return the wrapper type associated with the given primitive
- * or null if no match is found
+ * or null if no match is found
*/
private static Class<?> getPrimitiveWrapper(Class<?> primitiveType) {
@@ -547,6 +578,7 @@
}
static class ClassWalkingLogic {
+
private Queue<Class<?>> classesList = new
LinkedList<Class<?>>();
private Set<Class<?>> visitedClasses = new
HashSet<Class<?>>();
@@ -562,29 +594,36 @@
while (!classesList.isEmpty()) {
Class<?> clazz = classesList.remove();
- visitor.visit(clazz);
+ if (visitedClasses.add(clazz)) {
+ visitor.visit(clazz);
- Class<?> superclass = clazz.getSuperclass();
- if (superclass != null) {
- if (visitedClasses.add(superclass)) {
- classesList.add(superclass);
+ Class<?> superclass = clazz.getSuperclass();
+ if (superclass != null) {
+ if (!visitedClasses.contains(superclass)) {
+ classesList.add(superclass);
+ }
}
- }
- Class<?>[] interfaces = clazz.getInterfaces();
- if (interfaces != null) {
- for (Class<?> iface : interfaces) {
- if (visitedClasses.add(superclass)) {
- classesList.add(iface);
+ Class<?>[] interfaces = clazz.getInterfaces();
+ if (interfaces != null) {
+ for (Class<?> iface : interfaces) {
+ if (!visitedClasses.contains(iface)) {
+ classesList.add(iface);
+ }
}
}
}
}
- //this is for the case when we started just from interface, not class
+ //While interfaces do not have Object.class in their hierarchy directly,
+ //implementations of interface are always inherited from Object.
+ //As methods in this class are primarily designed to work with
implementations (beans),
+ //we are adding Object.class explicitly if it hasn't been visited yet.
if (visitedClasses.add(Object.class)) {
visitor.visit(Object.class);
}
+
+ visitedClasses.clear();
}
}
@@ -593,6 +632,7 @@
}
public static Type getLastTypeArgument(Type type) {
+ //TODO: generic arrays support
Type[] actualTypeArguments = null;
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
@@ -607,6 +647,7 @@
}
public static Class<?> getRawType(Type type) {
+ //TODO: generic arrays support
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
return (Class<?>) parameterizedType.getRawType();
Modified:
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/node/AstFunctionTreeNode.java
===================================================================
---
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/node/AstFunctionTreeNode.java 2009-12-07
15:36:38 UTC (rev 16092)
+++
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/node/AstFunctionTreeNode.java 2009-12-07
19:36:23 UTC (rev 16093)
@@ -19,6 +19,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+
+
package org.richfaces.cdk.parser.el.node;
import java.lang.reflect.Type;
@@ -35,6 +37,7 @@
* This class extend AbstractTreeNode and wrap AstFunction node.
*
* @author amarkhel
+ *
*/
public class AstFunctionTreeNode extends AbstractTreeNode {
public AstFunctionTreeNode(Node node) {
@@ -47,13 +50,18 @@
String functionPrefix = functionNode.getPrefix();
String functionName = functionNode.getLocalName();
+ String variableName;
if (functionPrefix != null && functionPrefix.length() != 0) {
//TODO: this should be a property getter, not property name
//TODO: support "super" keyword
- sb.append(functionPrefix);
+ variableName = functionPrefix;
} else {
- sb.append(ELNodeConstants.THIS_PREFIX);
+ variableName = ELNodeConstants.THIS_PREFIX;
}
+
+ sb.append(variableName);
+ visitor.setVariableType(context.get(variableName));
+
sb.append(ELNodeConstants.DOT);
sb.append(functionName);
Modified:
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/node/ConstantValueTreeNode.java
===================================================================
---
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/node/ConstantValueTreeNode.java 2009-12-07
15:36:38 UTC (rev 16092)
+++
root/cdk/trunk/plugins/generator/src/main/java/org/richfaces/cdk/parser/el/node/ConstantValueTreeNode.java 2009-12-07
19:36:23 UTC (rev 16093)
@@ -31,18 +31,19 @@
/**
* @author Nick Belaevski
+ *
*/
public final class ConstantValueTreeNode extends AbstractTreeNode {
- //XXX what class to use for null object: Void.class or null - null will be used
- public static final ConstantValueTreeNode NULL_NODE =
- new ConstantValueTreeNode(ELNodeConstants.NULL_VALUE, NullType.INSTANCE);
+ // XXX what class to use for null object: Void.class or null - special NullType be
used
+ public static final ConstantValueTreeNode NULL_NODE = new
ConstantValueTreeNode(ELNodeConstants.NULL_VALUE,
+ NullType.INSTANCE);
- public static final ConstantValueTreeNode TRUE_NODE =
- new ConstantValueTreeNode(ELNodeConstants.TRUE_VALUE, Boolean.TYPE);
+ public static final ConstantValueTreeNode TRUE_NODE = new
ConstantValueTreeNode(ELNodeConstants.TRUE_VALUE,
+ Boolean.TYPE);
- public static final ConstantValueTreeNode FALSE_NODE =
- new ConstantValueTreeNode(ELNodeConstants.FALSE_VALUE, Boolean.TYPE);
+ public static final ConstantValueTreeNode FALSE_NODE = new
ConstantValueTreeNode(ELNodeConstants.FALSE_VALUE,
+ Boolean.TYPE);
private final String value;
@@ -55,13 +56,14 @@
this.type = type;
}
- /* (non-Javadoc)
- * @see
org.richfaces.cdk.parser.el.node.AbstractTreeNode#visit(java.lang.StringBuilder,
java.util.Map,
- * org.richfaces.cdk.parser.el.ELVisitor)
- */
+ /*
+ * (non-Javadoc)
+ *
+ * @see
org.richfaces.cdk.parser.el.node.AbstractTreeNode#visit(java.lang.StringBuilder,
java.util.Map,
+ * org.richfaces.cdk.parser.el.ELVisitor)
+ */
@Override
- public void visit(StringBuilder sb, Map<String, Type> context,
- ELVisitor visitor) throws ParsingException {
+ public void visit(StringBuilder sb, Map<String, Type> context, ELVisitor
visitor) throws ParsingException {
sb.append(value);
visitor.setVariableType(type);