/**************************************************************************************
 * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved.                 *
 * http://aspectwerkz.codehaus.org                                                    *
 * ---------------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the LGPL license      *
 * a copy of which has been included with this distribution in the license.txt file.  *
 **************************************************************************************/
package org.codehaus.aspectwerkz.hook.impl;

import java.security.ProtectionDomain;
import java.util.Iterator;

import org.codehaus.aspectwerkz.hook.ClassPreProcessor;

/**
 * Helper class called by the modified java.lang.ClassLoader.
 * <p/>
 * This class is called at different points by the modified java.lang.ClassLoader of the
 * org.codehaus.aspectwerkz.hook.impl.ClassLoaderPreProcessorImpl implemention.<br/> This class must reside in the
 * -Xbootclasspath when AspectWerkz layer 1 is used, but the effective implementation of the class preprocessor
 * (AspectWerkz layer 2) can be in standard system classpath (-cp).
 *
 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
 */
public class ClassPreProcessorHelper {

    /**
     * ClassPreProcessor used if aspectwerkz.classloader.preprocessor property is defined to full qualified class name
     */
    private static ClassPreProcessor preProcessor;

    /**
     * true if preProcesor already initalized
     */
    private static boolean preProcessorInitialized;

    /**
     * option used to defined the class preprocessor
     */
    private static String PRE_PROCESSOR_CLASSNAME_PROPERTY = "aspectwerkz.classloader.preprocessor";

    /**
     * default class preprocessor
     */
    private static String PRE_PROCESSOR_CLASSNAME_DEFAULT = "org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor";

    static {
        initializePreProcessor();
    }

    /**
     * Returns the configured class preprocessor
     * Should be called after initialization only
     *
     * @return the preprocessor or null if not initialized
     */
    public static ClassPreProcessor getClassPreProcessor() {
        return preProcessor;
    }

    /**
     * Initialization of the ClassPreProcessor The ClassPreProcessor implementation is lazy loaded. This allow to put it
     * in the regular classpath whereas the instrumentation layer (layer 1) is in the bootclasspath
     */
    public static synchronized void initializePreProcessor() {
        //@todo review log statement according to log layer
        if (preProcessorInitialized) {
            return;
        }
        preProcessorInitialized = true;
        Class klass = null;
        String s = System.getProperty(PRE_PROCESSOR_CLASSNAME_PROPERTY, PRE_PROCESSOR_CLASSNAME_DEFAULT);

        try {
            // force loading thru System class loader to allow
            // preprocessor implementation to be in standard classpath
            klass = Class.forName(s, true, ClassLoader.getSystemClassLoader());
            // special hidden option to allow testing of HotSwap weaving when WeavingCL is used
            // in such a case the CLPP must be in the WeavingCL hierarchy, which is parrallel
            // to the regulare SystemCL
            if (System.getProperty("aspectwerkz.transform.forceWCL")!=null) {
                klass = Class.forName(s, true, Thread.currentThread().getContextClassLoader());
            }
        }
        catch (ClassNotFoundException _ex) {
            System.err.println("AspectWerkz - WARN - Pre-processor class '" + s + "' not found");
        }
        if (klass != null) {
            try {
                preProcessor = (ClassPreProcessor)klass.newInstance();
                preProcessor.initialize(null);
                System.out.println("AspectWerkz - INFO - Pre-processor " + s + " loaded and initialized");
            }
            catch (Throwable throwable) {
                System.err.println("AspectWerkz - WARN - Error initializing pre-processor class " + s + ':');
                throwable.printStackTrace();
            }
        }
    }

    /**
     * byte code instrumentation of class loaded
     */
    public static byte[] defineClass0Pre(
            ClassLoader caller, String name, byte[] b, int off, int len, ProtectionDomain pd) {
        if (!preProcessorInitialized) {
            initializePreProcessor();
        }

        if (preProcessor == null) {
            // we need to check this due to reentrancy when ClassPreProcessorHelper is beeing initialized
            // since it tries to load a ClassPreProcessor implementation
            byte[] obyte = new byte[len];
            System.arraycopy(b, off, obyte, 0, len);
            return obyte;
        }
        else {
            try {
                byte[] ibyte = new byte[len];
                byte[] obyte = new byte[]{};
                System.arraycopy(b, off, ibyte, 0, len);
                obyte = preProcessor.preProcess(name, ibyte, caller);
                return obyte;
            }
            catch (Throwable throwable) {
                System.err.println(
                        "AspectWerkz - WARN - Error pre-processing class " + name + " in " + Thread.currentThread()
                );
                throwable.printStackTrace();
                byte[] obyte = new byte[len];
                System.arraycopy(b, off, obyte, 0, len);
                //abyte = preProcessor.preProcess(name, abyte, caller);
                return obyte;
            }
        }
    }

}
