/**
 * Copyright © 2013 Antonin Stefanutti (antonin.stefanutti@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.astefanutti.metrics.cdi;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.annotation.Metric;

import javax.enterprise.inject.Vetoed;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedMember;
import javax.enterprise.inject.spi.AnnotatedParameter;
import javax.enterprise.inject.spi.InjectionPoint;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static io.astefanutti.metrics.cdi.MetricsParameter.useAbsoluteName;

@Vetoed
/* package-private */ class SeMetricName implements MetricName {

    private final MetricsExtension extension;

    SeMetricName(MetricsExtension extension) {
        this.extension = extension;
    }

    @Override
    public String of(InjectionPoint ip) {
        Annotated annotated = ip.getAnnotated();
        if (annotated instanceof AnnotatedMember)
            return of((AnnotatedMember<?>) annotated);
        else if (annotated instanceof AnnotatedParameter)
            return of((AnnotatedParameter<?>) annotated);
        else
            throw new IllegalArgumentException("Unable to retrieve metric name for injection point [" + ip + "], only members and parameters are supported");
    }

    @Override
    public String of(AnnotatedMember<?> member) {
        if (member.isAnnotationPresent(Metric.class)) {
            Metric metric = member.getAnnotation(Metric.class);
            String name = (metric.name().isEmpty()) ? member.getJavaMember().getName() : of(metric.name());
            return metric.absolute() | extension.getParameter(useAbsoluteName, Boolean.class).orElse(false) ? name : MetricRegistry.name(member.getJavaMember().getDeclaringClass(), name);
        } else {
            return extension.getParameter(useAbsoluteName, Boolean.class).orElse(false) ? member.getJavaMember().getName() : MetricRegistry.name(member.getJavaMember().getDeclaringClass(), member.getJavaMember().getName());
        }
    }

    @Override
    public String of(String attribute) {
        return attribute;
    }

    private String of(AnnotatedParameter<?> parameter) {
        if (parameter.isAnnotationPresent(Metric.class)) {
            Metric metric = parameter.getAnnotation(Metric.class);
            String name = (metric.name().isEmpty()) ? getParameterName(parameter) : of(metric.name());
            return metric.absolute() | extension.getParameter(useAbsoluteName, Boolean.class).orElse(false) ? name : MetricRegistry.name(parameter.getDeclaringCallable().getJavaMember().getDeclaringClass(), name);
        } else {
            return extension.getParameter(useAbsoluteName, Boolean.class).orElse(false) ? getParameterName(parameter) : MetricRegistry.name(parameter.getDeclaringCallable().getJavaMember().getDeclaringClass(), getParameterName(parameter));
        }
    }

    // Let's rely on reflection to retrieve the parameter name until Java 8 is required.
    // To be refactored eventually when CDI SPI integrate JEP-118.
    // See http://openjdk.java.net/jeps/118
    // And http://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
    // TODO: move into a separate metric name strategy
    private String getParameterName(AnnotatedParameter<?> parameter) {
        try {
            Method method = Method.class.getMethod("getParameters");
            Object[] parameters = (Object[]) method.invoke(parameter.getDeclaringCallable().getJavaMember());
            Object param = parameters[parameter.getPosition()];
            Class<?> Parameter = Class.forName("java.lang.reflect.Parameter");
            if ((Boolean) Parameter.getMethod("isNamePresent").invoke(param))
                return (String) Parameter.getMethod("getName").invoke(param);
            else
                throw new UnsupportedOperationException("Unable to retrieve name for parameter [" + parameter + "], activate the -parameters compiler argument or annotate the injected parameter with the @Metric annotation");
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException cause) {
            throw new UnsupportedOperationException("Unable to retrieve name for parameter [" + parameter + "], @Metric annotation on injected parameter is required before Java 8");
        }
    }
}

@@ -1030,33 +1030,14 @@
 ect.
-InvocationTargetException
+Method
 ;%0Aim
@@ -1059,22 +1059,25 @@
 reflect.
-Method
+Parameter
 ;%0A%0Aimpor
@@ -3466,97 +3466,8 @@
  %7D%0A%0A
-    // Let's rely on reflection to retrieve the parameter name until Java 8 is required.%0A
     
@@ -3519,16 +3519,17 @@
 ntegrate
+s
  JEP-118
@@ -3541,409 +3541,170 @@
  // 
-See http://openjdk.java.net/jeps/118%0A    // And http://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html%0A    // TODO: move into a separate metric name strategy%0A    private String getParameterName(AnnotatedParameter%3C?%3E parameter) %7B%0A        try %7B%0A            Method method = Method.class.getMethod(%22getParameters%22);%0A            Object%5B%5D parameters = (Object%5B%5D) method.invoke(
+TODO: move into a separate metric name strategy%0A    private String getParameterName(AnnotatedParameter%3C?%3E parameter) %7B%0A        Parameter%5B%5D parameters = ((Method) 
 para
@@ -3752,28 +3752,43 @@
 r())
-;%0A            Object
+.getParameters();%0A        Parameter
  par
@@ -3841,160 +3841,37 @@
     
-    Class%3C?%3E Parameter = Class.forName(%22java.lang.reflect.Parameter%22);%0A            if ((Boolean) Parameter.getMethod(%22isNamePresent%22).invoke(param))%0A   
+if (param.isNamePresent()) %7B%0A
     
@@ -3882,75 +3882,42 @@
     
- 
 return 
-(String) Parameter.getMethod(%22getName%22).invoke(param
+param.getName(
 );%0A    
+    %7D%0A
     
@@ -3916,37 +3916,35 @@
   %7D%0A        else
-%0A    
+ %7B%0A
             thro
@@ -4152,316 +4152,8 @@
 %22);%0A
-        %7D catch (NoSuchMethodException %7C IllegalAccessException %7C InvocationTargetException %7C ClassNotFoundException cause) %7B%0A            throw new UnsupportedOperationException(%22Unable to retrieve name for parameter %5B%22 + parameter + %22%5D, @Metric annotation on injected parameter is required before Java 8%22);%0A
     

Use Method.getParameters Java 8 API without reflection