/*
 * 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 javax.annotation.meta.When;

import org.apache.bcel.generic.ConstantPoolGen;

import edu.umd.cs.findbugs.ba.BasicBlock;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.ClassContext;
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;

/**
 * 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();
        if (ClassContext.DUMP_DATAFLOW_ANALYSIS) {
            dataflow.dumpDataflow(analysis);
        }
        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.
        if (xmethod.isIdentity())
            return;

        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);
            }
        }
    }
}
