/*
 *  Copyright the original author or authors.
 *
 *  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.apache.commons.weaver.privilizer;

import java.io.File;
import java.lang.annotation.ElementType;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.weaver.model.ScanRequest;
import org.apache.commons.weaver.model.Scanner;
import org.apache.commons.weaver.model.WeavableClass;
import org.apache.commons.weaver.model.WeaveEnvironment;
import org.apache.commons.weaver.model.WeaveInterest;
import org.apache.commons.weaver.privilizer.Privilizer;
import org.apache.commons.weaver.spi.Cleaner;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;

/**
 * Removes classes privilized with a different policy.
 */
public class PrivilizerCleaner implements Cleaner {
    private static final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES;
    private static final Logger LOG = Logger.getLogger(PrivilizerCleaner.class.getName());

    @Override
    public boolean clean(WeaveEnvironment environment, Scanner scanner) {
        final Privilizer privilizer = new Privilizer(environment);

        final List<String> toDelete = new ArrayList<String>();

        final ScanRequest scanRequest = new ScanRequest().add(WeaveInterest.of(Privilized.class, ElementType.TYPE));

        LOG.log(Level.FINE, "Cleaning classes privilized with policy other than {0}", privilizer.policy);
        for (WeavableClass<?> weavableClass : scanner.scan(scanRequest).getClasses().with(Privilized.class)) {
            final Policy privilizedPolicy = Policy.valueOf(weavableClass.getAnnotation(Privilized.class).value());
            if (privilizedPolicy == privilizer.policy) {
                continue;
            }
            final String className = weavableClass.getTarget().getName();
            LOG.log(Level.FINE, "Class {0} privilized with {1}; deleting.",
                new Object[] { className, privilizedPolicy });

            try {
                final ClassReader classReader = new ClassReader(privilizer.fileArchive.getBytecode(className));
                classReader.accept(new ClassVisitor(Opcodes.ASM4) {
                    @Override
                    public void visit(int version, int access, String name, String signature, String superName,
                        String[] interfaces) {
                        toDelete.add(name);
                    }

                    @Override
                    public void visitInnerClass(String name, String outerName, String innerName, int access) {
                        if (toDelete.contains(outerName)) {
                            toDelete.add(name);
                        }
                    }
                }, ASM_FLAGS);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        boolean result = false;
        for (String className : toDelete) {
            final File classfile = new File(environment.target, toResourcePath(className));
            final boolean success = classfile.delete();
            LOG.log(Level.FINE, "Deletion of {0} was {1}.", new Object[] { classfile,
                success ? "successful" : "unsuccessful" });
            result |= success;
        }
        return result;
    }

    private static String toResourcePath(String className) {
        return className.replace('.', '/') + ".class";
    }

}

@@ -776,73 +776,8 @@
 ist;
-%0Aimport java.util.logging.Level;%0Aimport java.util.logging.Logger;
 %0A%0Aim
@@ -1043,64 +1043,8 @@
 st;%0A
-import org.apache.commons.weaver.privilizer.Privilizer;%0A
 impo
@@ -1315,214 +1315,8 @@
 er %7B
-%0A    private static final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES;%0A    private static final Logger LOG = Logger.getLogger(PrivilizerCleaner.class.getName());
 %0A%0A  
@@ -1659,36 +1659,34 @@
         
-LOG.log(Level.FINE, 
+environment.debug(
 %22Cleanin
@@ -1729,19 +1729,18 @@
 er than 
-%7B0%7D
+%25s
 %22, privi
@@ -2167,38 +2167,35 @@
     
-LOG.log(Level.FINE, 
+environment.debug(
 %22Class 
-%7B0%7D
+%25s
  pri
@@ -2207,19 +2207,18 @@
 ed with 
-%7B1%7D
+%25s
 ; deleti
@@ -2226,39 +2226,8 @@
 g.%22,
-%0A                new Object%5B%5D %7B
  cla
@@ -2250,18 +2250,16 @@
 edPolicy
- %7D
 );%0A%0A    
@@ -3028,16 +3028,79 @@
  %7D, 
-ASM_FLAG
+ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAME
 S);%0A
@@ -3446,28 +3446,26 @@
     
-LOG.log(Level.FINE, 
+environment.debug(
 %22Del
@@ -3477,106 +3477,61 @@
  of 
-%7B0%7D
+%25s
  was 
-%7B1%7D.%22, new Object%5B%5D %7B classfile,%0A                success ? %22successful%22 : %22unsuccessful%22 %7D
+%25ssuccessful.%22, classfile, success ? %22%22 : %22un%22
 );%0A 

use environment-provided logging and inline ASM_FLAGS