/*
 * 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 java.util.Iterator;
import java.util.Set;

import org.apache.bcel.classfile.Code;

import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.LocalVariableAnnotation;
import edu.umd.cs.findbugs.StatelessDetector;
import edu.umd.cs.findbugs.ba.SignatureParser;

public class FindFieldSelfAssignment extends BytecodeScanningDetector implements StatelessDetector {
	private BugReporter bugReporter;
	int state;



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



	@Override
		 public void visit(Code obj) {
		state = 0;
		super.visit(obj);
		initializedFields.clear();
	}


	int register;
	String f;
	String className;
	Set<String> initializedFields = new HashSet<String>();

	@Override
		 public void sawOpcode(int seen) {

		switch (state) {
		case 0:
			if (seen == ALOAD_0)
				state = 1;
			else if (seen == DUP)
				state = 6;
			break;
		case 1:
			if (seen == ALOAD_0)
				state = 2;
			else
				state = 0;
			break;
		case 2:
			if (seen == GETFIELD) {
				state = 3;
				f = getRefConstantOperand();
				className = getClassConstantOperand();
			} else
				state = 0;
			break;
		case 3:
			if (seen == PUTFIELD && getRefConstantOperand().equals(f) && getClassConstantOperand().equals(className)) {

				int priority = NORMAL_PRIORITY;
				
				LocalVariableAnnotation possibleMatch = LocalVariableAnnotation.findMatchingIgnoredParameter(getClassContext(), getMethod(), 
						getSigConstantOperand());
				if (possibleMatch != null)
					priority = HIGH_PRIORITY;
				else 
					possibleMatch = LocalVariableAnnotation.findUniqueMatchingParameter(getClassContext(), getMethod(), 
							getSigConstantOperand());
				
				bugReporter.reportBug(new BugInstance(this, "SA_FIELD_SELF_ASSIGNMENT", priority)
						.addClassAndMethod(this)
						.addReferencedField(this)
						.addOptionalAnnotation(possibleMatch)
						.addSourceLine(this));
			}
			state = 0;
			break;
		 case 6:
			if (isRegisterStore()) {
				state = 7;
				register = getRegisterOperand();
			} else state = 0;
			break;
		case 7:
			if (isRegisterStore() && register ==  getRegisterOperand()) {
				bugReporter.reportBug(new BugInstance(this, "SA_LOCAL_DOUBLE_ASSIGNMENT", NORMAL_PRIORITY)
				.addClassAndMethod(this)
				.add( LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), register, getPC(), getPC()-1))
				.addSourceLine(this));
			} 
			state = 0;
			break;
		}

		if (seen == PUTFIELD  && getClassConstantOperand().equals(className))
			initializedFields.add(getRefConstantOperand());

	}

}

@@ -2537,16 +2537,17 @@
 %09%09%09else 
+%7B
 %0A%09%09%09%09%09po
@@ -2674,24 +2674,145 @@
 Operand());%0A
+%09%09%09%09%09if (possibleMatch != null && possibleMatch.getName().toLowerCase().equals(f.toLowerCase()))%0A%09%09%09%09%09%09priority--;%0A%09%09%09%09%7D%0A
 %09%09%09%09%0A%09%09%09%09bug

raise priority if names are identical except for case; really need an edit distance function