[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