// Path: src/main/java/com/bnorm/auto/weave/AfterJoinPoint.java
// public interface AfterJoinPoint extends JoinPoint {
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/AfterReturningJoinPoint.java
// public interface AfterReturningJoinPoint extends AfterJoinPoint {
// 
//     Object result();
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/AfterThrowingJoinPoint.java
// public interface AfterThrowingJoinPoint extends AfterJoinPoint {
// 
//     Throwable error();
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/AroundJoinPoint.java
// public interface AroundJoinPoint extends JoinPoint {
// 
//     Object proceed();
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/BeforeJoinPoint.java
// public interface BeforeJoinPoint extends JoinPoint {
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/JoinPoint.java
// public interface JoinPoint {
// 
//     Object target();
// 
//     List<?> args();
// 
//     String method();
// 
//     Class<?> targetType();
// 
//     Class<?> returnType();
// 
//     List<Class<?>> argTypes();
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/Advice.java
// public interface Advice {
// 
//     Object call(Chain chain);
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/AfterAdvice.java
// public abstract class AfterAdvice implements Advice {
// 
//     @Override
//     public final Object call(Chain chain) {
//         try {
//             return chain.proceed();
//         } finally {
//             after(chain);
//         }
//     }
// 
//     protected abstract void after(AfterJoinPoint afterJoinPoint);
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/AfterReturningAdvice.java
// public abstract class AfterReturningAdvice implements Advice {
// 
//     @Override
//     public final Object call(Chain chain) {
//         Object result = chain.proceed();
//         afterReturning(chain);
//         return result;
//     }
// 
//     protected abstract void afterReturning(AfterReturningJoinPoint afterReturningJoinPoint);
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/AfterThrowingAdvice.java
// public abstract class AfterThrowingAdvice implements Advice {
// 
//     @Override
//     public final Object call(Chain chain) {
//         try {
//             return chain.proceed();
//         } catch (Throwable t) {
//             afterThrowing(chain);
//             throw t;
//         }
//     }
// 
//     protected abstract void afterThrowing(AfterThrowingJoinPoint afterThrowingJoinPoint);
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/AroundAdvice.java
// public abstract class AroundAdvice implements Advice {
// 
//     @Override
//     public final Object call(Chain chain) {
//         return around(chain);
//     }
// 
//     protected abstract Object around(AroundJoinPoint aroundJoinPoint);
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/BeforeAdvice.java
// public abstract class BeforeAdvice implements Advice {
// 
//     @Override
//     public final Object call(Chain chain) {
//         before(chain);
//         return chain.proceed();
//     }
// 
//     protected abstract void before(BeforeJoinPoint beforeJoinPoint);
// }


import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.lang.model.element.Modifier;
import com.bnorm.auto.weave.AfterJoinPoint;
import com.bnorm.auto.weave.AfterReturningJoinPoint;
import com.bnorm.auto.weave.AfterThrowingJoinPoint;
import com.bnorm.auto.weave.AroundJoinPoint;
import com.bnorm.auto.weave.BeforeJoinPoint;
import com.bnorm.auto.weave.JoinPoint;
import com.bnorm.auto.weave.internal.advice.Advice;
import com.bnorm.auto.weave.internal.advice.AfterAdvice;
import com.bnorm.auto.weave.internal.advice.AfterReturningAdvice;
import com.bnorm.auto.weave.internal.advice.AfterThrowingAdvice;
import com.bnorm.auto.weave.internal.advice.AroundAdvice;
import com.bnorm.auto.weave.internal.advice.BeforeAdvice;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
    ;

    public static final Map<String, CrosscutEnum> crosscutMap;

    static {
        HashMap<String, CrosscutEnum> temp = new HashMap<>();
        for (CrosscutEnum crosscut : CrosscutEnum.values()) {
            temp.put(crosscut.joinPoint.getName(), crosscut);
        }
        crosscutMap = Collections.unmodifiableMap(temp);
    }

    protected final Class<? extends JoinPoint> joinPoint;
    protected final Class<? extends Advice> chain;
    protected final Class<?> returnType;
    protected final String lowerCaseName;

    CrosscutEnum(Class<? extends JoinPoint> joinPoint, Class<? extends Advice> chain, Class<?> returnType) {
        this.joinPoint = joinPoint;
        this.chain = chain;
        this.returnType = returnType;
        this.lowerCaseName = Names.classToVariable(name());
    }

    public TypeSpec getAdvice(CodeBlock codeBlock) {
        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(lowerCaseName);
        methodBuilder.addAnnotation(Override.class);
        methodBuilder.addModifiers(Modifier.PUBLIC);
        methodBuilder.returns(returnType);
        methodBuilder.addParameter(joinPoint, "joinPoint");
// Path: src/main/java/com/bnorm/auto/weave/AfterJoinPoint.java
// public interface AfterJoinPoint extends JoinPoint {
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/AfterReturningJoinPoint.java
// public interface AfterReturningJoinPoint extends AfterJoinPoint {
// 
//     Object result();
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/AfterThrowingJoinPoint.java
// public interface AfterThrowingJoinPoint extends AfterJoinPoint {
// 
//     Throwable error();
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/AroundJoinPoint.java
// public interface AroundJoinPoint extends JoinPoint {
// 
//     Object proceed();
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/BeforeJoinPoint.java
// public interface BeforeJoinPoint extends JoinPoint {
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/JoinPoint.java
// public interface JoinPoint {
// 
//     Object target();
// 
//     List<?> args();
// 
//     String method();
// 
//     Class<?> targetType();
// 
//     Class<?> returnType();
// 
//     List<Class<?>> argTypes();
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/Advice.java
// public interface Advice {
// 
//     Object call(Chain chain);
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/AfterAdvice.java
// public abstract class AfterAdvice implements Advice {
// 
//     @Override
//     public final Object call(Chain chain) {
//         try {
//             return chain.proceed();
//         } finally {
//             after(chain);
//         }
//     }
// 
//     protected abstract void after(AfterJoinPoint afterJoinPoint);
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/AfterReturningAdvice.java
// public abstract class AfterReturningAdvice implements Advice {
// 
//     @Override
//     public final Object call(Chain chain) {
//         Object result = chain.proceed();
//         afterReturning(chain);
//         return result;
//     }
// 
//     protected abstract void afterReturning(AfterReturningJoinPoint afterReturningJoinPoint);
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/AfterThrowingAdvice.java
// public abstract class AfterThrowingAdvice implements Advice {
// 
//     @Override
//     public final Object call(Chain chain) {
//         try {
//             return chain.proceed();
//         } catch (Throwable t) {
//             afterThrowing(chain);
//             throw t;
//         }
//     }
// 
//     protected abstract void afterThrowing(AfterThrowingJoinPoint afterThrowingJoinPoint);
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/AroundAdvice.java
// public abstract class AroundAdvice implements Advice {
// 
//     @Override
//     public final Object call(Chain chain) {
//         return around(chain);
//     }
// 
//     protected abstract Object around(AroundJoinPoint aroundJoinPoint);
// }
// 
// Path: src/main/java/com/bnorm/auto/weave/internal/advice/BeforeAdvice.java
// public abstract class BeforeAdvice implements Advice {
// 
//     @Override
//     public final Object call(Chain chain) {
//         before(chain);
//         return chain.proceed();
//     }
// 
//     protected abstract void before(BeforeJoinPoint beforeJoinPoint);
// }


// Path: src/main/java/com/bnorm/auto/weave/internal/CrosscutEnum.java
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.lang.model.element.Modifier;
import com.bnorm.auto.weave.AfterJoinPoint;
import com.bnorm.auto.weave.AfterReturningJoinPoint;
import com.bnorm.auto.weave.AfterThrowingJoinPoint;
import com.bnorm.auto.weave.AroundJoinPoint;
import com.bnorm.auto.weave.BeforeJoinPoint;
import com.bnorm.auto.weave.JoinPoint;
import com.bnorm.auto.weave.internal.advice.Advice;
import com.bnorm.auto.weave.internal.advice.AfterAdvice;
import com.bnorm.auto.weave.internal.advice.AfterReturningAdvice;
import com.bnorm.auto.weave.internal.advice.AfterThrowingAdvice;
import com.bnorm.auto.weave.internal.advice.AroundAdvice;
import com.bnorm.auto.weave.internal.advice.BeforeAdvice;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

    ;

    public static final Map<String, CrosscutEnum> crosscutMap;

    static {
        HashMap<String, CrosscutEnum> temp = new HashMap<>();
        for (CrosscutEnum crosscut : CrosscutEnum.values()) {
            temp.put(crosscut.joinPoint.getName(), crosscut);
        }
        crosscutMap = Collections.unmodifiableMap(temp);
    }

    protected final Class<? extends JoinPoint> joinPoint;
    protected final Class<? extends Advice> chain;
    protected final Class<?> returnType;
    protected final String lowerCaseName;

    CrosscutEnum(Class<? extends JoinPoint> joinPoint, Class<? extends Advice> chain, Class<?> returnType) {
        this.joinPoint = joinPoint;
        this.chain = chain;
        this.returnType = returnType;
        this.lowerCaseName = Names.classToVariable(name());
    }

    public TypeSpec getAdvice(CodeBlock codeBlock) {
        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(lowerCaseName);
        methodBuilder.addAnnotation(Override.class);
        methodBuilder.addModifiers(Modifier.PUBLIC);
        methodBuilder.returns(returnType);
        methodBuilder.addParameter(joinPoint, "joinPoint");
        methodBuilder.addCode(codeBlock);