/*
 * fb-contrib - Auxiliary detectors for Java programs
 * Copyright (C) 2005-2014 Dave Brosius
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package com.mebigfatguy.fbcontrib.collect;

import java.util.HashSet;
import java.util.Set;

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.AnnotationEntry;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;

import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.NonReportingDetector;
import edu.umd.cs.findbugs.ba.ClassContext;

public class CollectStatistics extends BytecodeScanningDetector implements NonReportingDetector
{
	private static Set<String> COMMON_METHOD_SIGS = new HashSet<String>();
	static {
		COMMON_METHOD_SIGS.add("toString\\(\\)Ljava/lang/String;");
		COMMON_METHOD_SIGS.add("hashCode\\(\\)I");
		COMMON_METHOD_SIGS.add("clone\\(\\).*");
		COMMON_METHOD_SIGS.add("values\\(\\).*");
		COMMON_METHOD_SIGS.add("main\\(\\[Ljava/lang/String;\\)V");
	}
	private int numMethodCalls;
	private boolean classHasAnnotation;

	public CollectStatistics(BugReporter bugReporter) {
		Statistics.getStatistics().clear();
	}

    @Override
    public void visitClassContext(ClassContext classContext) {
    	if (classContext.getAnalysisContext().isApplicationClass(classContext.getJavaClass())) {
	    	JavaClass cls = classContext.getJavaClass();
	    	AnnotationEntry[] annotations = cls.getAnnotationEntries();
	    	classHasAnnotation = (annotations != null) && (annotations.length > 0);
	    	super.visitClassContext(classContext);
    	}
	}

	@Override
	public void visitCode(Code obj) {

		numMethodCalls = 0;

		byte[] code = obj.getCode();
		if (code != null) {
			super.visitCode(obj);
			String clsName = getClassName();
			int accessFlags = getMethod().getAccessFlags();
			MethodInfo mi = Statistics.getStatistics().addMethodStatistics(clsName, getMethodName(), getMethodSig(), accessFlags, obj.getLength(), numMethodCalls);
			if (clsName.contains("$") || ((accessFlags & (ACC_ABSTRACT|ACC_INTERFACE|ACC_ANNOTATION)) != 0)) {
				mi.addCallingAccess(Constants.ACC_PUBLIC);
			} else if ((accessFlags & Constants.ACC_PRIVATE) == 0) {
				if (isAssociationedWithAnnotations(getMethod())) {
					mi.addCallingAccess(Constants.ACC_PUBLIC);
				} else {
					String methodSig = getMethodName() + getMethodSig();
					for (String sig : COMMON_METHOD_SIGS) {
						if (methodSig.matches(sig)) {
							mi.addCallingAccess(Constants.ACC_PUBLIC);
						}
					}
				}
			}
		}
	}

	@Override
	public void sawOpcode(int seen) {
		switch (seen) {
			case INVOKEVIRTUAL:
			case INVOKEINTERFACE:
			case INVOKESPECIAL:
			case INVOKESTATIC:
				numMethodCalls++;
			break;
			default:
				break;
		}
	}

	public boolean isAssociationedWithAnnotations(Method m) {
		if (classHasAnnotation) {
			return true;
		}

		AnnotationEntry[] annotations = m.getAnnotationEntries();
    	return (annotations != null) && (annotations.length > 0);
	}
}

@@ -2004,103 +2004,8 @@
 ) %7B%0A
-    %09if (classContext.getAnalysisContext().isApplicationClass(classContext.getJavaClass())) %7B%0A%09
     
@@ -2046,25 +2046,24 @@
 avaClass();%0A
-%09
     %09Annotat
@@ -2115,17 +2115,16 @@
 ries();%0A
-%09
     %09cla
@@ -2192,17 +2192,16 @@
 h %3E 0);%0A
-%09
     %09sup
@@ -2240,15 +2240,8 @@
 t);%0A
-    %09%7D%0A
 %09%7D%0A%0A

revert commit 113620c83ea77a795212e244a006f02d9548639c: need method size/complexity for all classes for PRMC