/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2003-2007 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.ba.jsr305;

import edu.umd.cs.findbugs.ba.BasicBlock;
import org.apache.bcel.generic.ConstantPoolGen;

import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.DepthFirstSearch;
import edu.umd.cs.findbugs.ba.ReverseDepthFirstSearch;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.ba.vna.ValueNumber;
import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
import edu.umd.cs.findbugs.classfile.Global;
import edu.umd.cs.findbugs.classfile.IAnalysisCache;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import javax.annotation.meta.When;

/**
 * Factory for BackwardTypeQualifierDataflow objects
 * for given type qualifier values.
 * 
 * @author David Hovemeyer
 */
public class BackwardTypeQualifierDataflowFactory
	extends TypeQualifierDataflowFactory
	<
	BackwardTypeQualifierDataflowAnalysis,
	BackwardTypeQualifierDataflow
	> {

	/**
	 * Constructor.
	 * 
	 * @param methodDescriptor MethodDescriptor of the method for which we
	 *                         want to create BackwardTypeQualifierDataflow objects
	 */
	public BackwardTypeQualifierDataflowFactory(MethodDescriptor methodDescriptor) {
		super(methodDescriptor);
	}

	/* (non-Javadoc)
	 * @see edu.umd.cs.findbugs.ba.jsr305.TypeQualifierDataflowFactory#getDataflow(edu.umd.cs.findbugs.ba.DepthFirstSearch, edu.umd.cs.findbugs.ba.XMethod, edu.umd.cs.findbugs.ba.CFG, edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow, org.apache.bcel.generic.ConstantPoolGen, edu.umd.cs.findbugs.classfile.IAnalysisCache, edu.umd.cs.findbugs.classfile.MethodDescriptor, edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValue)
	 */
	@Override
	protected BackwardTypeQualifierDataflow getDataflow(DepthFirstSearch dfs, XMethod xmethod, CFG cfg,
			ValueNumberDataflow vnaDataflow, ConstantPoolGen cpg, IAnalysisCache analysisCache,
			MethodDescriptor methodDescriptor, TypeQualifierValue typeQualifierValue) throws CheckedAnalysisException {
		ReverseDepthFirstSearch rdfs = analysisCache.getMethodAnalysis(ReverseDepthFirstSearch.class, methodDescriptor);
		
		BackwardTypeQualifierDataflowAnalysis analysis = new BackwardTypeQualifierDataflowAnalysis(
				dfs, rdfs, xmethod, cfg, vnaDataflow, cpg, typeQualifierValue
				);

		// Get the corresponding forward dataflow.
		// We use it to halt tracking of backwards values once we know
		// that they encounter a conflicting forward value.
		ForwardTypeQualifierDataflowFactory forwardFactory =
			analysisCache.getMethodAnalysis(ForwardTypeQualifierDataflowFactory.class, methodDescriptor);
		ForwardTypeQualifierDataflow forwardDataflow = forwardFactory.getDataflow(typeQualifierValue);
		analysis.setForwardTypeQualifierDataflow(forwardDataflow);
		analysis.registerSourceSinkLocations();
		
		BackwardTypeQualifierDataflow dataflow = new BackwardTypeQualifierDataflow(cfg, analysis);
		
		dataflow.execute();
		
		return dataflow;
	}

	@Override
	protected void populateDatabase(BackwardTypeQualifierDataflow dataflow, ValueNumberDataflow vnaDataflow, XMethod xmethod, TypeQualifierValue tqv) throws CheckedAnalysisException {
		assert TypeQualifierDatabase.USE_DATABASE;
		
		// Get the dataflow fact that propagated
		// back to the entry of the method.
		// This will contain the effective type qualifier
		// annotations on the method parameters.
		
		BasicBlock entry = dataflow.getCFG().getEntry();
		TypeQualifierValueSet entryFact = dataflow.getAnalysis().getResultFact(entry);
		
		for (int i = 0; i < xmethod.getNumParams(); i++) {
			if (TypeQualifierApplications.getEffectiveTypeQualifierAnnotation(xmethod, i, tqv) != null) {
				// this parameter already has an explicit annotation
				continue;
			}
			
			// Get the value number for this parameter
			ValueNumber paramVN = vnaDataflow.getAnalysis().getEntryValueForParameter(i);
			
			FlowValue paramFlowValue = entryFact.getValue(paramVN);
			if (paramFlowValue == FlowValue.ALWAYS || paramFlowValue == FlowValue.NEVER) {
				TypeQualifierDatabase tqdb = Global.getAnalysisCache().getDatabase(TypeQualifierDatabase.class);
				TypeQualifierAnnotation tqa = TypeQualifierAnnotation.getValue(
					tqv, paramFlowValue == FlowValue.ALWAYS ? When.ALWAYS : When.NEVER);
				tqdb.setParameter(xmethod.getMethodDescriptor(), i, tqv, tqa);
			}
		}
	}
}
