/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2004-2006 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.Map;
import java.util.Set;

import javax.annotation.CheckForNull;

import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.Method;

import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.MethodAnnotation;
import edu.umd.cs.findbugs.OpcodeStack;
import edu.umd.cs.findbugs.ProgramPoint;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.FieldSummary;
import edu.umd.cs.findbugs.ba.PutfieldScanner;
import edu.umd.cs.findbugs.ba.XField;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;

public class ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass extends OpcodeStackDetector {

	BugReporter bugReporter;

	BugAccumulator accumulator;

	public ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass(BugReporter bugReporter) {
		this.bugReporter = bugReporter;
		this.accumulator = new BugAccumulator(bugReporter);
	}
	
	Set<XField> initializedFields, nullCheckedFields;

	@Override
    public void visit(Code obj) {
		if (getMethod().isStatic())
			return;
		initializedFields = new HashSet<XField>();
		nullCheckedFields = new HashSet<XField>();
		super.visit(obj);
		accumulator.reportAccumulatedBugs();
	}

	@Override
	public void sawOpcode(int opcode) {
		if (opcode == PUTFIELD) {
			XField f = getXFieldOperand();
			OpcodeStack.Item item = stack.getStackItem(1);
			if (item.getRegisterNumber() != 0)
				return;
			initializedFields.add(f);
			return;
		}
		if (opcode != GETFIELD)
			return;
		OpcodeStack.Item item = stack.getStackItem(0);
		if (item.getRegisterNumber() != 0)
			return;
		XField f = getXFieldOperand();
		
		if (f == null || !f.getClassDescriptor().equals(getClassDescriptor()))
			return;
		if (f.isSynthetic() || f.getName().startsWith("this$"))
			return;
		if (initializedFields.contains(f))
			return;
		FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();

		ClassDescriptor superClassDescriptor = DescriptorFactory
		        .createClassDescriptor(getSuperclassName());
		Set<ProgramPoint> calledFrom = fieldSummary.getCalledFromSuperConstructor(superClassDescriptor, getXMethod());
		if (calledFrom.isEmpty())
			return;
		UnreadFields unreadFields = AnalysisContext.currentAnalysisContext().getUnreadFields();
		
		int priority;
		if (!unreadFields.isWrittenInConstructor(f))
			return;
		
		if (f.isFinal())
			priority = HIGH_PRIORITY;
		else if (unreadFields.isWrittenDuringInitialization(f) || unreadFields.isWrittenOutsideOfInitialization(f) )
			priority = NORMAL_PRIORITY;
		else
			priority = HIGH_PRIORITY;
		
		int nextOpcode = getNextOpcode();
		if (nullCheckedFields.contains(f) || nextOpcode == IFNULL || nextOpcode == IFNONNULL || nextOpcode == IFEQ || nextOpcode == IFNE) {
			priority++;
			nullCheckedFields.add(f);
		}
		
		for (ProgramPoint p : calledFrom) {
			XMethod upcall = getConstructorThatCallsSuperConstructor(p.method);
			if (calledFrom == null) 
				continue;
			Method upcallMethod = null;
			for(Method m : getThisClass().getMethods()) {
				if (m.getName().equals(upcall.getName()) 
						&& m.getSignature().equals(upcall.getSignature())) {
						upcallMethod = m;
						break;
				}
			}
			if (upcallMethod == null)
				continue;
			Map<Integer, OpcodeStack.Item> putfieldsAt = PutfieldScanner.getPutfieldsFor(getThisClass(), upcallMethod, f);
			if (putfieldsAt.isEmpty())
				continue;
			Map.Entry<Integer, OpcodeStack.Item> e = putfieldsAt.entrySet().iterator().next();
			int pc = e.getKey();
			OpcodeStack.Item value = e.getValue();
			if (value.isNull() || value.hasConstantValue(0)) 
				priority++;
			
			SourceLineAnnotation fieldSetAt = SourceLineAnnotation.fromVisitedInstruction(getThisClass(), upcallMethod, pc);
			
			BugInstance bug = new BugInstance(this, "UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR", priority).addClassAndMethod(this).addField(f);
			bug.addMethod(p.method).describe(MethodAnnotation.METHOD_SUPERCLASS_CONSTRUCTOR)
			   .addSourceLine(p.getSourceLineAnnotation()).describe(SourceLineAnnotation.ROLE_CALLED_FROM_SUPERCLASS_AT)
			   .addMethod(upcall).describe(MethodAnnotation.METHOD_CONSTRUCTOR)
			   .add(fieldSetAt).describe(SourceLineAnnotation.ROLE_FIELD_SET_TOO_LATE_AT);
			   
			accumulator.accumulateBug(bug, this);
		}
			

		
	}
	
	private @CheckForNull XMethod getConstructorThatCallsSuperConstructor(XMethod superConstructor) {
		FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();

		XMethod lookfor = superConstructor.getSignature().equals("()V") ? null : superConstructor;
		for(XMethod m : getXClass().getXMethods()) if (m.getName().equals("<init>")) {
			if (fieldSummary.getSuperCall(m) == lookfor) 
				return m;
		}
		return null;
	}

}

@@ -4114,16 +4114,128 @@
 = null) 
+// bug; wrong thing to check, we should generate a RCN kaboom warning here%0A%09%09%09%09continue;%0A%09%09%09if (upcall == null) 
 %0A%09%09%09%09con

fix NPE that should have been found by findbugs