Package org.jboss.kernel.plugins.annotations

Source Code of org.jboss.kernel.plugins.annotations.CommonAnnotationAdapter

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, 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.kernel.plugins.annotations;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.jboss.beans.info.spi.BeanInfo;
import org.jboss.beans.info.spi.PropertyInfo;
import org.jboss.beans.metadata.api.annotations.MCAnnotations;
import org.jboss.logging.Logger;
import org.jboss.metadata.spi.MetaData;
import org.jboss.metadata.spi.retrieval.RetrievalUtils;
import org.jboss.metadata.spi.signature.ConstructorSignature;
import org.jboss.metadata.spi.signature.DeclaredMethodSignature;
import org.jboss.metadata.spi.signature.FieldSignature;
import org.jboss.metadata.spi.signature.Signature;
import org.jboss.reflect.spi.AnnotatedInfo;
import org.jboss.reflect.spi.ClassInfo;
import org.jboss.reflect.spi.ConstructorInfo;
import org.jboss.reflect.spi.FieldInfo;
import org.jboss.reflect.spi.MethodInfo;
import org.jboss.reflect.spi.TypeInfo;
import org.jboss.reflect.spi.TypeInfoFactory;

/**
* Common bean annotation handler.
*
* @param <T> exact annotation plugin type
* @param <U> exact handle type
* @author <a href="mailto:ales.justin@jboss.com">Ales Justin</a>
*/
public abstract class CommonAnnotationAdapter<T extends MetaDataAnnotationPlugin<?, ?>, U>
{
   /** The log */
   protected Logger log = Logger.getLogger(getClass());

   /** The annotation plugins */
   private final Map<ElementType, Map<Class<? extends Annotation>, Set<T>>> pluginsMap = new HashMap<ElementType, Map<Class<? extends Annotation>, Set<T>>>();

   ReadWriteLock lock = new ReentrantReadWriteLock();

   /** The property annotation plugin filter */
   private static final AnnotationPluginFilter PROPERTY_FILTER = new PropertyAnnotationPluginFilter();

   /** The method annotation plugin filter */
   private static final AnnotationPluginFilter METHOD_FILTER = new MethodAnnotationPluginFilter();
  
   /** The meta annotations plugins that pick out things like qualifiers */
   private final Map<ElementType, Map<Class<? extends Annotation>, Set<T>>> metaAnnotationsPluginsMap = new HashMap<ElementType, Map<Class<? extends Annotation>, Set<T>>>();
  
   /**
    * Add the annotation plugin.
    * Breaks down the plugin usage into
    * different ElementType support collections.
    *
    * @param plugin the annotation plugin
    */
   public void addAnnotationPlugin(T plugin)
   {
      if (plugin == null)
         throw new IllegalArgumentException("Null plugin.");

      Class<? extends Annotation> annotation = plugin.getAnnotation();
      if (annotation == null)
         throw new IllegalArgumentException("Null annotation class: " + plugin);

      if (annotation.getAnnotation(Target.class) == null)
         log.warn("Annotation " + annotation + " missing @Target annotation!");
      if (annotation.getAnnotation(Retention.class) == null)
         log.warn("Annotation " + annotation + " missing @Retention annotation!");

      Set<ElementType> supported = plugin.getSupportedTypes();
      if (supported == null || supported.isEmpty())
         throw new IllegalArgumentException("Null or empty support types: " + plugin);

      lock.writeLock().lock();
      try
      {
         for (ElementType type : supported)
         {
            Map<Class<? extends Annotation>, Set<T>> pluginsForType = pluginsMap.get(type);
            if (pluginsForType == null)
            {
               pluginsForType = new HashMap<Class<? extends Annotation>, Set<T>>();
               pluginsMap.put(type, pluginsForType);
            }
            Set<T> plugins = pluginsForType.get(plugin.getAnnotation());
            if (plugins == null)
            {
               plugins = new LinkedHashSet<T>();
               pluginsForType.put(plugin.getAnnotation(), plugins);
            }
            plugins.add(plugin);
         }
      }
      finally
      {
         lock.writeLock().unlock();
      }
   }

   /**
    * Remove the plugin.
    *
    * @param plugin the annotation plugin
    */
   public void removeAnnotationPlugin(T plugin)
   {
      if (plugin == null)
         return;

      Set<ElementType> supported = plugin.getSupportedTypes();
      if (supported == null || supported.isEmpty())
         throw new IllegalArgumentException("Null or empty support types: " + plugin);

      lock.writeLock().lock();
      try
      {
         for (ElementType type : supported)
         {
            Map<Class<? extends Annotation>, Set<T>> pluginsForType = pluginsMap.get(type);
            if (pluginsForType != null)
            {
               Set<T> plugins = pluginsForType.get(plugin.getAnnotation());
               if (plugins != null)
               {
                  plugins.remove(plugin);
                  if (plugins.isEmpty())
                     pluginsForType.remove(plugin.getAnnotation());
               }
               if (pluginsForType.isEmpty())
                  pluginsMap.remove(type);
            }
         }
      }
      finally
      {
         lock.writeLock().unlock();
      }
   }
  
   @SuppressWarnings("unchecked")
   public void addMetaAnnotationPlugin(T plugin)
   {
      if (plugin == null)
         throw new IllegalArgumentException("Null plugin.");
      if (plugin instanceof MetaAnnotationPlugin == false)
         throw new IllegalArgumentException("Not a MetaAnnotationPlugin");

      Class<? extends Annotation> annotation = ((MetaAnnotationPlugin<?, ?>)plugin).getMetaAnnotation();
      if (annotation == null)
         throw new IllegalArgumentException("Null annotation class: " + plugin);

      if (annotation.getAnnotation(Target.class) == null)
         log.warn("Annotation " + annotation + " missing @Target annotation!");
      if (annotation.getAnnotation(Retention.class) == null)
         log.warn("Annotation " + annotation + " missing @Retention annotation!");

      Set<ElementType> supported = plugin.getSupportedTypes();
      if (supported == null || supported.isEmpty())
         throw new IllegalArgumentException("Null or empty support types: " + plugin);

      lock.writeLock().lock();
      try
      {
         for (ElementType type : supported)
         {
            Map<Class<? extends Annotation>, Set<T>> pluginsForType = metaAnnotationsPluginsMap.get(type);
            if (pluginsForType == null)
            {
               pluginsForType = new HashMap<Class<? extends Annotation>, Set<T>>();
               metaAnnotationsPluginsMap.put(type, pluginsForType);
            }
            Set<T> plugins = pluginsForType.get(((MetaAnnotationPlugin<?, ?>)plugin).getMetaAnnotation());
            if (plugins == null)
            {
               plugins = new LinkedHashSet<T>();
               pluginsForType.put(((MetaAnnotationPlugin<?, ?>)plugin).getMetaAnnotation(), plugins);
            }
            plugins.add(plugin);
         }
      }
      finally
      {
         lock.writeLock().unlock();
      }
   }
  
   public void removeMetaAnnotationPlugin(T plugin)
   {
      if (plugin == null)
         return;

      Set<ElementType> supported = plugin.getSupportedTypes();
      if (supported == null || supported.isEmpty())
         throw new IllegalArgumentException("Null or empty support types: " + plugin);

      lock.writeLock().lock();
      try
      {
         for (ElementType type : supported)
         {
            Map<Class<? extends Annotation>, Set<T>> pluginsForType = metaAnnotationsPluginsMap.get(type);
            if (pluginsForType != null)
            {
               Set<T> plugins = pluginsForType.get(((MetaAnnotationPlugin<?, ?>)plugin).getMetaAnnotation());
               if (plugins != null)
               {
                  plugins.remove(plugin);
                  if (plugins.isEmpty())
                     pluginsForType.remove(((MetaAnnotationPlugin<?, ?>)plugin).getMetaAnnotation());
               }
               if (pluginsForType.isEmpty())
                  metaAnnotationsPluginsMap.remove(type);
            }
         }
      }
      finally
      {
         lock.writeLock().unlock();
      }
   }

   /**
    * Apply plugin.
    *
    * @param plugin the plugin
    * @param annotation the annotation
    * @param info the bean info
    * @param retrieval the metadata
    * @param handle the handle
    * @throws Throwable for any error
    */
   protected abstract void applyPlugin(T plugin, Annotation annotation, AnnotatedInfo info, MetaData retrieval, U handle) throws Throwable;

   /**
    * Clean plugin.
    *
    * @param plugin the plugin
    * @param annotation the annotation
    * @param info the bean info
    * @param retrieval the metadata
    * @param handle the handle
    * @throws Throwable for any error
    */
   protected abstract void cleanPlugin(T plugin, Annotation annotation, AnnotatedInfo info, MetaData retrieval, U handle) throws Throwable;

   /**
    * Get the name from handle.
    *
    * @param handle the handle
    * @return handle's name
    */
   protected abstract Object getName(U handle);

   /**
    * Get plugins.
    *
    * @param type the element type to match
    * @param the annotation to find a plugin for
    * @param filter possible plugins filter
    * @param annotationClasses possible annotations
    * @return iterable matching plugins. This is a clone of the original
    */
   protected Iterable<T> getPlugins(ElementType type, Annotation annotation, AnnotationPluginFilter filter, Collection<Class<? extends Annotation>> annotationClasses)
   {
      if (type == null)
         throw new IllegalArgumentException("Null type");
      if (annotation == null)
         throw new IllegalArgumentException("Null annotation");
      if (annotationClasses != null && !annotationClasses.contains(annotation))
         return Collections.emptySet();
     
      Set<T> plugins = null;
      lock.readLock().lock();
      try
      {
         Map<Class<? extends Annotation>, Set<T>> pluginsForType = pluginsMap.get(type);
         if (pluginsForType != null)
         {
            plugins = pluginsForType.get(annotation.annotationType());
           
            //Clone the collection since somebody might change it while we read it
            if (plugins != null && !plugins.isEmpty())
               plugins = new LinkedHashSet<T>(plugins);
         }
      }
      finally
      {
         lock.readLock().unlock();
      }
     
      if (plugins == null || plugins.isEmpty())
         return Collections.emptySet();
     
      if (filter != null)
      {
         List<T> result = new ArrayList<T>();
         for (T plugin : plugins)
         {
            if (filter.accept(plugin) && (annotationClasses == null || annotationClasses.contains(plugin.getAnnotation())))
            {
               result.add(plugin);
            }
         }
         return result;
      }
      return plugins;
   }
  
   /**
    * Get plugins for metaannotations.
    *
    * @param type the element type to match
    * @return A map of the mathing plugins indexed by metaannotation. This is a clone of the original
    */
   protected Map<Class<? extends Annotation>, Set<T>> getMetaAnnotationsForType(ElementType type)
   {
      lock.readLock().lock();
      try
      {
         Map<Class<? extends Annotation>, Set<T>> metaAnnotationsForType = metaAnnotationsPluginsMap.get(type);
         if (metaAnnotationsForType == null || metaAnnotationsForType.isEmpty())
            return Collections.emptyMap();
        
         Map<Class<? extends Annotation>, Set<T>> clone = new HashMap<Class<? extends Annotation>, Set<T>>();
         for (Entry<Class<? extends Annotation>, Set<T>> entry : metaAnnotationsForType.entrySet())
         {
            clone.put(entry.getKey(), new LinkedHashSet<T>(entry.getValue()));
         }
        
         return clone;
      }
      finally
      {
         lock.readLock().unlock();
      }
   }
  
   /**
    * Handle apply or cleanup of annotations.
    *
    * @param info the bean info
    * @param retrieval the metadata
    * @param handle the handle to use in a plugin
    * @param isApplyPhase is this apply phase
    * @throws Throwable for any error
    */
   protected void handleAnnotations(BeanInfo info, MetaData retrieval, U handle, boolean isApplyPhase) throws Throwable
   {
      if (info == null)
         throw new IllegalArgumentException("Null bean info.");
      if (retrieval == null)
         throw new IllegalArgumentException("Null metadata.");
      if (handle == null)
         throw new IllegalArgumentException("Null handle.");

      //Use cached metadata context
      retrieval = RetrievalUtils.createCachedMetaData(retrieval);
     
      boolean trace = log.isTraceEnabled();
      if (trace)
         log.trace(getName(handle) + " apply annotations");

      // limit the annotations
      MCAnnotations annotations = retrieval.getAnnotation(MCAnnotations.class);
      if (annotations != null && (annotations.ignore() || annotations.value().length == 0))
      {
         if (trace)
            log.trace("Ignoring annotations lookup: " + annotations);

         return;
      }

      Collection<Class<? extends Annotation>> annotationClasses = (annotations != null ? Arrays.asList(annotations.value()) : null);

      // class
      ClassInfo classInfo = info.getClassInfo();
      Annotation[] anns = retrieval.getAnnotations();
      for (int i = 0 ; i < anns.length ; i++)
      {
         for(T plugin : getPlugins(ElementType.TYPE, anns[i], null, annotationClasses))
         {
            if (isApplyPhase)
               applyPlugin(plugin, anns[i], classInfo, retrieval, handle);
            else
               cleanPlugin(plugin, anns[i], classInfo, retrieval, handle);
         }
      }
      //Meta annotations on class
      Map<Class<? extends Annotation>, Set<T>> metaAnnotationsForType = getMetaAnnotationsForType(ElementType.TYPE);
      if (metaAnnotationsForType != null)
      {
         for (Entry<Class<? extends Annotation>, Set<T>> entry : metaAnnotationsForType.entrySet())
         {
            for (Annotation annotation : retrieval.getAnnotationsAnnotatedWith(entry.getKey()))
            {
               for (T plugin : entry.getValue())
               {
                  if (isApplyPhase)
                     applyPlugin(plugin, annotation, classInfo, retrieval, handle);
                  else
                     cleanPlugin(plugin, annotation, classInfo, retrieval, handle);
               }
            }
         }
      }
     
      // constructors
      Set<ConstructorInfo> constructors = info.getConstructors();
      if (constructors != null && constructors.isEmpty() == false)
      {
         for(ConstructorInfo ci : constructors)
         {
            Signature cis = new ConstructorSignature(ci);
            MetaData cmdr = retrieval.getComponentMetaData(cis);
            if (cmdr != null)
            {
               Annotation[] canns = cmdr.getAnnotations();
               for (int i = 0 ; i < canns.length ; i++)
               {
                  for(T plugin : getPlugins(ElementType.CONSTRUCTOR, canns[i], null, annotationClasses))
                  {
                     if (isApplyPhase)
                        applyPlugin(plugin, canns[i], ci, cmdr, handle);
                     else
                        cleanPlugin(plugin, canns[i], ci, cmdr, handle);
                  }
               }
            }
            else if (trace)
               log.trace("No annotations for " + ci);
         }
      }
      else if (trace)
         log.trace("No constructors");
     

      // properties
      Set<MethodInfo> visitedMethods = new HashSet<MethodInfo>();
      Set<PropertyInfo> properties = info.getProperties();
      if (properties != null && properties.isEmpty() == false)
      {
         for(PropertyInfo pi : properties)
         {
            FieldInfo field = pi.getFieldInfo();
            if (field != null)
            {
               Signature sis = new FieldSignature(field);
               MetaData cmdr = retrieval.getComponentMetaData(sis);
               if (cmdr != null)
               {
                  Annotation[] canns = cmdr.getAnnotations();
                  for (int i = 0 ; i < canns.length ; i++)
                  {
                     for(T plugin : getPlugins(ElementType.FIELD, canns[i], null, annotationClasses))
                     {
                        if (isApplyPhase)
                           applyPlugin(plugin, canns[i], field, cmdr, handle);
                        else
                           cleanPlugin(plugin, canns[i], field, cmdr, handle);
                     }
                  }
               }
               else if (trace)
                  log.trace("No annotations for field " + field.getName());
            }
            // apply setter and getter as well - if they exist
            handleMethod(retrieval, handle, isApplyPhase, trace, visitedMethods, pi, pi.getSetter(), "setter", annotationClasses);
            handleMethod(retrieval, handle, isApplyPhase, trace, visitedMethods, pi, pi.getGetter(), "getter", annotationClasses);
         }
      }
      else if (trace)
         log.trace("No properties");

      // get Object's class info - it's cached so it shouldn't take much
      TypeInfoFactory tif = classInfo.getTypeInfoFactory();
      TypeInfo objectTI = tif.getTypeInfo(Object.class);

      // methods
      Set<MethodInfo> methods = info.getMethods();
      if (methods != null && methods.isEmpty() == false)
      {
         for(MethodInfo mi : methods)
         {
            ClassInfo declaringCI = mi.getDeclaringClass();
            // direct == check is OK
            if (declaringCI != objectTI && visitedMethods.contains(mi) == false)
            {
               Signature mis = new DeclaredMethodSignature(mi);
               MetaData cmdr = retrieval.getComponentMetaData(mis);
               if (cmdr != null)
               {
                  Annotation[] canns = cmdr.getAnnotations();
                  for (int i = 0 ; i < canns.length ; i++)
                  {
                     for(T plugin : getPlugins(ElementType.METHOD, canns[i], METHOD_FILTER, annotationClasses))
                     {
                        if (isApplyPhase)
                           applyPlugin(plugin, canns[i], mi, cmdr, handle);
                        else
                           cleanPlugin(plugin, canns[i], mi, cmdr, handle);
                     }
                  }
               }
               else if (trace)
                  log.trace("No annotations for " + mi);
            }
         }
      }
      else if (trace)
         log.trace("No methods");

      // static methods
      MethodInfo[] staticMethods = getStaticMethods(classInfo);
      if (staticMethods != null && staticMethods.length != 0)
      {
         for(MethodInfo smi : staticMethods)
         {
            if (smi.isStatic() && smi.isPublic())
            {
               Signature mis = new DeclaredMethodSignature(smi);
               MetaData cmdr = retrieval.getComponentMetaData(mis);
               if (cmdr != null)
               {
                  Annotation[] canns = cmdr.getAnnotations();
                  for (int i = 0 ; i < canns.length ; i++)
                  {
                     for(T plugin : getPlugins(ElementType.METHOD, canns[i], METHOD_FILTER, annotationClasses))
                     {                 
                        if (isApplyPhase)
                           applyPlugin(plugin, canns[i], smi, cmdr, handle);
                        else
                           cleanPlugin(plugin, canns[i], smi, cmdr, handle);
                     }
                  }
               }
               else if (trace)
                  log.trace("No annotations for " + smi);
            }
         }
      }
      else if (trace)
         log.trace("No static methods");

      // fields - if accessible - are already handled with propertys
   }

   /**
    * Handle setter or getter on property.
    *
    * @param retrieval the metadata
    * @param handle the handle
    * @param isApplyPhase is apply phase
    * @param trace is trace enabled
    * @param visitedMethods visited methods
    * @param pi the property info
    * @param method the method info
    * @param type method type
    * @param annotationClasses the possible annotation classes
    * @throws Throwable for any error
    */
   protected void handleMethod(
         MetaData retrieval,
         U handle,
         boolean isApplyPhase,
         boolean trace,
         Set<MethodInfo> visitedMethods,
         PropertyInfo pi,
         MethodInfo method,
         String type,
         Collection<Class<? extends Annotation>> annotationClasses)
         throws Throwable
   {
      if (method == null)
         return;
     
      visitedMethods.add(method);
      Signature sis = new DeclaredMethodSignature(method);
      MetaData cmdr = retrieval.getComponentMetaData(sis);
      if (cmdr != null)
      {
         Annotation[] canns = cmdr.getAnnotations();
         for (int i = 0 ; i < canns.length ; i++)
         {
            for(T plugin : getPlugins(ElementType.METHOD, canns[i], PROPERTY_FILTER, annotationClasses))
            {
               if (isApplyPhase)
                  applyPlugin(plugin, canns[i], pi, cmdr, handle);
               else
                  cleanPlugin(plugin, canns[i], pi, cmdr, handle);
            }
         }
      }
      else if (trace)
         log.trace("No annotations for " + type + ": " + pi.getName());
   }

   /**
    * Get the static methods of class info.
    *
    * @param classInfo the class info
    * @return the static methods
    */
   protected MethodInfo[] getStaticMethods(ClassInfo classInfo)
   {
      return classInfo.getDeclaredMethods();
   }  
}
TOP

Related Classes of org.jboss.kernel.plugins.annotations.CommonAnnotationAdapter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.