# DecoratorMixin.java

    /*
     * Copyright 2009 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.qi4j.api.composite;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import org.qi4j.api.injection.scope.Uses;

    /**
     * Generic decorator mixin that allows a Composite to wrap
     * any other Composite as long as they share an interface.
     * <p>
     * Can be used to effectively implement
     * singleton mixins, since the decorated object can be shared between
     * many instances.
     * </p>
     */
    public class DecoratorMixin
        implements InvocationHandler
    {
        private Object delegate;

        public DecoratorMixin( @Uses Object delegate )
        {
            if( delegate instanceof Class )
            {
                Thread.dumpStack();
            }
            this.delegate = delegate;
        }

        @Override
        public Object invoke( Object object, Method method, Object[] args )
            throws Throwable
        {
            if( delegate instanceof InvocationHandler )
            {
                InvocationHandler handler = (InvocationHandler) delegate;
                return handler.invoke( object, method, args );
            }
            else
            {
                try
                {
                    return method.invoke( delegate, args );
                }
                catch( InvocationTargetException e )
                {
                    throw e.getCause();
                }
                catch( IllegalArgumentException e )
                {
                    String message = constructMessage( method, args );
                    throw new IllegalArgumentException( message, e );
                }
            }
        }

        private String constructMessage( Method method, Object[] args )
        {
            StringBuilder builder = new StringBuilder();
            builder.append( "\nmethod: " );
            builder.append( method.getDeclaringClass().getName() );
            builder.append( "." );
            builder.append( method.getName() );
            builder.append( "\ndelegate: " );
            builder.append( delegate );
            builder.append( "\ndelegateType: " );
            builder.append( delegate == null ? "n/a" : delegate.getClass().getName() );
            builder.append( "\narguments: \n" );
            for( Object arg : args )
            {
                builder.append( "    " );
                Class argClass = arg.getClass();
                if( Proxy.isProxyClass( argClass ) )
                {
                    builder.append( Proxy.getInvocationHandler( arg ).getClass().getName() );
                }
                else
                {
                    builder.append( argClass.getName() );
                }
                builder.append( '\n' );
            }
            return builder.toString();
        }
    }