package accrue.analysis.notnulldataflow;

import polyglot.ast.Formal;
import polyglot.ast.MethodDecl;
import polyglot.ast.ProcedureDecl;
import polyglot.types.FieldInstance;
import polyglot.types.SemanticException;
import accrue.AccrueExtensionInfo;
import accrue.analysis.AnalysisTopics;
import accrue.analysis.interprocanalysis.AnalysisUnit;
import accrue.analysis.interprocanalysis.AnalysisUtil;
import accrue.analysis.interprocanalysis.WorkQueue;
import accrue.analysis.interprocvarcontext.AnalysisFactoryVarContext;
import accrue.analysis.interprocvarcontext.VarContext;

public class NotNullAnalysisFactory extends
        AnalysisFactoryVarContext<NotNullAbsVal> {
    public NotNullAnalysisFactory(AccrueExtensionInfo extInfo) {
        super(extInfo);
    }

    public static final String ANALYSIS_NAME =
            "accrue.analysis.notnulldataflow";

    @Override
    public String analysisName() {
        return ANALYSIS_NAME;
    }

    @Override
    public String analysisReportName() {
        return AnalysisTopics.NULL_ANALYSIS;
    }

    @Override
    public NotNullAbsVal bottomAbstractValue() {
        return NotNullAbsVal.NOT_NULL;
    }

    private static VarContext<NotNullAbsVal> INITIAL_VAR_CONTEXT =
            new VarContext<NotNullAbsVal>(ANALYSIS_NAME, NotNullAbsVal.NOT_NULL);

    @Override
    public NotNullAnalysisUtil analysisUtil(
            WorkQueue<VarContext<NotNullAbsVal>> wq, AnalysisUnit aunit) {
        return new NotNullAnalysisUtil(wq, aunit, extInfo);
    }

    @Override
    public VarContext<NotNullAbsVal> startItem(
            AnalysisUtil<VarContext<NotNullAbsVal>> autil,
            ProcedureDecl startProc) throws SemanticException {
        // the VarContext for main(String[] args)
        VarContext<NotNullAbsVal> c = INITIAL_VAR_CONTEXT;

        // need to map args to something.
        MethodDecl md = (MethodDecl) startProc;
        Formal args = md.formals().get(0);

        NotNullAnalysisUtil nautil = (NotNullAnalysisUtil) autil;

        // the array, and the contents, can never be null.
        c = c.addLocal(args.name(), NotNullAbsVal.NOT_NULL);
        c =
                nautil.addLocations(c,
                                    autil.abstractLocationsForArray(args),
                                    NotNullAbsVal.NOT_NULL);

        // set the length
        FieldInstance lengthFI =
                autil.typeSystem().findField(args.type().type().toArray(),
                                             "length");
        c =
                nautil.addLocations(c,
                                    autil.abstractLocations(args, lengthFI),
                                    NotNullAbsVal.NOT_NULL);

        return c;
    }

}
