package accrue.analysis.pointer.analyses;

import polyglot.types.MemberInstance;
import accrue.analysis.pointer.CContext;
import accrue.analysis.pointer.CallSiteNode;
import accrue.analysis.pointer.HContext;

/**
 * Analysis that keeps the first N call sites that matches a particular prefix. 
 * Once the context stack is full, no more call sites are tracked, even if they 
 * match. This can be used together with a more general analysis, using 
 * {@link CrossProductAnalysis}, to gain distinguish calls to particular methods 
 * or packages. For example to calls to the standard library by using 
 * "java.lang" for the prefix.
 */
public class StickFirstCallee extends StickFirstMatches {

    /**
     * Any call sites to methods with the given prefix will be tracked up to the specified sensitivity.
     */
    private final String prefix;

    /**
     * Create an analysis with the default sensitivity and default string. This 
     * is meant for testing only.
     */
    public StickFirstCallee() {
        this("java");
    }

    /**
     * Create an analysis that tracks methods with a particular prefix with the 
     * given context-call-stack depth, once that depth is reached the context is 
     * "stuck" and no more call sites are added.
     * 
     * @param sensitivity
     *            depth of the call stack to keep track of
     * @param prefixToTrack
     *            Methods with this prefix will be tracked up to the sensitivity, 
     *            deeper than the sensitivity they will "stick" in the context.
     */
    public StickFirstCallee(int sensitivity, String prefixToTrack) {
        super(sensitivity);
        this.prefix = prefixToTrack;
    }

    /**
     * Create an analysis that tracks methods with a particular prefix with the 
     * given context-call-stack depth, once that depth is reached the context is 
     * "stuck" and no more call sites are added.
     * 
     * @param prefixToTrack
     *            Methods with this prefix will be tracked up to the sensitivity, 
     *            deeper than the sensitivity they will "stick" in the context.
     */
    public StickFirstCallee(String prefixToTrack) {
        this.prefix = prefixToTrack;
    }

    @Override
    public String toString() {
        return "CalleeStickFirst(" + this.prefix + ")";
    }

    @Override
    boolean match(CallSiteNode callSite, HContext receiver,
            CContext callerContext) {
        if (callSite.callee() instanceof MemberInstance) {
            MemberInstance mi = (MemberInstance) callSite.callee();
            if (mi.container().isClass()) {
                String fullName = mi.container().toClass().fullName();
                if (fullName.startsWith(prefix)) {
                    return true;
                }
            }
        }

        return false;
    }
}
