/************************************************
 * 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.config.Configuration;
import net.sf.joyaop.framework.*;
import net.sf.joyaop.util.ClassUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.*;

/**
 * @author Shen Li
 */
public class AspectRuntimeImpl implements AspectRuntime {
    private static final Log log = LogFactory.getLog(AspectRuntimeImpl.class);

    private final Set interceptors = new HashSet();
    private final Set mixins = new HashSet();
    private final Set interfaces = new HashSet();
    private final Map aspectizedClasses = new HashMap();

    public AspectRuntimeImpl() {
        this(null);
    }

    public AspectRuntimeImpl(Configuration configuration) {
        this(configuration, null);
    }

    public AspectRuntimeImpl(Configuration configuration, AspectFactory aspectFactory) {
        if (configuration != null) {
            configuration.configure(this);
        }
        if (aspectFactory != null) {
            setAspectFactory(aspectFactory);
        }
    }

    public Object newInstance(Class clazz) {
        return newInstance(clazz, new Class[0], new Object[0]);
    }

    public Object newInstance(String className) {
        return newInstance(ClassUtils.loadClass(className));
    }

    public Object newInstance(String className, Class[] argTypes, Object[] args) {
        return newInstance(ClassUtils.loadClass(className), argTypes, args);
    }

    public Object newInstance(Class clazz, Class[] argTypes, Object[] args) {
        AspectizedClass aspectizedClass;
        synchronized (this) {
            aspectizedClass = (AspectizedClass) aspectizedClasses.get(clazz);
            if (aspectizedClass == null) {
                aspectizedClass = new AspectizedClassImpl(clazz);
                for (Iterator i = interfaces.iterator(); i.hasNext();) {
                    ((InterfaceAspect) i).weave(aspectizedClass);
                }
                for (Iterator i = mixins.iterator(); i.hasNext();) {
                    ((MixinAspect) i.next()).weave(aspectizedClass);
                }
                for (Iterator i = interceptors.iterator(); i.hasNext();) {
                    ((InterceptorAspect) i.next()).weave(aspectizedClass);
                }
                aspectizedClasses.put(clazz, aspectizedClass);
            }
        }
        return aspectizedClass.newInstance(argTypes, args);
    }

    public void addInterceptor(InterceptorAspect interceptorAspect) {
        interceptors.add(interceptorAspect);
        if (log.isInfoEnabled()) {
            log.info("Interceptor aspect " + interceptorAspect.getAspectClass().getName() + " loaded.");
        }
    }

    public void addMixin(MixinAspect mixinAspect) {
        mixins.add(mixinAspect);
        if (log.isInfoEnabled()) {
            log.info("Mxin aspect " + mixinAspect.getAspectClass().getName() + " loaded.");
        }
    }

    public void addInterface(InterfaceAspect interfaceAspect) {
        interfaces.add(interfaceAspect);
        if (log.isInfoEnabled()) {
            log.info("Interface aspect " + interfaceAspect.getAspectClass().getName() + " loaded.");
        }
    }

    public void setAspectFactory(AspectFactory aspectFactory) {
        RuntimeAspectInstance.setAspectFactory(aspectFactory);
    }

    public AspectFactory getAspectFactory() {
        return RuntimeAspectInstance.getAspectFactory();
    }
}