/*
 *  Copyright 2013
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may use this file in compliance with the Apache License, Version 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 net.sf.plugfy.verifier.java;

import java.io.IOException;

import net.sf.plugfy.verifier.VerificationContext;
import net.sf.plugfy.verifier.Verifier;

import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;

/**
 * verifies a java class
 *
 * @author hendrik
 */
public class ClassVerifier implements Verifier {
    private VerificationContext context;
    private JavaClass javaClass;
    private ConstantPoolGen cpg;
    private ClassVisitor visitor;

    /**
     * verifies the resource
     * @param name filename
     * @param verificationContext verification context
     *
     * @throws IOException in case of an input/output error
     */
    @Override
    public void verify(final String name, final VerificationContext verificationContext) throws IOException {
        this.context = verificationContext;
        try {
            String prefix = verificationContext.getUnderVerification().getFile();
            String className = name.replace(prefix, "").replace('/', '.').replaceAll("\\.class$", "");
            this.javaClass = this.context.getRepository().loadClass(className);
        } catch (final ClassNotFoundException e) {
            throw new IOException(e);
        }
        this.cpg = new ConstantPoolGen(this.javaClass.getConstantPool());
        this.visitor = new ClassVisitor(this.javaClass, this.cpg, verificationContext);

        this.analyzeClassSignature();
        this.analyzeFields();
        this.analyzeMethods();
    }

    /**
     * analyze the class signature
     */
    private void analyzeClassSignature() {
        SignatureUtil.checkSignatureDependencies(this.context.getRepository(), this.context.getResult(), this.javaClass);
    }

    /**
     * analyze fields
     */
    private void analyzeFields() {
        for (final Field field : this.javaClass.getFields()) {
            this.analyzeField(field);
        }
    }

    /**
     * analyze field
     *
     * @param field field
     */
    private void analyzeField(final Field field) {
        SignatureUtil.checkSignatureDependencies(this.context.getRepository(), this.context.getResult(), field, this.javaClass.getClassName());
    }

    /**
     * analyze methods
     */
    private void analyzeMethods() {
        for (final Method method : this.javaClass.getMethods()) {
            this.analyzeMethod(method);
        }
    }

    /**
     * analyze method
     *
     * @param method method
     */
    private void analyzeMethod(final Method method) {

        // check real and generic signature of the method
        SignatureUtil.checkSignatureDependencies(this.context.getRepository(), this.context.getResult(), method, this.javaClass.getClassName());

        // check types of variables
        final LocalVariableTable localVariableTable = method.getLocalVariableTable();
        if (localVariableTable != null) {
            for (final LocalVariable localVariable : localVariableTable.getLocalVariableTable()) {
                SignatureUtil.checkSignatureDependencies(this.context.getRepository(), this.context.getResult(), localVariable.getSignature(), this.javaClass.getClassName());
            }
        }

        // check code
        final MethodGen mg = new MethodGen(method, this.javaClass.getClassName(), this.cpg);
        final InstructionList instructionList = mg.getInstructionList();
        if (instructionList != null) {
            InstructionHandle handle = instructionList.getStart();
            while (handle != null) {
                final Instruction instruction = handle.getInstruction();
                instruction.accept(this.visitor);
                handle = handle.getNext();
            }
        }
    }
}

@@ -562,16 +562,37 @@
 ception;
+%0Aimport java.net.URL;
 %0A%0Aimport
@@ -1134,16 +1134,56 @@
 thodGen;
+%0Aimport org.apache.bcel.util.Repository;
 %0A%0A/**%0A *
@@ -1815,21 +1815,29 @@
     
-String prefix
+URL underVerification
  = v
@@ -1877,16 +1877,63 @@
 cation()
+;%0A            String prefix = underVerification
 .getFile
@@ -1955,25 +1955,33 @@
  String 
-className
+nameWithoutPrefix
  = name.
@@ -1999,16 +1999,66 @@
 fix, %22%22)
+;%0A            String className = nameWithoutPrefix
 .replace
@@ -2109,30 +2109,37 @@
         
-this.javaClass
+Repository repository
  = this.
@@ -2161,16 +2161,57 @@
 sitory()
+;%0A            this.javaClass = repository
 .loadCla

split instructions to ease debugging