package com.mebigfatguy.fbcontrib.detect;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.Signature;

import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.Detector;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.visitclass.PreorderVisitor;

public class UnboundMethodTemplateParameter extends PreorderVisitor implements Detector {

    private static final Pattern TEMPLATED_SIGNATURE = Pattern.compile("(\\<[^\\>]+\\>)(.+)");
    private static final Pattern TEMPLATE = Pattern.compile("\\<?([^:]+):[^;]*;");
    private BugReporter bugReporter;


    public UnboundMethodTemplateParameter(BugReporter bugReporter) {
        this.bugReporter = bugReporter;
    }

    public void visitClassContext(ClassContext classContext) {
        JavaClass cls = classContext.getJavaClass();
        cls.accept(this);
    }

    @Override
    public void visitMethod(Method obj) {
        Attribute[] attributes = obj.getAttributes();
        for (Attribute a : attributes) {
            if (a.getName().equals("Signature")) {
                TemplateSignature ts = parseSignatureAttribute((Signature) a);
                if (ts != null) {
                    for (String templateParm : ts.templateParameters) {
                        if (!ts.signature.contains("<T" + templateParm + ";>") && !ts.signature.contains("[T" + templateParm + ";")) {
                            bugReporter.reportBug(new BugInstance(this, "UMTP_UNBOUND_METHOD_TEMPLATE_PARAMETER", NORMAL_PRIORITY)
                                        .addClass(this)
                                        .addMethod(this)
                                        .addString("Template Parameter: " + templateParm));
                            return;
                        }
                    }
                }
                return;
            }
        }
    }

    public void report() {
    }

    private TemplateSignature parseSignatureAttribute(Signature signatureAttribute) {

        Matcher m = TEMPLATED_SIGNATURE.matcher(signatureAttribute.getSignature());
        if (m.matches()) {
            TemplateSignature ts = new TemplateSignature();
            ts.signature = m.group(2);

            String template = m.group(1);

            m = TEMPLATE.matcher(template);
            List<String> templates = new ArrayList<String>();
            while (m.find()) {
                templates.add(m.group(1));
            }
            ts.templateParameters = templates.toArray(new String[templates.size()]);

            return ts;
        }

        return null;
    }

    static class TemplateSignature {
        String[] templateParameters;
        String signature;
    }
}

@@ -543,16 +543,241 @@
 sitor;%0A%0A
+/**%0A * Looks for methods that declare method level template parameter(s) that are not bound to any of the%0A * method's parameters, and thus is not adding any validation/type safety to the method, and is%0A * just confusing.%0A */%0A
 public c
@@ -1200,152 +1200,528 @@
     
-public void visitClassContext(ClassContext classContext) %7B%0A        JavaClass cls = classContext.getJavaClass();%0A        cls.accept(this);%0A    %7D%0A
+/**%0A     * implements the visitor to accept the class for visiting%0A     *%0A     * @param classContext the context object of the currently parsed class%0A     */%0A    public void visitClassContext(ClassContext classContext) %7B%0A        JavaClass cls = classContext.getJavaClass();%0A        cls.accept(this);%0A    %7D%0A%0A    /**%0A     * implements the visitor to find methods that declare template parameters%0A     * that are not bound to any parameter.%0A     *%0A     * @param obj the context object of the currently parsed method%0A     */
 %0A   
@@ -2762,24 +2762,276 @@
 () %7B%0A    %7D%0A%0A
+    /**%0A     * builds a template signature object based on the signature attribute of the method%0A     *%0A     * @param signatureAttribute the signature attribute%0A     * @return a template signature if there are templates defined, otherwise null%0A     */%0A
     private 
@@ -3697,24 +3697,129 @@
 ull;%0A    %7D%0A%0A
+    /**%0A     * a simple data only class for holding the template parameters and method signature%0A     */%0A
     static c

add javadoc