/*
 * Bytecode Analysis Framework
 * Copyright (C) 2003,2004 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;

import java.util.BitSet;
import java.util.Iterator;

import org.apache.bcel.generic.InstructionHandle;

import edu.umd.cs.findbugs.annotations.CheckForNull;

/**
 * A dataflow analysis to compute dominator relationships between
 * basic blocks.  Use the {@link #getResultFact} method to get the dominator
 * set for a given basic block.  The dominator sets are represented using
 * the {@link java.util.BitSet} class, with the individual bits
 * corresponding to the IDs of basic blocks.
 * <p/>
 * <p> Subclasses extend this class to compute either dominators
 * or postdominators.
 * <p/>
 * <p> An EdgeChooser may be specified to select which edges
 * to take into account. For example, exception edges could be
 * ignored.</p>
 *
 * @author David Hovemeyer
 * @see DataflowAnalysis
 * @see CFG
 * @see BasicBlock
 */
public abstract class AbstractDominatorsAnalysis extends BasicAbstractDataflowAnalysis<BitSet> {
	private final CFG cfg;
	private EdgeChooser edgeChooser;

	/**
	 * Constructor.
	 *
	 * @param cfg                  the CFG to compute dominator relationships for
	 * @param ignoreExceptionEdges true if exception edges should be ignored
	 */
	public AbstractDominatorsAnalysis(CFG cfg, final boolean ignoreExceptionEdges) {
		this(cfg, new EdgeChooser() {
			public boolean choose(Edge edge) {
				if (ignoreExceptionEdges && edge.isExceptionEdge())
					return false;
				else
					return true;
			}
		});
	}

	/**
	 * Constructor.
	 * 
	 * @param cfg         the CFG to compute dominator relationships for
	 * @param edgeChooser EdgeChooser to choose which Edges to consider significant
	 */
	public AbstractDominatorsAnalysis(CFG cfg, EdgeChooser edgeChooser) {
		this.cfg = cfg;
		this.edgeChooser = edgeChooser;
	}

	public BitSet createFact() {
		return new BitSet();
	}

	public void copy(BitSet source, BitSet dest) {
		dest.clear();
		dest.or(source);
	}

	public void initEntryFact(BitSet result) {
		// No blocks dominate the entry block
		result.clear();
	}

	public boolean isTop(BitSet fact) {
		// We represent TOP as a bitset with an illegal bit set
		return fact.get(cfg.getNumBasicBlocks());
	}

	public void makeFactTop(BitSet fact) {
		// We represent TOP as a bitset with an illegal bit set
		fact.set(cfg.getNumBasicBlocks());
	}

	public boolean same(BitSet fact1, BitSet fact2) {
		return fact1.equals(fact2);
	}

	public void transfer(BasicBlock basicBlock, @CheckForNull InstructionHandle end, BitSet start, BitSet result) throws DataflowAnalysisException {
		// Start with intersection of dominators of predecessors
		copy(start, result);

		if (!isTop(result)) {
			// Every block dominates itself
			result.set(basicBlock.getLabel());
		}
	}

	public void meetInto(BitSet fact, Edge edge, BitSet result) throws DataflowAnalysisException {
		if (!edgeChooser.choose(edge))
			return;

		if (isTop(fact))
			return;
		else if (isTop(result))
			copy(fact, result);
		else
		// Meet is intersection
			result.and(fact);
	}

	/**
	 * Get a bitset containing the unique IDs of
	 * all blocks which dominate (or postdominate) the given block.
	 * 
	 * @param block a BasicBlock
	 * @return BitSet of the unique IDs of all blocks that dominate
	 *         (or postdominate) the BasicBlock
	 */
	public BitSet getAllDominatorsOf(BasicBlock block) {
		return getResultFact(block);
	}

	/**
	 * Get a bitset containing the unique IDs of 
	 * all blocks in CFG dominated (or postdominated, depending
	 * on how the analysis was done) by given block.
	 *
	 * @param dominator we want to get all blocks dominated (or postdominated)
	 *                  by this block
	 * @return BitSet of the ids of all blocks dominated by the given block
	 */
	public BitSet getAllDominatedBy(BasicBlock dominator) {
		BitSet allDominated = new BitSet();
		for (Iterator<BasicBlock> i = cfg.blockIterator(); i.hasNext();) {
			BasicBlock block = i.next();
			BitSet dominators = getResultFact(block);
			if (dominators.get(dominator.getLabel()))
				allDominated.set(block.getLabel());
		}
		return allDominated;
	}

}

// vim:ts=4
