Skip to content
EKnife.java 6.56 KiB
Newer Older
Carlos Galindo's avatar
Carlos Galindo committed
/*
 * e-Knife, a program slicing tool for Erlang based on the EDG
 * Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

Carlos Galindo's avatar
Carlos Galindo committed
package eknife;

import edg.DotFactory;
import edg.EDGFactory;
Carlos Galindo's avatar
Carlos Galindo committed
import edg.PdfFactory;
Carlos Galindo's avatar
Carlos Galindo committed
import edg.graph.EDG;
import edg.graph.LAST;
Carlos Galindo's avatar
Carlos Galindo committed
import edg.graph.Node;
import edg.slicing.ConstrainedAlgorithm;
import edg.slicing.SlicingAlgorithm;
import edg.slicing.SlicingCriterion;

import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
Carlos Galindo's avatar
Carlos Galindo committed

public class EKnife
{
	public enum Language { Java, Erlang, Php }

	public static void main(String[] args)
	{
		if (args.length == 0) {
			EKnife.printHelp();
			return;
		}

Carlos Galindo's avatar
Carlos Galindo committed
		final Args arguments = EKnife.processArguments(args);

		if (!arguments.isValid()) {
			EKnife.printHelp();
			System.exit(3);
		}
		EKnife.run(arguments);
	}

	private static Args processArguments(String[] args)
	{
		Args kArgs = new Args();

		for (int argIndex = 0; argIndex < args.length; argIndex++)
		{
			final String arg = args[argIndex];

			switch (arg)
			{
				case "-i": case "--input":
				case "-o": case "--output":
				case "-f": case "--file":
				case "-l": case "--line":
				case "-v": case "--var":
				case "-n": case "--occurrence":
Carlos Galindo's avatar
Carlos Galindo committed
				case "-G": case "--print-graph":
					if (argIndex == args.length - 1) {
						System.out.printf("Parameter %s requires an argument\n", arg);
						printHelp();
						System.exit(1);
					}
Carlos Galindo's avatar
Carlos Galindo committed
			}
			switch (arg)
			{
				case "--help":
					printHelp();
					System.exit(0);
				case "-i": case "--input":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.inputPath = args[argIndex + 1];
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-o": case "--output":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.outputFile = new File(args[argIndex + 1]);
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-f": case "--file":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.file = args[argIndex + 1];
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-l": case "--line":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.line = Integer.parseInt(args[argIndex + 1]);
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-v": case "--var":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.name = args[argIndex + 1];
					break;
Carlos Galindo's avatar
Carlos Galindo committed
				case "-n": case "--occurrence":
Carlos Galindo's avatar
Carlos Galindo committed
					kArgs.occurrence = Integer.parseInt(args[argIndex + 1]);
					break;
				case "-G": case "--print-graph":
					kArgs.setGraphFile(new File(args[argIndex + 1]));
					break;
Carlos Galindo's avatar
Carlos Galindo committed
			}
		}

		kArgs.completeData();
		return kArgs;
	}
	private static void printHelp()
	{
		String help = "";

		help += "Use the following options:\n";
		help += "  -i,--input  <file/folder>   The file/folder where the source code is\n";
		help += "  -o,--output <file/folder>   The file/folder where the slice will be stored\n";
		help += "  -f,--file   <file>          The file (relative to -i) where the slicing criterion is\n";
		help += "  -l,--line   <num>           The line of the slicing criterion\n";
		help += "  -v,--var    <name>          The name of the slicing criterion (must be a variable)\n";
		help += "  -n,--occurrence <num>       The occurrence of the slicing criterion in that line\n";
		help += "  -G,--print-graph <file.dot> Exports the graph as a dot file\n";
		help += "  -G,--print-graph <file.pdf> Exports the graph as a PDF file\n";
Carlos Galindo's avatar
Carlos Galindo committed
		help += "  --help                      Show this message.\n";

		System.out.print(help);
	}

	private static void run(Args a)
	{
		final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true);
		final EDG edg = new EDGFactory(last).createEDG();
Carlos Galindo's avatar
Carlos Galindo committed
		final SlicingCriterion slicingCriterion = new SlicingCriterion(a.file, a.line, a.name, a.occurrence);
		final Node SC = edg.getNode(slicingCriterion);
		if (SC == null) {
			System.out.println("Error: the slicing criterion could not be found! " + slicingCriterion);
			System.exit(1);
		}
		final SlicingAlgorithm slicingAlgorithm = new ConstrainedAlgorithm(edg);
		final Set<Node> slice = slicingAlgorithm.slice(SC);
Carlos Galindo's avatar
Carlos Galindo committed
		if (slice.isEmpty()) {
			System.out.println("Warning: no files will be written, as the slice is empty.");
			System.exit(2);
		}
Carlos Galindo's avatar
Carlos Galindo committed
		if (a.graphFile != null) {
			switch (a.graphFormat) {
				case DOT:
					DotFactory.createDot(a.graphFile, edg, SC, slice);
					break;
				case PDF:
					PdfFactory.createPdf(a.graphFile, edg, SC, slice);
					break;
			}
		}
Carlos Galindo's avatar
Carlos Galindo committed
		CodeFactory.createCode(Language.Erlang, a.outputFile, edg, slice);
	}

	static class Args {
Carlos Galindo's avatar
Carlos Galindo committed
		enum GraphFormat { PDF, DOT }

Carlos Galindo's avatar
Carlos Galindo committed
		String inputPath;
		File outputFile;
		String file;
		int line;
		String name;
		int occurrence = 1;
Carlos Galindo's avatar
Carlos Galindo committed
		File graphFile;
		GraphFormat graphFormat;


		void setGraphFile(File graphFile) {
			if (graphFile.getName().endsWith(".dot"))
				graphFormat = GraphFormat.DOT;
			else if (graphFile.getName().endsWith(".pdf"))
				graphFormat = GraphFormat.PDF;
			else {
				System.out.println("The graph file must end in .dot or .pdf, you set it to " + graphFile);
				System.exit(1);
			}
			this.graphFile = graphFile;
		}
Carlos Galindo's avatar
Carlos Galindo committed

		void completeData() {
			if (file == null && inputPath != null && new File(inputPath).isFile())
				file = new File(inputPath).getName();
		}

		boolean isValid() {
			List<String> errors = new LinkedList<>();
			if (inputPath == null)
				errors.add("You must specify a file to analyze with '-i'.");
			else if (!new File(inputPath).exists())
				errors.add("The input file you've specified does not exist or isn't readable(" + inputPath + ").");
			else if (file == null)
				errors.add("The input file is a folder, so you must specify a the file that contains the slicing criterion with '-f'.");
			if (outputFile == null)
				errors.add("You must specify a location for the output with '-o'.");
			else if (!outputFile.exists() && !outputFile.getAbsoluteFile().getParentFile().isDirectory())
				errors.add("The output file's parent folder does not exist, or the output folder does not exist.");
			if (line <= 0)
				errors.add("You must specify a line number greater than 0 with '-l'.");
			if (name == null || name.isEmpty())
				errors.add("You must specify a valid variable name with '-v'.");
			if (occurrence <= 0)
				errors.add("You must specify an occurrence greater than 0 with '-n'.");
			for (String error : errors)
				System.out.println(error);
			return errors.isEmpty();
Carlos Galindo's avatar
Carlos Galindo committed
		}
	}
}