/************************************************
 * 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.cglib.proxy.CallbackFilter;
import net.sf.joyaop.AspectRuntimeException;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author Shen Li
 */
class CallbackFilterImpl implements CallbackFilter, Serializable {
    private Map methodToIndex = new HashMap();
    private Map classToIndex = new HashMap();

    public void addMethod(Method method, int index) {
        methodToIndex.put(method, new Integer(index));
    }

    public void addClass(Class clazz, int index) {
        classToIndex.put(clazz, new Integer(index));
    }

    public int accept(Method method) {
        Integer i = (Integer) methodToIndex.get(method);
        if (i != null) {
            return i.intValue();
        }
        i = (Integer) classToIndex.get(method.getDeclaringClass());
        if (i != null) {
            return i.intValue();
        }
        throw new AspectRuntimeException("Can't find the target object for method " + method.getName());
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeObject(classToIndex);
        out.writeInt(methodToIndex.size());
        for (Iterator i = methodToIndex.entrySet().iterator(); i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            Method method = (Method) entry.getKey();
            out.writeObject(method.getName());
            out.writeObject(method.getDeclaringClass());
            out.writeObject(method.getParameterTypes());
            out.writeObject(entry.getValue());
        }
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        classToIndex = (Map) in.readObject();
        methodToIndex = new HashMap();
        int size = in.readInt();
        for (int i = 0; i < size; i++) {
            String name = (String) in.readObject();
            Class clazz = (Class) in.readObject();
            Class[] paramTypes = (Class[]) in.readObject();
            Method method = null;
            try {
                method = clazz.getMethod(name, paramTypes);
            } catch (NoSuchMethodException e) {
                throw new AspectRuntimeException(e);
            }
            methodToIndex.put(method, in.readObject());
        }
    }

    public boolean equals(Object object) {
        if (this == object) {
            return true;
        }
        if (!(object instanceof CallbackFilterImpl)) {
            return false;
        }
        CallbackFilterImpl callbackFilter = (CallbackFilterImpl) object;
        if (!classToIndex.equals(callbackFilter.classToIndex)) {
            return false;
        }
        if (!methodToIndex.equals(callbackFilter.methodToIndex)) {
            return false;
        }
        return true;
    }
}