/************************************************
 * Copyright (c) Shen Li. All rights reserved.  *
 * http://joyaop.sourceforge.net                *
 * -------------------------------------------- *
 * Distributable under LGPL license.            *
 * See terms of license at gnu.org.             *
 ************************************************/
package net.sf.joyaop.impl;

import net.sf.joyaop.AbstractInterceptor;
import net.sf.joyaop.AspectRuntimeException;
import net.sf.joyaop.Interceptor;
import net.sf.joyaop.framework.InterceptorAspect;
import net.sf.joyaop.framework.AspectizedClass;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;

/**
 * @author Shen Li
 */
public class InterceptorAspectImpl extends RuntimeAspectImpl implements InterceptorAspect, Comparable {
    private int precedence;

    public InterceptorAspectImpl() {
        setScope(JVM_SCOPE);
    }

    public void weave(AspectizedClass aspectizedClass) {
        if (!getPointcut().matches(aspectizedClass.getOriginalClass())) {
            return;
        }
        for (Iterator i = aspectizedClass.getMethods().iterator(); i.hasNext();) {
            Method m = (Method) i.next();
            if (getPointcut().matches(aspectizedClass.getOriginalClass(), m) && checkMethod(m)) {
                aspectizedClass.addInterceptor(m, this);
            }
        }
    }

    public int getPrecedence() {
        return precedence;
    }

    public void setPrecedence(int precedence) {
        this.precedence = precedence;
    }

    public int compareTo(Object object) {
        // because TreeSet only support ascending order
        return (((InterceptorAspect) object).getPrecedence() - precedence);
    }

    private boolean checkMethod(Method method) {
        int mod = method.getModifiers();
        if (Modifier.isStatic(mod) || Modifier.isNative(mod) || Modifier.isFinal(mod)) {
            return false;
        }
        if (Interceptor.class.isAssignableFrom(getAspectClass())) {
            return true;
        }
        if (AbstractInterceptor.class.isAssignableFrom(getAspectClass())) {
            return true;
        }
        try {
            Method callback = getAspectClass().getMethod(method.getName(), method.getParameterTypes());
            return !Modifier.isAbstract(callback.getModifiers());
        } catch (NoSuchMethodException e) {
            throw new AspectRuntimeException(e);
        }
    }
}