/*  Copyright 2007 Niclas Hedhman.
 *
 * 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 org.ops4j.orthogon.internal;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.ops4j.lang.NullArgumentException;
import org.ops4j.orthogon.AspectFactory;

public final class AspectFactoryImpl
    implements AspectFactory
{
    private final MixinFactory m_mixinFactory;
    private final InvocationStackPool m_pool;

    public AspectFactoryImpl( MixinFactory mixinFactory, InvocationStackFactory invocationStackFactory )
        throws IllegalArgumentException
    {
        NullArgumentException.validateNotNull( mixinFactory, "mixinFactory" );
        NullArgumentException.validateNotNull( invocationStackFactory, "invocationStackFactory" );

        m_mixinFactory = mixinFactory;
        m_pool = new InvocationStackPool( invocationStackFactory );
    }

    @SuppressWarnings( "unchecked" )
    public <T> T newInstance( Class<T> primaryAspect )
        throws IllegalArgumentException
    {
        NullArgumentException.validateNotNull( primaryAspect, "primaryAspect" );
        AspectRoutingHandler handler = getInvocationHandler( primaryAspect );
        ClassLoader classLoader = primaryAspect.getClassLoader();
        Class[] classes = new Class[] { primaryAspect };
        return (T) Proxy.newProxyInstance( classLoader, classes, handler );
    }

    public <T> T getInstance( String identity )
    {
        // TODO: finish implementation
        return null;
    }

    /**
     * Returns the mixing implementation instance of the specified {@code mixinInterface}. Returns {@code null} if the
     * mixin interface implementation is not registered.
     *
     * @param mixinInterface The mixin interface.
     * @param primaryAspect  The primary aspect that creates the specified {@code mixinInterface}.
     *                       This argument must not be {@code null}.
     *
     * @return Returns the mixin implementation.
     *
     * @throws IllegalArgumentException Thrown if the specified {@code primaryAspect} argument is {@code null}.
     * @since 1.0.0
     */
    public Object createMixin( Class mixinInterface, Class primaryAspect )
        throws IllegalArgumentException
    {
        NullArgumentException.validateNotNull( primaryAspect, "primaryAspect" );

        return m_mixinFactory.create( mixinInterface, primaryAspect );
    }

    private AspectRoutingHandler getInvocationHandler( Class primaryAspect )
        throws IllegalArgumentException
    {
        NullArgumentException.validateNotNull( primaryAspect, "primaryAspect" );

        return new AspectRoutingHandler( primaryAspect, this );
    }

    final InvocationStack getInvocationStack( Method invokedMethod, Object proxy )
        throws IllegalArgumentException
    {
        NullArgumentException.validateNotNull( invokedMethod, "invokedMethod" );
        NullArgumentException.validateNotNull( proxy, "proxy" );

        Class proxyClass = proxy.getClass();
        Class[] targetClasses = proxyClass.getInterfaces();
        JoinpointDescriptor adviceDescriptor = new JoinpointDescriptor( invokedMethod, targetClasses );
        return m_pool.getInvocationStack( adviceDescriptor );
    }

    /**
     * Returns {@code true} if the specified {@code mixinInterface} is still exists, {@code false} otherwise.
     *
     * @param mixinInterface The mixin interface class to check.
     *
     * @return A {@code boolean} indicator whether the specified {@code mixinInterface} is exists.
     *
     * @since 1.0.0
     */
    final boolean isMixinInterfaceExists( Class mixinInterface )
    {
        return m_mixinFactory.checkExistence( mixinInterface );
    }

    final void release( InvocationStack stack )
    {
        if( stack == null )
        {
            return;
        }

        m_pool.release( stack );
    }
}

@@ -699,16 +699,17 @@
 .Proxy;%0A
+%0A
 import o
@@ -949,16 +949,598 @@
 _pool;%0A%0A
+    private static final class ProxyCache%0A    %7B%0A        final Class m_proxyClass;%0A        final Class%5B%5D m_interfaces;%0A%0A        public ProxyCache( final Class proxyClass )%0A        %7B%0A            m_proxyClass = proxyClass;%0A            m_interfaces = proxyClass.getInterfaces();%0A        %7D%0A%0A        public boolean needsRefresh( Class proxyClass )%0A        %7B%0A            return false == m_proxyClass.equals( proxyClass );%0A        %7D%0A%0A        public Class%5B%5D getInterfaces()%0A        %7B%0A            return m_interfaces;%0A        %7D%0A    %7D%0A%0A    private final ThreadLocal%3CProxyCache%3E m_proxyCache;%0A%0A
     publ
@@ -1896,24 +1896,24 @@
 xinFactory;%0A
-
         m_po
@@ -1967,16 +1967,71 @@
 ctory );
+%0A%0A        m_proxyCache = new ThreadLocal%3CProxyCache%3E();
 %0A    %7D%0A%0A
@@ -4103,24 +4103,30 @@
 );%0A%0A        
+final 
 Class proxyC
@@ -4150,32 +4150,311 @@
 lass();%0A        
+ProxyCache localProxyCache = m_proxyCache.get();%0A%0A        if ( localProxyCache == null %7C%7C localProxyCache.needsRefresh( proxyClass ) )%0A        %7B%0A            localProxyCache = new ProxyCache( proxyClass );%0A            m_proxyCache.set( localProxyCache );%0A        %7D%0A%0A        final 
 Class%5B%5D targetCl
@@ -4457,34 +4457,39 @@
 etClasses = 
-p
+localP
 roxyC
-lass
+ache
 .getInterfac

Use thread-local cache to improve performance without too much memory overhead.