package com.alibaba.ttl.threadpool.agent;

import com.alibaba.ttl.threadpool.agent.internal.logging.Logger;
import com.alibaba.ttl.threadpool.agent.internal.transformlet.ClassInfo;
import com.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

/**
 * TTL {@link ClassFileTransformer} of Java Agent
 *
 * @author Jerry Lee (oldratlee at gmail dot com)
 * @see ClassFileTransformer
 * @see <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>
 * @since 0.9.0
 */
public class TtlTransformer implements ClassFileTransformer {
    private static final Logger logger = Logger.getLogger(TtlTransformer.class);

    private static final byte[] EMPTY_BYTE_ARRAY = {};

    private final List<JavassistTransformlet> transformletList = new ArrayList<JavassistTransformlet>();

    TtlTransformer(List<? extends JavassistTransformlet> transformletList) {
        for (JavassistTransformlet transformlet : transformletList) {
            this.transformletList.add(transformlet);
            logger.info("[TtlTransformer] add Transformlet " + transformlet.getClass() + " success");
        }
    }

    @Override
    public final byte[] transform(@Nullable final ClassLoader loader, @Nullable final String classFile, final Class<?> classBeingRedefined,
                                  final ProtectionDomain protectionDomain, @NonNull final byte[] classFileBuffer) {
        try {
            // Lambda has no class file, no need to transform, just return.
            if (classFile == null) return EMPTY_BYTE_ARRAY;

            final String className = toClassName(classFile);

            ClassInfo classInfo = new ClassInfo(className, classFileBuffer, loader);

            for (JavassistTransformlet transformlet : transformletList) {
                transformlet.doTransform(classInfo);
                if (classInfo.isModified()) return classInfo.getCtClass().toBytecode();
            }
        } catch (Throwable t) {
            String msg = "Fail to transform class " + classFile + ", cause: " + t.toString();
            logger.log(Level.SEVERE, msg, t);
            throw new IllegalStateException(msg, t);
        }

        return EMPTY_BYTE_ARRAY;
    }

    private static String toClassName(@NonNull final String classFile) {
        return classFile.replace('/', '.');
    }
}

@@ -355,16 +355,75 @@
 ullable;
+%0Aimport edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 %0A%0Aimport
@@ -1038,57 +1038,587 @@
     
-private static final byte%5B%5D EMPTY_BYTE_ARRAY = %7B%7D
+/**%0A     * %22%3Ccode%3Enull%3C/code%3E if no transform is performed%22,%0A     * see %7B@code @return%7D of %7B@link ClassFileTransformer#transform(ClassLoader, String, Class, ProtectionDomain, byte%5B%5D)%7D%0A     */%0A    @SuppressFBWarnings(%7B%22EI_EXPOSE_REP%22%7D)%0A    // %5BERROR%5D com.alibaba.ttl.threadpool.agent.TtlTransformer.transform(ClassLoader, String, Class, ProtectionDomain, byte%5B%5D)%0A    //         may expose internal representation by returning TtlTransformer.NO_TRANSFORM%0A    // the value is null, so there is NO %22EI_EXPOSE_REP%22 problem actually.%0A    private static final byte%5B%5D NO_TRANSFORM = null
 ;%0A%0A 
@@ -2443,32 +2443,28 @@
  return 
-EMPTY_BYTE_ARRAY
+NO_TRANSFORM
 ;%0A%0A     
@@ -3090,24 +3090,20 @@
 urn 
-EMPTY_BYTE_ARRAY
+NO_TRANSFORM
 ;%0A  

return null if no transform is performed #158