/*
 * IronJacamar, a Java EE Connector Architecture implementation
 * Copyright 2008, Red Hat Inc, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file 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.jca.common.annotations.repository.jandex;

import org.jboss.jca.common.spi.annotations.repository.Annotation;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.jboss.logging.Logger;

/**
 * An AnnotationImpl.
 *
 * @author <a href="mailto:stefano.maestri@redhat.com">Stefano Maestri</a>
 * @author <a href="mailto:jesper.pedersen@ironjacamar.org">Jesper Pedersen</a>
 */
public class AnnotationImpl implements Annotation
{
   private static Logger log = Logger.getLogger(AnnotationImpl.class);
   private String className;
   private ClassLoader cl;
   private List<String> parameterTypes;
   private String memberName;
   private boolean onMethod;
   private boolean onField;
   private Class<? extends java.lang.annotation.Annotation> annotationClass;
   
   /**
    * Create a new AnnotationImpl.
    *
    * @param className className
    * @param cl classloader
    * @param parameterTypes parameterTypes
    * @param memberName memberName
    * @param onMethod onMethod
    * @param onField onField
    * @param annotationClass annotationClass
    */
   @SuppressWarnings("unchecked")
   public AnnotationImpl(String className, ClassLoader cl, List<String> parameterTypes, 
                         String memberName, boolean onMethod, boolean onField, Class<?> annotationClass)
   {
      this.className = className;
      this.cl = cl;
      if (parameterTypes != null)
      {
         this.parameterTypes = new ArrayList<String>(parameterTypes.size());
         this.parameterTypes.addAll(parameterTypes);
      }
      else
      {
         this.parameterTypes = new ArrayList<String>(0);
      }

      this.memberName = memberName;
      this.onMethod = onMethod;
      this.onField = onField;
      if (annotationClass.isAnnotation())
      {
         this.annotationClass = (Class<? extends java.lang.annotation.Annotation>)annotationClass;
      }
      else
      {
         throw new IllegalArgumentException("annotationClass should be an annotation");
      }
   }
   
   /**
    * Get the className.
    *
    * @return the className.
    */
   @Override
   public String getClassName()
   {
      return className;
   }

   /**
    * Get the annotation.
    *
    * @return the annotation.
    */
   @Override
   public Object getAnnotation()
   {
      try
      {
         if (isOnField())
         {
            Class<?> clazz = cl.loadClass(className);
            while (!clazz.equals(Object.class))
            {
               try
               {
                  Field field = SecurityActions.getDeclaredField(clazz, memberName);
                  return field.getAnnotation(annotationClass);
               }
               catch (Throwable t)
               {
                  clazz = clazz.getSuperclass();
               }
            }
         }
         else if (isOnMethod())
         {
            Class<?> clazz = cl.loadClass(className);
            Class<?>[] params = new Class<?>[parameterTypes.size()];
            int i = 0;
            for (String paramClazz : parameterTypes)
            {
               params[i] = cl.loadClass(paramClazz);
               i++;
            }
            while (!clazz.equals(Object.class))
            {
               try
               {
                  Method method = SecurityActions.getDeclaredMethod(clazz, memberName, params);
                  return method.getAnnotation(annotationClass);
               }
               catch (Throwable t)
               {
                  clazz = clazz.getSuperclass();
               }
            }
         }
         else
         { // onclass
            Class<?> clazz = cl.loadClass(className);
            return clazz.getAnnotation(annotationClass);
         }
      }
      catch (Exception e)
      {
         log.error(e.getMessage(), e);
      }

      return null;
   }

   /**
    * Get the parameterTypes.
    *
    * @return the parameterTypes.
    */
   @Override
   public List<String> getParameterTypes()
   {
      return Collections.unmodifiableList(parameterTypes);
   }

   /**
    * Get the memberName.
    *
    * @return the memberName.
    */
   @Override
   public String getMemberName()
   {
      return memberName;
   }

   /**
    * Get the onMethod.
    *
    * @return the onMethod.
    */
   @Override
   public boolean isOnMethod()
   {
      return onMethod;
   }

   /**
    * Get the onField.
    *
    * @return the onField.
    */
   @Override
   public boolean isOnField()
   {
      return onField;
   }

   /**
    * String representation
    * @return The string
    */
   @Override
   public String toString()
   {
      StringBuilder sb = new StringBuilder();

      sb.append("AnnotationImpl@").append(Integer.toHexString(System.identityHashCode(this)));
      sb.append("[className=").append(className);
      sb.append(" cl=").append(cl);
      sb.append(" parameterTypes=").append(parameterTypes);
      sb.append(" memberName=").append(memberName);
      sb.append(" onMethod=").append(onMethod);
      sb.append(" onField=").append(onField);
      sb.append(" annotationClass=").append(annotationClass);
      sb.append("]");

      return sb.toString();
   }
}
