Transform.java

package edu.udel.cis.vsl.abc.transform.IF;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

import edu.udel.cis.vsl.abc.ast.IF.ASTFactory;
import edu.udel.cis.vsl.abc.ast.entity.IF.Entity;
import edu.udel.cis.vsl.abc.err.IF.ABCRuntimeException;
import edu.udel.cis.vsl.abc.transform.common.CommonNameTransformer;
import edu.udel.cis.vsl.abc.transform.common.CompareCombiner;
import edu.udel.cis.vsl.abc.transform.common.ExternLinkageVariableRenamer;
import edu.udel.cis.vsl.abc.transform.common.GenericSelectionRemover;
import edu.udel.cis.vsl.abc.transform.common.Pruner;
import edu.udel.cis.vsl.abc.transform.common.SideEffectRemover;

/**
 * This class manages the set of transformations provided by an execution of
 * ABC.
 * 
 * It provides a static method {@link #newTransformer(String, ASTFactory)} to
 * create a new transformer instance based on the transformer code. It also
 * provides a method that lists all the known codes.
 * 
 * If you create a new {@link Transformer}, you may edit this file so it knows
 * about your transformer, following the pattern of the others: simply add an
 * entry to array {@link #records}. It is not required for you to do so---your
 * transformer can be used to transform a program whether or not it appears
 * here. The advantage of appearing here is that a switch will be automatically
 * added to the command line interface for all transformers appearing here.
 * 
 * Alternatively, you can add your transformer at runtime using method
 * {@link #addTransform(TransformRecord)}.
 * 
 * @author siegel
 * 
 */
public class Transform {

	private static CompareCombiner compareCombiner = new CompareCombiner();

	/**
	 * A list of the transformers available to the command line interface. Add
	 * one entry here when you create a new transformer, following the same
	 * pattern as the others.
	 */
	private static TransformRecord[] records = new TransformRecord[]{

			new TransformRecord(GenericSelectionRemover.CODE,
					GenericSelectionRemover.LONG_NAME,
					GenericSelectionRemover.SHORT_DESCRIPTION) {
				@Override
				public Transformer create(ASTFactory astFactory) {
					return new GenericSelectionRemover(astFactory);
				}
			},
			
			new TransformRecord(SideEffectRemover.CODE,
					SideEffectRemover.LONG_NAME,
					SideEffectRemover.SHORT_DESCRIPTION) {
				@Override
				public Transformer create(ASTFactory astFactory) {
					return new SideEffectRemover(astFactory);
				}
			},

			new TransformRecord(Pruner.CODE, Pruner.LONG_NAME,
					Pruner.SHORT_DESCRIPTION) {
				@Override
				public Transformer create(ASTFactory astFactory) {
					return new Pruner(astFactory);
				}
			},

			new TransformRecord(ExternLinkageVariableRenamer.CODE,
					ExternLinkageVariableRenamer.LONG_NAME,
					ExternLinkageVariableRenamer.SHORT_DESCRIPTION) {
				@Override
				public Transformer create(ASTFactory astFactory) {
					return new ExternLinkageVariableRenamer(astFactory);
				}
			}

			// add more here.

	};

	/**
	 * Map from transform codes to corresponding records.
	 */
	private static Map<String, TransformRecord> codeToRecord = new LinkedHashMap<>();

	/*
	 * Initializes codeToRecord using the list of hard-wired transforms in array
	 * records.
	 */
	static {
		for (TransformRecord record : records) {
			if (codeToRecord.put(record.code, record) != null)
				throw new ABCRuntimeException(
						"Two transformations named " + record.code);
		}
	}

	/**
	 * Adds a new transform to the collection of known ones. Use this method if
	 * you don't want to hardwire your transform in to the ABC code base. You
	 * will need to create an instance of TransformRecord, for example, in the
	 * anonymous way done above with the built-in transforms. It can be added at
	 * any time but usually it is done in some initialization phase.
	 * 
	 * @param record
	 *            a transform record
	 * @throws ABCRuntimeException
	 *             if there is already a transform with the given code in this
	 *             current collection
	 */
	public static void addTransform(TransformRecord record) {
		if (codeToRecord.put(record.code, record) != null)
			throw new ABCRuntimeException(
					"Two transformations named " + record.code);
	}

	/**
	 * Returns the set of transformer codes.
	 * 
	 * @return the set of transformer codes
	 */
	public static Collection<String> getCodes() {
		return codeToRecord.keySet();
	}

	/**
	 * Returns the short description associated to the given transformer code.
	 * 
	 * @param code
	 *            a transformer code currently in the collection
	 * @return the short description of the transformation
	 * @throws ABCRuntimeException
	 *             if there is no such code in the current collection
	 */
	public static String getShortDescription(String code) {
		TransformRecord record = codeToRecord.get(code);

		if (record == null)
			throw new ABCRuntimeException(
					"No transformation with code " + code);
		return record.shortDescription;
	}

	/**
	 * Returns the long name associated to the given transformer code.
	 * 
	 * @param code
	 *            a transformer code currently in the collection
	 * @return the long name of that transformer, e.g., "MPITransformer"
	 * @throws ABCRuntimeException
	 *             if there is no such code in the current collection
	 */
	public static String getLongName(String code) {
		TransformRecord record = codeToRecord.get(code);

		if (record == null)
			throw new ABCRuntimeException(
					"No transformation with code " + code);
		return record.name;
	}

	/**
	 * Returns the {@link TransformRecord} with the given <code>code</code>, or
	 * <code>null</code> if no such record exists.
	 * 
	 * @param code
	 *            a transformer code
	 * @return the {@link TransformRecord} with that code or <code>null</code>
	 */
	public static TransformRecord getRecord(String code) {
		return codeToRecord.get(code);
	}

	/**
	 * Produces a new transformer using the given AST Factory. The kind of
	 * transformer produced is determined by the given code, which is a string.
	 * This string is a short name used to identify the transformer, e.g.,
	 * "prune" for the Pruner. The short name can also be used as a commandline
	 * flag to induce use of that transformer, e.g., "abc -prune foo.c".
	 * 
	 * The given AST Factory become permanently identified with the new
	 * transformer. That transformer can only operate on ASTs that were produced
	 * with the same AST Factory.
	 * 
	 * 
	 * 
	 * @param code
	 *            a short string indicating the kind of transformer to produce
	 * @param astFactory
	 *            the AST factory that will is used to produce all the ASTs upon
	 *            which the new transformer will operate
	 * @return a new transformer instance of the specified kind
	 * @throws ABCRuntimeException
	 *             if there is no such code in the current collection
	 */
	public static Transformer newTransformer(String code,
			ASTFactory astFactory) {
		TransformRecord record = codeToRecord.get(code);

		if (record == null)
			throw new ABCRuntimeException(
					"No transformation with code " + code);
		return record.create(astFactory);
	}

	/**
	 * Gets the compare combiner for combining two CIVL programs into one which
	 * compares the two for functional equivalence.
	 * 
	 * @return the new compare combiner
	 */
	public static Combiner compareCombiner() {
		return Transform.compareCombiner;
	}

	public static NameTransformer nameTransformer(
			Map<Entity, String> newNameMap, ASTFactory astFactory) {
		return new CommonNameTransformer(newNameMap, astFactory);
	}
}