[jboss-cvs] JBossAS SVN: r63629 - projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Mon Jun 25 00:13:26 EDT 2007
Author: flavia.rainone at jboss.com
Date: 2007-06-25 00:13:26 -0400 (Mon, 25 Jun 2007)
New Revision: 63629
Added:
projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/Algorithm.java
projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/ContextualizedArguments.java
projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/VariableNode.java
Log:
[JBAOP-420] First version of algorithm, partially tested (works on all simple scenarios, and need to tested on more complex ones)
Added: projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/Algorithm.java
===================================================================
--- projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/Algorithm.java (rev 0)
+++ projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/Algorithm.java 2007-06-25 04:13:26 UTC (rev 63629)
@@ -0,0 +1,264 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.aop.advice.annotation;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="flavia.rainone at jboss.com">Flavia Rainone</a>
+ *
+ */
+public class Algorithm
+{
+ private static final Algorithm INSTANCE = new Algorithm();
+ public static Algorithm getInstance()
+ {
+ return INSTANCE;
+ }
+
+ public Map<String, VariableNode> getInitialHierarchy()
+ {
+ return new HashMap<String, VariableNode>();
+ }
+
+ public boolean isAssignable(Type type, Type fromType,
+ Map<String, VariableNode> variableHierarchy)
+ {
+ if (type instanceof Class)
+ {
+ return isAssignable((Class<?>) type, fromType, variableHierarchy);
+ }
+ if (type instanceof ParameterizedType)
+ {
+ return isAssignable((ParameterizedType) type, fromType,
+ variableHierarchy);
+ }
+ if (type instanceof TypeVariable)
+ {
+ VariableNode node = null;
+ TypeVariable fromVariable = (TypeVariable) type;
+ if (variableHierarchy.containsKey(fromVariable.getName()))
+ {
+ node = variableHierarchy.get(fromVariable.getName());
+ } else
+ {
+ node = new VariableNode(fromVariable, variableHierarchy);
+ }
+ return node.addLowerBound(fromType);
+ }
+ if (type instanceof WildcardType)
+ {
+ throw new RuntimeException("This comparison should never happen");
+ } else
+ {
+ return isAssignable((GenericArrayType) type, fromType,
+ variableHierarchy);
+ }
+ }
+
+ // is classType super of fromType?
+ private boolean isAssignable(Class<?> classType, Type fromType,
+ Map<String, VariableNode> variableHierarchy)
+ {
+ if (fromType instanceof Class)
+ {
+ return classType.isAssignableFrom((Class<?>) fromType);
+ }
+ else if (fromType instanceof ParameterizedType)
+ {
+ return classType.isAssignableFrom(
+ (Class<?>) ((ParameterizedType) fromType).getRawType());
+ }
+ else if (fromType instanceof TypeVariable)
+ {
+ Type[] bounds = getConcreteBounds(fromType);
+ boolean inside = false;
+ for (int i = 0; i < bounds.length && !inside; i++)
+ {
+ if (bounds[i] instanceof Class)
+ {
+ if (classType.isAssignableFrom((Class<?>) bounds[i]))
+ {
+ inside = true;
+ }
+ }
+ else
+ {
+ // bound must be a parameterized type
+ if (classType.isAssignableFrom(
+ (Class<?>) ((ParameterizedType) bounds[i]).getRawType()))
+ {
+ inside = true;
+ }
+ }
+ }
+ return inside;
+ }
+ // type instanceof GenericArrayType (ommitting check for performance
+ // reasons)
+ if (classType == Object.class)
+ {
+ return true;
+ }
+ if (classType.isArray())
+ {
+ return isAssignable(classType.getComponentType(),
+ ((GenericArrayType) fromType).getGenericComponentType(),
+ variableHierarchy);
+ }
+ return false;
+ }
+
+ /**
+ * @param type
+ * @return
+ */
+ private Type[] getConcreteBounds(Type type)
+ {
+ TypeVariable current = (TypeVariable) type;
+ Type[] bounds = current.getBounds();
+ while (bounds.length == 1 && bounds[0] instanceof TypeVariable)
+ {
+ current = (TypeVariable) bounds[0];
+ bounds = current.getBounds();
+ }
+ return bounds;
+ }
+
+ private boolean isAssignable(ParameterizedType paramType, Type fromType,
+ Map<String, VariableNode> variableHierarchy)
+ {
+ if (fromType instanceof TypeVariable)
+ {
+ Type[] concreteBounds = getConcreteBounds((TypeVariable) fromType);
+ for (int i = 0; i < concreteBounds.length; i++)
+ {
+ if (isAssignable(paramType, concreteBounds[i], variableHierarchy))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ return ContextualizedArguments.isAssignable(CHECKER, paramType, fromType, variableHierarchy);
+ }
+
+ private boolean isAssignable(GenericArrayType arrayType, Type fromType,
+ Map<String, VariableNode> variableHierarchy)
+ {
+ if (fromType instanceof Class)
+ {
+ Class<?> fromClass = (Class<?>) fromType;
+ if (!fromClass.isArray())
+ {
+ return false;
+ }
+ return isAssignable(arrayType.getGenericComponentType(),
+ fromClass.getComponentType(), variableHierarchy);
+ }
+ if (fromType instanceof GenericArrayType)
+ {
+ GenericArrayType fromArrayType = (GenericArrayType) fromType;
+ return isAssignable(arrayType.getGenericComponentType(),
+ fromArrayType.getGenericComponentType(), variableHierarchy);
+ }
+ return false;
+ }
+
+ // ////////////////////////////////////////////////////////
+ private static final ContextualizedArguments.EqualityChecker<Map<String, VariableNode>> CHECKER
+ = new ContextualizedArguments.EqualityChecker<Map<String,VariableNode>>()
+ {
+
+ public boolean isSame(Type type2, Type type1, Map<String, VariableNode> variableHierarchy)
+ {
+ if (type2 instanceof TypeVariable)
+ {
+ TypeVariable variable2 = (TypeVariable) type2;
+ VariableNode node = variableHierarchy.containsKey(variable2.getName())?
+ variableHierarchy.get(variable2.getName()):
+ new VariableNode(variable2, variableHierarchy);
+ return node.assignValue(type1);
+ }
+ if (type2 instanceof Class)
+ {
+ return type1.equals(type2);
+ }
+ if (type2 instanceof ParameterizedType)
+ {
+ if (!(type1 instanceof ParameterizedType))
+ {
+ return false;
+ }
+ ParameterizedType paramType1 = (ParameterizedType) type1;
+ ParameterizedType paramType2 = (ParameterizedType) type2;
+ if (!isSame(paramType2.getRawType(), paramType1.getRawType(), variableHierarchy))
+ {
+ return false;
+ }
+ return isSame(paramType2.getActualTypeArguments(), paramType1.getActualTypeArguments(), variableHierarchy);
+ }
+ if (type2 instanceof WildcardType)
+ {
+ Type[] upperBounds2 = ((WildcardType) type2).getUpperBounds();
+ Algorithm algorithm = Algorithm.getInstance();
+ if (type1 instanceof WildcardType)
+ {
+ Type[] upperBounds1 = ((WildcardType) type1).getUpperBounds();
+ outer: for (int i = 0; i < upperBounds2.length; i++)
+ {
+ for (int j = 0; j < upperBounds1.length; j++)
+ {
+ if (algorithm.isAssignable(upperBounds2[i], upperBounds1[i], variableHierarchy))
+ {
+ continue outer;
+ }
+ }
+ return false;
+ }
+ // TODO lower bounds: inverted algorithm
+ return true;
+ }
+ else
+ {
+ for (int i = 0; i < upperBounds2.length; i++)
+ {
+ if (!algorithm.isAssignable(upperBounds2[i], type1, variableHierarchy))
+ {
+ return false;
+ }
+ }
+ // TODO lower bounds: inverted algorithm
+ return true;
+ }
+ }
+ return true;
+ }
+ };
+
+}
\ No newline at end of file
Added: projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/ContextualizedArguments.java
===================================================================
--- projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/ContextualizedArguments.java (rev 0)
+++ projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/ContextualizedArguments.java 2007-06-25 04:13:26 UTC (rev 63629)
@@ -0,0 +1,444 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.aop.advice.annotation;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+/**
+ * @author <a href="flavia.rainone at jboss.com">Flavia Rainone</a>
+ *
+ */
+class ContextualizedArguments
+{
+ static abstract class EqualityChecker<T>
+ {
+ protected boolean isSame(Type[] arguments, Type[] assignableArguments, T token)
+ {
+ for (int i = 0; i < arguments.length; i++)
+ {
+ if (!isSame(arguments[i], assignableArguments[i], token))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ abstract boolean isSame(Type argument, Type assignableArgument, T token);
+ }
+
+
+ public static<T> boolean isAssignable(EqualityChecker<T> action, ParameterizedType paramType, Type assignable, T token)
+ {
+ Class<?> typeRaw = null;
+ ParameterizedType assignableParamType = null;
+ Class<?> desiredType = (Class<?>) paramType.getRawType();
+ if (assignable instanceof Class)
+ {
+ typeRaw = (Class<?>) assignable;
+ if (!desiredType.isAssignableFrom(typeRaw))
+ {
+ return false;
+ }
+ if (typeRaw.getTypeParameters().length > 0)
+ {
+ // notice that, if typeClass equals the fromParamType, we also have
+ // the
+ // result true with a warning (i.e., this if is not only for
+ // typeClass
+ // subclass of fromParamType, but also for typeCass same as
+ // fromParamType raw
+ return true;// TODO With warning
+ }
+ }
+ else if (assignable instanceof ParameterizedType)
+ {
+ assignableParamType = (ParameterizedType) assignable;
+ typeRaw = (Class<?>) assignableParamType.getRawType();
+ if (typeRaw == desiredType)
+ {
+ // compare arguments with arguments
+ return action.isSame(paramType.getActualTypeArguments(),
+ assignableParamType.getActualTypeArguments(), token);
+ }
+ else if (!desiredType.isAssignableFrom(typeRaw))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ // try to get, if null, warning, parameters lost in hierarchy
+ Type[] arguments = getContextualizedArguments(assignableParamType, typeRaw, desiredType);
+ if (arguments == null)
+ {
+ return true; // TODO with Warning
+ }
+ return action.isSame(paramType.getActualTypeArguments(), arguments, token);
+ }
+
+
+
+
+ public static final Type[] getContextualizedArguments(ParameterizedType paramType,
+ Class rawType, Class desiredType)
+ {
+ ContextualizedArguments contextualizedArguments = getContextualizedArgumentsInternal(
+ desiredType, rawType);
+ if (contextualizedArguments == null)
+ {
+ return null;
+ }
+ if (paramType != null)
+ {
+ contextualizedArguments.contextualizeVariables(null, paramType);
+ }
+ return contextualizedArguments.getArguments();
+ }
+
+ private static final ContextualizedArguments getContextualizedArgumentsInternal(
+ Class<?> desiredType, Class<?> classType)
+ {
+ Type superType = null;
+ if (desiredType.isInterface())
+ {
+ for (Type superInterface : classType.getGenericInterfaces())
+ {
+ if ((superInterface instanceof Class && desiredType
+ .isAssignableFrom((Class<?>) superInterface))
+ || (superInterface instanceof ParameterizedType &&
+ desiredType.isAssignableFrom((Class<?>)
+ ((ParameterizedType) superInterface).getRawType())))
+ {
+ superType = superInterface;
+ break;
+ }
+ }
+ }
+ else
+ {
+ superType = classType.getGenericSuperclass();
+ }
+ ContextualizedArguments result = null;
+ if (superType instanceof Class)
+ {
+ if (superType == desiredType)
+ {
+ return null;
+ }
+ result = getContextualizedArgumentsInternal(desiredType,
+ (Class<?>) superType);
+ }
+ else
+ {
+ ParameterizedType superParamType = (ParameterizedType) superType;
+ Class<?> superClassType = (Class<?>) superParamType.getRawType();
+ if (superClassType == desiredType)
+ {
+ return new ContextualizedArguments(superParamType
+ .getActualTypeArguments(), classType);
+ }
+ else
+ {
+ result = getContextualizedArgumentsInternal(desiredType,
+ superClassType);
+ }
+ }
+ if (result == null
+ || !result.contextualizeVariables(classType, superType))
+ {
+ return null;
+ }
+ return result;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ private static ReplacementRecorder<ContextualizedArguments> replacementCreator = new ReplacementRecorder<ContextualizedArguments>()
+ {
+ public void recordReplacement(ContextualizedArguments outer, int variableIndex, Type[] replacementTarget, int targetIndex)
+ {
+ outer.initialize();
+ outer.createVariableReplacement(variableIndex, outer.arguments, targetIndex);
+ }
+ };
+
+ private Type[] arguments;
+ //private Collection<VariableReplacement> variableReplacements;
+ private LinkedList<VariableReplacement> variableReplacements;
+
+ // declaring class extends queried class (DeclaringClass<A, B, C> extends Queried<X, Y, Z, ..., W>,
+ // where X, Y, Z...W, are a list of types for which we need to map (contextualize)
+ // variables
+ // A, B, C... D are variables of DeclaringClass, that may be used in the contextualization proccess
+ public ContextualizedArguments(Type[] result, Class<?> declaringClass)
+ {
+ this.arguments = result;
+ for (int i = 0; i < result.length; i++)
+ {
+ Type newArgument = processArgument(result, i, declaringClass,
+ replacementCreator, this);
+ if (newArgument != null)
+ {
+ this.arguments[i] = newArgument;
+ }
+ }
+ }
+
+ private static <O> Type processArgument(Type[] argumentContainer, int argumentIndex,
+ Class<?> declaringClass, ReplacementRecorder<O> recorder, O outer)
+ {
+ Type argument = argumentContainer[argumentIndex];
+ if (argument instanceof Class)
+ {
+ return null;
+ }
+ if (argument instanceof ParameterizedType)
+ {
+ ParameterizedType paramType = (ParameterizedType) argument;
+ ParameterizedType_ newParamType = null;
+ Type[] arguments = paramType.getActualTypeArguments();
+ for (int i = 0; i < arguments.length; i++)
+ {
+ Type newType = processArgument(arguments, i, declaringClass, recorder,
+ outer);
+ if (newType != null)
+ {
+ if (newParamType == null)
+ {
+ newParamType = new ParameterizedType_(paramType);
+ }
+ newParamType.getActualTypeArguments()[i] = newType;
+ }
+ }
+ return newParamType;
+ }
+ //else if (bounds[i] instanceof TypeVariable)
+ if (declaringClass == null)
+ {
+ return null;
+ }
+ String paramName = ((TypeVariable) argument).getName();
+ int index = 0;
+ TypeVariable[] typeVariables = declaringClass.getTypeParameters();
+ for (index = 0; index < typeVariables.length; index ++)
+ {
+ if (typeVariables[index].getName().equals(paramName)){
+ break;
+ }
+ }
+ recorder.recordReplacement(outer, index, argumentContainer, argumentIndex);
+ return argument;
+ }
+
+ boolean initialized = false;
+
+ private void initialize()
+ {
+ if (!initialized)
+ {
+ Type[] oldResult = this.arguments;
+ this.arguments = new Type[oldResult.length];
+ System.arraycopy(oldResult, 0, this.arguments, 0, this.arguments.length);
+ this.variableReplacements = new LinkedList<VariableReplacement>();
+ initialized = true;
+ }
+ }
+ private ListIterator<VariableReplacement> iterator;
+ // newDeclaringClass extends/implements oldDeclaringType
+ // returns false = warning (work with raw type hence)
+ public boolean contextualizeVariables(Class newDeclaringClass, Type oldDeclaringType)
+ {
+ if (!initialized || variableReplacements.isEmpty())
+ {
+ initialized = false;
+ return true;
+ }
+ if (oldDeclaringType instanceof Class)
+ {
+ return false; // warning
+ }
+ ParameterizedType oldParamType = (ParameterizedType) oldDeclaringType;
+ for (iterator = variableReplacements.listIterator(); iterator.hasNext(); )
+ {
+ if(iterator.next().replace(oldParamType, newDeclaringClass))
+ {
+ iterator.remove();
+ }
+ }
+ iterator = null;
+ return true;
+ }
+
+ public Type[] getArguments()
+ {
+ return this.arguments;
+ }
+
+ public void createVariableReplacement(int variableIndex, Type[] replacementTarget,
+ int targetIndex)
+ {
+ if (iterator != null)
+ {
+ iterator.add(new VariableReplacement(variableIndex, replacementTarget,
+ targetIndex));
+ }
+ else
+ {
+ this.variableReplacements.add(new VariableReplacement(
+ variableIndex, replacementTarget, targetIndex));
+ }
+ }
+ private static ReplacementRecorder<VariableReplacement> updater = new ReplacementRecorder<VariableReplacement>()
+ {
+ public void recordReplacement(VariableReplacement outer, int variableIndex, Type[] replacementTarget, int targetIndex)
+ {
+ if (outer.pendingReplacement) // outer is already busy
+ {
+ outer.getOuter().createVariableReplacement(variableIndex,
+ replacementTarget, targetIndex);
+ }
+ else
+ {
+ outer.variableIndex = variableIndex;
+ outer.target = replacementTarget;
+ outer.targetIndex = targetIndex;
+ outer.pendingReplacement = true;
+ }
+ }
+ };
+
+ class VariableReplacement
+ {
+ private int variableIndex;
+ private Type[] target;
+ private int targetIndex;
+ private boolean pendingReplacement;
+
+ public VariableReplacement(int variableIndex, Type[] target, int targetIndex)
+ {
+ this.variableIndex = variableIndex;
+ this.target = target;
+ this.targetIndex = targetIndex;
+ this.pendingReplacement = true;
+ }
+
+ // return true if replacement has been done for good
+ public boolean replace(ParameterizedType paramType, Class<?> declaringClass)
+ {
+ target[targetIndex] = paramType.getActualTypeArguments()[variableIndex];
+ this.pendingReplacement = false;
+ Type newType = ContextualizedArguments.processArgument(target, targetIndex,
+ declaringClass, updater, this);
+ if (newType != null)
+ {
+ target[targetIndex] = newType;
+ return false;
+ }
+ return true;
+ }
+
+ ContextualizedArguments getOuter()
+ {
+ return ContextualizedArguments.this;
+ }
+ }
+
+ private static class ParameterizedType_ implements ParameterizedType
+ {
+ private Type[] arguments;
+ private Type ownerType;
+ private Type rawType;
+
+ ParameterizedType_(ParameterizedType type)
+ {
+ Type[] actualArguments = type.getActualTypeArguments();
+ this.arguments = new Type[actualArguments.length];
+ System.arraycopy(actualArguments, 0, arguments, 0, actualArguments.length);
+ this.ownerType = type.getOwnerType();
+ this.rawType = type.getRawType();
+ }
+
+
+ public Type[] getActualTypeArguments()
+ {
+ return this.arguments;
+ }
+
+ public Type getOwnerType()
+ {
+ return this.ownerType;
+ }
+
+ public Type getRawType()
+ {
+ return this.rawType;
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (!(obj instanceof ParameterizedType))
+ {
+ return false;
+ }
+ ParameterizedType other = (ParameterizedType) obj;
+ if (!this.ownerType.equals(other.getOwnerType()) ||
+ !this.rawType.equals(other.getRawType()))
+ {
+ return false;
+ }
+ Type[] otherArguments = other.getActualTypeArguments();
+ for (int i = 0; i < arguments.length; i++)
+ {
+ if (!arguments[i].equals(otherArguments[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ interface ReplacementRecorder<T>
+ {
+ public void recordReplacement(T outer, int variableIndex, Type[] replacementTarget, int targetIndex);
+
+ }
+}
\ No newline at end of file
Added: projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/VariableNode.java
===================================================================
--- projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/VariableNode.java (rev 0)
+++ projects/aop/trunk/aop/src/main/org/jboss/aop/advice/annotation/VariableNode.java 2007-06-25 04:13:26 UTC (rev 63629)
@@ -0,0 +1,366 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.aop.advice.annotation;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * @author <a href="flavia.rainone at jboss.com">Flavia Rainone</a>
+ *
+ */
+public class VariableNode
+{
+ private VariableNode previous;
+ private VariableNode next;
+ private Collection<Type> lowerBounds;
+ private TypeVariable variable;
+ private Type assignedValue;
+
+ public VariableNode(TypeVariable content, Map<String, VariableNode> map)
+ {
+ this.variable = content;
+ lowerBounds = new HashSet<Type>();
+ Type[] bounds = content.getBounds();
+ if (bounds.length == 1 && bounds[0] instanceof TypeVariable)
+ {
+ TypeVariable typeVariable = (TypeVariable) bounds[0];
+ if (map.containsKey(typeVariable.getName()))
+ {
+ next = map.get(typeVariable.getName());
+ }
+ else
+ {
+ next = new VariableNode(typeVariable, map);
+ }
+ next.previous = this;
+ }
+ map.put(content.getName(), this);
+ }
+
+ public final boolean assignValue(Type value)
+ {
+ if (this.assignedValue != null)
+ {
+ return isSame(this.assignedValue, value);
+ }
+ if (!isInsideBounds(value, true) ||
+ (this.previous != null && !this.previous.areBoundsInside(value)))
+ {
+ return false;
+ }
+ this.assignedValue = value;
+ return true;
+ }
+
+ public final boolean addLowerBound(Type lowerBound)
+ {
+ if (!isInsideBounds(lowerBound, false) ||
+ (this.previous != null && !this.previous.areBoundsInside(lowerBound)))
+ {
+ return false;
+ }
+ // TODO check this
+ if (lowerBound instanceof TypeVariable)
+ {
+ Type[] bounds = ((TypeVariable) lowerBound).getBounds();
+ if (bounds.length == 1)
+ {
+ this.lowerBounds.add(bounds[0]);
+ }
+ else
+ {
+ this.lowerBounds.add(new ChoiceBound(bounds));
+ }
+ }
+ else
+ {
+ this.lowerBounds.add(lowerBound);
+ }
+ return true;
+ }
+
+ private boolean areBoundsInside(Type bound)
+ {
+ if (this.assignedValue != null && !isAssignable(bound, assignedValue))
+ {
+ return false;
+ }
+ for (Type lowerBound: this.lowerBounds)
+ {
+ if (!isAssignable(bound, lowerBound))
+ {
+ return false;
+ }
+ }
+ if (previous != null)
+ {
+ return previous.areBoundsInside(bound);
+ }
+ return true;
+ }
+
+ private boolean isInsideBounds(Type lowerBound, boolean checkLowerBounds)
+ {
+ if (this.assignedValue != null && !isAssignable(assignedValue, lowerBound))
+ {
+ return false;
+ }
+ if (checkLowerBounds)
+ {
+ for (Type bound: this.lowerBounds)
+ {
+ if (!isAssignable(bound, lowerBound))
+ {
+ return false;
+ }
+ }
+ }
+ if (next == null)
+ {
+ Type[] bounds = variable.getBounds();
+ for (int i = 0; i < bounds.length; i++)
+ {
+ if (isAssignable(bounds[i], lowerBound))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ return next.isInsideBounds(lowerBound, true);
+ }
+
+ // both type and bound belong to the same context
+ private boolean isAssignable(Type bound, Type type)
+ {
+ if (type instanceof TypeVariable)
+ {
+ if (bound instanceof TypeVariable)
+ {
+ if (type == bound)
+ {
+ return true;
+ }
+ TypeVariable typeVariable = (TypeVariable) type;
+ Type[] bounds = typeVariable.getBounds();
+ while (bounds.length == 1 && bounds[0] instanceof TypeVariable)
+ {
+ if (bounds[0] == bound)
+ {
+ return true;
+ }
+ type = (TypeVariable) bounds[0];
+ bounds = typeVariable.getBounds();
+ }
+ TypeVariable variableBound = (TypeVariable) bound;
+ Type[] variableBoundBounds = typeVariable.getBounds();
+ while (variableBoundBounds.length == 1 && variableBoundBounds[0] instanceof TypeVariable)
+ {
+ if (variableBoundBounds[0] == type)
+ {
+ return false;
+ }
+ variableBound = (TypeVariable) variableBoundBounds[0];
+ variableBoundBounds = variableBound.getBounds();
+ }
+ outer: for (int i = 0; i < variableBoundBounds.length; i++)
+ {
+ for (int j = 0; j < bounds.length; j++)
+ {
+ if (isAssignable(variableBoundBounds[i], bounds[j]))
+ {
+ continue outer;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+ for (Type variableBound: ((TypeVariable) type).getBounds())
+ {
+ if (isAssignable(bound, variableBound))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (bound instanceof Class)
+ {
+ if (bound == Object.class)
+ {
+ return true;
+ }
+ Class<?> boundClass = (Class<?>) bound;
+ if (type instanceof Class)
+ {
+ return boundClass.isAssignableFrom((Class<?>) type);
+ }
+ if (type instanceof ParameterizedType)
+ {
+ return boundClass.isAssignableFrom((Class<?>) ((ParameterizedType) type).getRawType());
+ }
+ return boundClass.isArray() && isAssignable(boundClass.getComponentType(), ((GenericArrayType) type).
+ getGenericComponentType());
+ }
+ if (bound instanceof ParameterizedType)
+ {
+ return ContextualizedArguments.isAssignable(CHECKER, (ParameterizedType) bound, type, this);
+ }
+ if (bound instanceof TypeVariable)
+ {
+ for (Type typeBound: ((TypeVariable) bound).getBounds())
+ {
+ if (!isAssignable(typeBound, type))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ ChoiceBound choiceBound = (ChoiceBound) bound;
+ for (Iterator<Type> it = choiceBound.bounds.iterator(); it.hasNext();)
+ {
+ Type option = it.next();
+ if (!isAssignable(option, type))
+ {
+ it.remove();
+ }
+ }
+ return !choiceBound.bounds.isEmpty();
+ }
+
+ protected boolean isSame(Type assignableArgument, Type argument)
+ {
+ if (assignableArgument instanceof WildcardType)
+ {
+ WildcardType assignableWildcardArg = (WildcardType) assignableArgument;
+ Type[] upperAssignBounds = assignableWildcardArg.getUpperBounds();
+ Type[] lowerAssignBounds = assignableWildcardArg.getUpperBounds();
+ if (argument instanceof WildcardType)
+ {
+ WildcardType wildcardArg = (WildcardType) argument;
+ Type[] upperBounds = wildcardArg.getUpperBounds();
+ if (!isAssignable(upperAssignBounds, upperBounds))
+ {
+ return false;
+ }
+
+ Type[] lowerBounds = wildcardArg.getLowerBounds();
+ outer: for (int i = 0; i < lowerBounds.length; i++)
+ {
+ for (int j = 0; j < lowerAssignBounds.length; j++)
+ {
+ if (isAssignable(lowerBounds[i], lowerAssignBounds[j]))
+ {
+ continue outer;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+ else if(argument instanceof TypeVariable)
+ {
+ if (!isAssignable(upperAssignBounds, ((TypeVariable) argument).getBounds()))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < upperAssignBounds.length; i++)
+ {
+ if (!isAssignable(upperAssignBounds[i], argument))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ else if (assignableArgument instanceof GenericArrayType)
+ {
+ if (argument instanceof GenericArrayType)
+ {
+ return isSame(((GenericArrayType) assignableArgument).getGenericComponentType(),
+ ((GenericArrayType) argument).getGenericComponentType());
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return assignableArgument.equals(argument);
+ }
+
+ /**
+ * @param node
+ * @param upperBounds
+ * @param upperAssignBounds
+ */
+ private boolean isAssignable(Type[] upperAssignBounds, Type[] upperBounds)
+ {
+ outer: for (int i = 0; i < upperAssignBounds.length; i++)
+ {
+ for (int j = 0; j < upperBounds.length; j++)
+ {
+ if (isAssignable(upperAssignBounds[i], upperBounds[j]))
+ {
+ continue outer;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+ private static ContextualizedArguments.EqualityChecker<VariableNode> CHECKER =
+ new ContextualizedArguments.EqualityChecker<VariableNode>()
+ {
+ public boolean isSame(Type assignableArgument, Type argument, VariableNode node)
+ {
+ return node.isSame(assignableArgument, argument);
+ }
+ };
+}
+
+class ChoiceBound implements Type
+{
+ public ChoiceBound(Type[] bounds)
+ {
+ this.bounds = new LinkedList<Type>();
+ Collections.addAll(this.bounds, bounds);
+ }
+
+ Collection<Type> bounds;
+}
\ No newline at end of file
More information about the jboss-cvs-commits
mailing list