# FragmentInvocationHandler.java

    /*
     * Copyright 2007 Rickard Öberg
     * 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.runtime.composite;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;

    /**
     * JAVADOC
     */
    abstract class FragmentInvocationHandler
        implements InvocationHandler
    {
        private static final String COMPACT_TRACE = "qi4j.compacttrace";

        private static final CompactLevel compactLevel;

        static
        {
            compactLevel = CompactLevel.valueOf( System.getProperty( COMPACT_TRACE, "proxy" ) );
        }

        protected Object fragment;
        protected Method method;

        void setFragment( Object fragment )
        {
            this.fragment = fragment;
        }

        public void setMethod( Method method )
        {
            this.method = method;
        }

        protected Throwable cleanStackTrace( Throwable throwable, Object proxy, Method method )
        {
            if( compactLevel == CompactLevel.off )
            {
                return throwable;
            }

            StackTraceElement[] trace = throwable.getStackTrace();

            // Check if exception originated within Zest or JDK - if so then skip compaction
            if( trace.length == 0 || !isApplicationClass( trace[ 0 ].getClassName() ) )
            {
                return throwable;
            }

            int count = 0;
            for( int i = 0; i < trace.length; i++ )
            {
                StackTraceElement stackTraceElement = trace[ i ];
                if( !isApplicationClass( stackTraceElement.getClassName() ) )
                {
                    // TODO: Should find stack entry outside Runtime, and compact beyond that
                    trace[ i ] = null;
                    count++;
                }
                else
                {
                    boolean classOrigin = stackTraceElement.getClassName().equals( proxy.getClass().getSimpleName() );
                    boolean methodOrigin = stackTraceElement.getMethodName().equals( method.getName() );
                    if( classOrigin && methodOrigin && compactLevel == CompactLevel.proxy )
                    {
                        // Stop removing if the originating method call has been located in the stack.
                        // For 'semi' and 'extensive' compaction, we don't and do the entire stack instead.
                        trace[ i ] = new StackTraceElement( proxy.getClass()
                                                                .getInterfaces()[ 0 ].getName(), method.getName(), null, -1 );
                        break; // Stop compacting this trace
                    }
                }
            }

            // Create new trace array
            int idx = 0;
            StackTraceElement[] newTrace = new StackTraceElement[ trace.length - count ];
            for( StackTraceElement stackTraceElement : trace )
            {
                if( stackTraceElement != null )
                {
                    newTrace[ idx++ ] = stackTraceElement;
                }
            }
            throwable.setStackTrace( newTrace );

            Throwable nested = throwable.getCause();
            if( nested != null )
            {
                //noinspection ThrowableResultOfMethodCallIgnored
                cleanStackTrace( nested, proxy, method );
            }
            return throwable;
        }

        private boolean isApplicationClass( String className )
        {
            if( compactLevel == CompactLevel.semi )
            {
                return !isJdkInternals( className );
            }
            return !( className.endsWith( FragmentClassLoader.GENERATED_POSTFIX ) ||
                      className.startsWith( "org.qi4j.runtime" ) ||
                      isJdkInternals( className ) );
        }

        private boolean isJdkInternals( String className )
        {
            return className.startsWith( "java.lang.reflect" )
                   || className.startsWith( "com.sun.proxy" )
                   || className.startsWith( "sun.reflect" );
        }
    }