/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2003-2005 University of Maryland
 * 
 * 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 edu.umd.cs.findbugs.detect;

import java.util.HashSet;

import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.Detector;
import edu.umd.cs.findbugs.NonReportingDetector;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.XFactory;
import edu.umd.cs.findbugs.ba.XField;
import edu.umd.cs.findbugs.ba.ch.Subtypes2;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;

/**
 * Detector to find private methods that are never called.
 */
public class CalledMethods extends BytecodeScanningDetector implements  NonReportingDetector {
	boolean emptyArrayOnTOS;
	HashSet<XField> emptyArray = new HashSet<XField>();
	HashSet<XField> nonEmptyArray = new HashSet<XField>();
	
	XFactory xFactory = AnalysisContext.currentXFactory();
	public CalledMethods(BugReporter bugReporter) {

	}

	@Override
	public void sawOpcode(int seen) {
		
		
		if ((seen == PUTFIELD || seen == PUTSTATIC)) {
			XField f = getXFieldOperand();
			if (f != null) {
				if (f.isFinal() || !f.isProtected() && !f.isPublic())
					if (emptyArrayOnTOS) emptyArray.add(f);
					else nonEmptyArray.add(f);
			}
			
				
		}
		emptyArrayOnTOS = (seen == ANEWARRAY || seen == NEWARRAY || seen == MULTIANEWARRAY && getIntConstant() == 1) && getPrevOpcode(1) == ICONST_0;

		if (seen == GETSTATIC || seen == GETFIELD) {
			XField f = getXFieldOperand();
			if (emptyArray.contains(f) && !nonEmptyArray.contains(f) && f.isFinal())
				emptyArrayOnTOS = true;
		}
		switch (seen) {
		case INVOKEVIRTUAL:
		case INVOKESPECIAL:
		case INVOKESTATIC:
		case INVOKEINTERFACE:
			ClassDescriptor c = getClassDescriptorOperand();
			Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
			if (subtypes2.isApplicationClass(c))
				xFactory.addCalledMethod(getMethodDescriptorOperand());

			break;
		default:
			break;
		}
	}
	
	@Override public void report() {
		emptyArray.removeAll(nonEmptyArray);
		for(XField f : emptyArray)
			xFactory.addEmptyArrayField(f);
		emptyArray.clear();
	}

}

// vim:ts=4
