diff --git a/.gitignore b/.gitignore index 0dbc612..d532590 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,13 @@ */*/*.csv csv/ +# Ignore Mac DS_Store files +.DS_Store +**/.DS_Store + +/output +/.idea/ + tempDesigniteDebugLog*.txt tempDesigniteLog*.txt designCodeSmells.txt @@ -22,4 +29,4 @@ designCodeSmells.txt hs_err_pid* /bin/ /target/ -target/ \ No newline at end of file +target/ diff --git a/src/Designite/ArgumentParser/ArgumentParser.java b/src/Designite/ArgumentParser/ArgumentParser.java new file mode 100644 index 0000000..710c90c --- /dev/null +++ b/src/Designite/ArgumentParser/ArgumentParser.java @@ -0,0 +1,30 @@ +package Designite.ArgumentParser; + +import org.apache.commons.cli.Option; + + +public abstract class ArgumentParser { + /** + * {@code createRequiredOption}. A method to initialise required {@link Option}. + * @param shortOpt + * @param longOpt + * @param description + * @return + */ + Option createRequiredOption(String shortOpt, String longOpt, String description) { + Option option = new Option(shortOpt, longOpt, true, description); + option.setRequired(true); + return option; + } + + /** + * {@code parseArguments} converts the appropriate {@code args} parameter from the system. + * It extracts the data from system arguments. + * @param args + * @return + */ + public abstract InputArgs parseArguments(String[] args); + + + +} diff --git a/src/Designite/ArgumentParser/CLIArgumentParser.java b/src/Designite/ArgumentParser/CLIArgumentParser.java new file mode 100644 index 0000000..11d8f0b --- /dev/null +++ b/src/Designite/ArgumentParser/CLIArgumentParser.java @@ -0,0 +1,41 @@ +package Designite.ArgumentParser; + +import Designite.utils.DJLogger; +import org.apache.commons.cli.*; + +/** + * {@code CLIArgumentParser} is a subclass of {@code ArgumentParser} that requires arguments from + * the console application in order to process the given arguments. + */ +public class CLIArgumentParser extends ArgumentParser { + + @Override + public InputArgs parseArguments(String[] args) { + Options argOptions = new Options(); + argOptions.addOption(this.createRequiredOption("i", "Input", "Input source folder path")); + argOptions.addOption(this.createRequiredOption("o", "Output", "Path to the output folder")); + CommandLineParser parser = new DefaultParser(); + HelpFormatter formatter = new HelpFormatter(); + CommandLine cmd = null; + try { + cmd = parser.parse(argOptions, args); + } catch (ParseException e) { + System.out.println(e.getMessage()); + formatter.printHelp("Designite", argOptions); + DJLogger.log("Quitting.."); + System.exit(1); + } + String inputFolderPath = cmd.getOptionValue("Input"); + String outputFolderPath = cmd.getOptionValue("Output"); + try { + return new InputArgs(inputFolderPath, outputFolderPath); + } catch (IllegalArgumentException ex) { + DJLogger.log(ex.getMessage()); + DJLogger.log("Quitting.."); + System.err.printf("The specified input path does not exist: %s%n", inputFolderPath); + System.exit(3); + } + return null; + } + +} diff --git a/src/Designite/InputArgs.java b/src/Designite/ArgumentParser/InputArgs.java similarity index 98% rename from src/Designite/InputArgs.java rename to src/Designite/ArgumentParser/InputArgs.java index ed7fd27..d1dc973 100644 --- a/src/Designite/InputArgs.java +++ b/src/Designite/ArgumentParser/InputArgs.java @@ -1,4 +1,4 @@ -package Designite; +package Designite.ArgumentParser; import java.io.File; diff --git a/src/Designite/ArgumentParser/RegularArgumentParser.java b/src/Designite/ArgumentParser/RegularArgumentParser.java new file mode 100644 index 0000000..7dc11b8 --- /dev/null +++ b/src/Designite/ArgumentParser/RegularArgumentParser.java @@ -0,0 +1,30 @@ +package Designite.ArgumentParser; + +import Designite.utils.DJLogger; + +import java.io.File; + + +/** + * {@code RegularArgumentParser} is a subclass of {@code ArgumentParser} that does not require any + * arguments. By default, it will use the current working directory (cwd) of the Java project + * to identify or analyze the smells in the current working project itself. + */ +public class RegularArgumentParser extends ArgumentParser { + + @Override + public InputArgs parseArguments(String[] args) { + String cwd = System.getProperty("user.dir"); + String inputFolderPath = String.join(File.separator, cwd, "src","Designite"); + String outputFolderPath = String.join(File.separator, cwd, "output"); + try { + return new InputArgs(inputFolderPath, outputFolderPath); + } catch (IllegalArgumentException ex) { + DJLogger.log(ex.getMessage()); + DJLogger.log("Quitting.."); + System.exit(3); + } + return null; + } + +} diff --git a/src/Designite/Designite.java b/src/Designite/Designite.java index 363564c..ada0ca6 100644 --- a/src/Designite/Designite.java +++ b/src/Designite/Designite.java @@ -1,134 +1,54 @@ package Designite; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import Designite.SourceModel.*; +import Designite.ArgumentParser.ArgumentParser; +import Designite.ArgumentParser.CLIArgumentParser; +import Designite.ArgumentParser.InputArgs; +import Designite.ArgumentParser.RegularArgumentParser; +import Designite.SourceModel.SM_Project; import Designite.utils.Constants; -import Designite.utils.Logger; +import Designite.utils.DJLogger; +import java.io.FileNotFoundException; import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.Calendar; -import org.apache.commons.cli.*; /** - * * This is the start of the project */ public class Designite { - public static void main(String[] args) throws IOException { - InputArgs argsObj = parseArguments(args); - SM_Project project = new SM_Project(argsObj); - Logger.logFile = getlogFileName(argsObj); - //TODO: log the version number - project.parse(); - project.resolve(); - project.computeMetrics(); - project.detectCodeSmells(); - if (Constants.DEBUG) - writeDebugLog(argsObj, project); - Logger.log("Done."); - } - - private static void writeDebugLog(InputArgs argsObj, SM_Project project) { - PrintWriter writer = getDebugLogStream(argsObj); - project.printDebugLog(writer); - if (writer != null) - writer.close(); - } - - private static InputArgs parseArguments(String[] args) { - Options argOptions = new Options(); - - Option input = new Option("i", "Input", true, "Input source folder path"); - input.setRequired(true); - argOptions.addOption(input); - - Option output = new Option("o", "Output", true, "Path to the output folder"); - output.setRequired(true); - argOptions.addOption(output); - - CommandLineParser parser = new DefaultParser(); - HelpFormatter formatter = new HelpFormatter(); - CommandLine cmd = null; - - try { - cmd = parser.parse(argOptions, args); - } catch (ParseException e) { - System.out.println(e.getMessage()); - formatter.printHelp("Designite", argOptions); - Logger.log("Quitting.."); - System.exit(1); - } - if(cmd==null) - { - System.out.println("Couldn't parse the command line arguments."); - formatter.printHelp("Designite", argOptions); - Logger.log("Quitting.."); - System.exit(2); + public static void main(String[] args) throws FileNotFoundException { + ArgumentParser argumentParser = (Constants.DEBUG) ? new RegularArgumentParser() : new CLIArgumentParser(); + InputArgs argsObj = argumentParser.parseArguments(args); + DJLogger.getInstance().setOutputDirectory(argsObj.getOutputFolder()); + SM_Project project = new SM_Project(argsObj); + project.parse(); + project.resolve(); + project.computeMetrics(); + project.detectCodeSmells(); + writeDebugLog(argsObj, project); + DJLogger.log("Done."); + } + + private static void writeDebugLog(InputArgs argsObj, SM_Project project) { + if (Constants.DEBUG) { + PrintWriter writer = getDebugLogStream(argsObj); + project.printDebugLog(writer); + if (writer != null) writer.close(); } - - String inputFolderPath = cmd.getOptionValue("Input"); - String outputFolderPath = cmd.getOptionValue("Output"); - - InputArgs inputArgs= null; - try - { - inputArgs = new InputArgs(inputFolderPath, outputFolderPath); + } + + private static PrintWriter getDebugLogStream(InputArgs argsObj) { + PrintWriter writer = null; + if (!argsObj.getOutputFolder().equals("")) { + String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(Calendar.getInstance().getTime()); + String filename = argsObj.getOutputFolder() + "DesigniteDebugLog" + timeStamp + ".txt"; + try { + writer = new PrintWriter(filename); + } catch (FileNotFoundException ex) { + DJLogger.log(ex.getMessage()); + } } - catch(IllegalArgumentException ex) - { - Logger.log(ex.getMessage()); - Logger.log("Quitting.."); - System.exit(3); - } - return inputArgs; - } - - private static String getlogFileName(InputArgs argsObj) { - String file = null; - String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(Calendar.getInstance().getTime()); - file = argsObj.getOutputFolder() + File.separator + "DesigniteLog" + timeStamp + ".txt"; - ensureOutputFolderExists(argsObj.getOutputFolder()); - return file; - } - - private static void ensureOutputFolderExists(String outputFolder) { - if (outputFolder == null) - return; - File folder = new File(outputFolder); - - if (folder.exists() && folder.isDirectory()) - return; - - try - { - boolean isCreated = folder.mkdirs(); - if (!isCreated) - { - System.out.println("Couldn't create output folder."); - } - } - catch (Exception ex) - { - System.out.println("Couldn't create output folder. " + ex.getMessage()); - } - } - private static PrintWriter getDebugLogStream(InputArgs argsObj) { - PrintWriter writer = null; - if (!argsObj.getOutputFolder().equals("")) { - String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(Calendar.getInstance().getTime()); - String filename = argsObj.getOutputFolder() + "DesigniteDebugLog" + timeStamp + ".txt"; - try { - writer = new PrintWriter(filename); - } catch (FileNotFoundException ex) { - Logger.log(ex.getMessage()); - } - } - return writer; - } + return writer; + } } diff --git a/src/Designite/SourceModel/Resolver.java b/src/Designite/SourceModel/Resolver.java index 8a6340c..06c7ff8 100644 --- a/src/Designite/SourceModel/Resolver.java +++ b/src/Designite/SourceModel/Resolver.java @@ -210,7 +210,7 @@ private void inferTypeInfo(SM_Project parentProject, TypeInfo typeInfo, Type typ // ProblemReferenceBinding and consequently to MissingTypeBinding if (iType == null) { inferPrimitiveType(parentProject, typeInfo, iType); - infereParametrized(parentProject, typeInfo, iType); + inferParametrized(parentProject, typeInfo, iType); } else if (iType.isRecovered()) { // Search in the ast explicitly and assign String unresolvedTypeName = typeOfVar.toString().replace("[]", ""); // cover the Array case @@ -220,7 +220,7 @@ private void inferTypeInfo(SM_Project parentProject, TypeInfo typeInfo, Type typ } } else { inferPrimitiveType(parentProject, typeInfo, iType); - infereParametrized(parentProject, typeInfo, iType); + inferParametrized(parentProject, typeInfo, iType); } } @@ -297,7 +297,7 @@ private void inferPrimitiveType(SM_Project parentProject, TypeInfo typeInfo, ITy } } - private void infereParametrized(SM_Project parentProject, TypeInfo typeInfo, ITypeBinding iType) { + private void inferParametrized(SM_Project parentProject, TypeInfo typeInfo, ITypeBinding iType) { if (iType != null && iType.isParameterizedType()) { typeInfo.setParametrizedType(true); addNonPrimitiveParameters(parentProject, typeInfo, iType); diff --git a/src/Designite/SourceModel/SM_Package.java b/src/Designite/SourceModel/SM_Package.java index eacfaa9..20a5298 100644 --- a/src/Designite/SourceModel/SM_Package.java +++ b/src/Designite/SourceModel/SM_Package.java @@ -10,7 +10,7 @@ import Designite.metrics.TypeMetricsExtractor; import org.eclipse.jdt.core.dom.CompilationUnit; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.metrics.TypeMetrics; import Designite.smells.designSmells.DesignSmellFacade; import Designite.smells.models.DesignCodeSmell; diff --git a/src/Designite/SourceModel/SM_Project.java b/src/Designite/SourceModel/SM_Project.java index 4739a03..4e752d9 100644 --- a/src/Designite/SourceModel/SM_Project.java +++ b/src/Designite/SourceModel/SM_Project.java @@ -1,23 +1,21 @@ package Designite.SourceModel; import java.io.File; -import java.io.IOException; import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; +import Designite.utils.DJLogger; +import Designite.utils.FileManager; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jface.text.Document; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.utils.CSVUtils; -import Designite.utils.Logger; import Designite.utils.models.Graph; public class SM_Project extends SM_SourceItem implements Parsable { @@ -40,6 +38,18 @@ public SM_Project(InputArgs argsObj) { setName(this.inputArgs.getProjectName()); } + public SM_Project() { + sourceFileList = new ArrayList(); + compilationUnitList = new ArrayList(); + packageList = new ArrayList(); + hierarchyGraph = new Graph(); + dependencyGraph = new Graph(); + setName(this.inputArgs.getProjectName()); + } + + + + public void setName(String name) { this.name = name; } @@ -68,7 +78,7 @@ public int getPackageCount() { // method used in tests public CompilationUnit createCU(String filePath) { - String fileToString = readFileToString(filePath); + String fileToString = FileManager.getInstance().readFileToString(filePath); int startingIndex = filePath.lastIndexOf(File.separatorChar); unitName = filePath.substring(startingIndex + 1); @@ -110,9 +120,9 @@ private void createPackageObjects() { private void checkNotNull(List list) { if (list == null) { - Logger.log("Application couldn't find any source code files in the specified path."); + DJLogger.log("Application couldn't find any source code files in the specified path."); System.exit(1); - Logger.log("Quitting.."); + DJLogger.log("Quitting.."); } } @@ -126,10 +136,9 @@ private SM_Package searchPackage(String packageName) { private void createCompilationUnits() { try { - getFileList(inputArgs.getSourceFolder()); - + sourceFileList = FileManager.getInstance().listFiles(inputArgs.getSourceFolder()); for (String file : sourceFileList) { - String fileToString = readFileToString(file); + String fileToString = FileManager.getInstance().readFileToString(file); int startingIndex = file.lastIndexOf(File.separatorChar); unitName = file.substring(startingIndex + 1); CompilationUnit unit = createAST(fileToString, unitName); @@ -174,32 +183,35 @@ private CompilationUnit createAST(final String content, String unitName) { return cu; } - private void getFileList(String path) { - File root = new File(path); - File[] list = root.listFiles(); - - if (list == null) - return; - for (File f : list) { - if (f.isDirectory()) { - getFileList(f.getAbsolutePath()); - } else { - - if (f.getName().endsWith(".java")) - sourceFileList.add(f.getAbsolutePath()); - } - } - return; - } - - private String readFileToString(String sourcePath) { - try { - return new String(Files.readAllBytes(Paths.get(sourcePath))); - } catch (IOException e) { - e.printStackTrace(); - return new String(); - } - } + // TODO : Duplicate code found in FileManager. + // VIOLATION: Single Responsibility +// private void getFileList(String path) { +// File root = new File(path); +// File[] list = root.listFiles(); +// +// if (list == null) +// return; +// for (File f : list) { +// if (f.isDirectory()) { +// getFileList(f.getAbsolutePath()); +// } else { +// +// if (f.getName().endsWith(".java")) +// sourceFileList.add(f.getAbsolutePath()); +// } +// } +// return; +// } + + // VIOLATION: Single Responsibility +// private String readFileToString(String sourcePath) { +// try { +// return new String(Files.readAllBytes(Paths.get(sourcePath))); +// } catch (IOException e) { +// e.printStackTrace(); +// return new String(); +// } +// } @Override public void printDebugLog(PrintWriter writer) { @@ -212,14 +224,14 @@ public void printDebugLog(PrintWriter writer) { @Override public void parse() { - Logger.log("Parsing the source code ..."); + DJLogger.log("Parsing the source code ..."); createCompilationUnits(); createPackageObjects(); parseAllPackages(); } public void resolve() { - Logger.log("Resolving symbols..."); + DJLogger.log("Resolving symbols..."); for (SM_Package pkg : packageList) { pkg.resolve(); } @@ -228,7 +240,7 @@ public void resolve() { } public void computeMetrics() { - Logger.log("Extracting metrics..."); + DJLogger.log("Extracting metrics..."); CSVUtils.initializeCSVDirectory(name, inputArgs.getOutputFolder()); for (SM_Package pkg : packageList) { pkg.extractTypeMetrics(); @@ -236,7 +248,7 @@ public void computeMetrics() { } public void detectCodeSmells() { - Logger.log("Extracting code smells..."); + DJLogger.log("Extracting code smells..."); for (SM_Package pkg : packageList) { pkg.extractCodeSmells(); } diff --git a/src/Designite/SourceModel/SM_Type.java b/src/Designite/SourceModel/SM_Type.java index e9a4ba6..f09af5b 100644 --- a/src/Designite/SourceModel/SM_Type.java +++ b/src/Designite/SourceModel/SM_Type.java @@ -15,7 +15,7 @@ import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.metrics.MethodMetrics; import Designite.smells.implementationSmells.ImplementationSmellDetector; import Designite.smells.models.ImplementationCodeSmell; diff --git a/src/Designite/SourceModel/TypeVisitor.java b/src/Designite/SourceModel/TypeVisitor.java index 9ccadf1..55a3d39 100644 --- a/src/Designite/SourceModel/TypeVisitor.java +++ b/src/Designite/SourceModel/TypeVisitor.java @@ -4,7 +4,7 @@ import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.TypeDeclaration; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import java.util.ArrayList; import java.util.List; diff --git a/src/Designite/smells/ThresholdsParser.java b/src/Designite/smells/ThresholdsParser.java index 1641d82..d282770 100644 --- a/src/Designite/smells/ThresholdsParser.java +++ b/src/Designite/smells/ThresholdsParser.java @@ -7,7 +7,7 @@ import java.io.IOException; import java.util.regex.Pattern; -import Designite.utils.Logger; +import Designite.utils.DJLogger; public class ThresholdsParser { @@ -32,7 +32,7 @@ public void parseThresholds() throws FileNotFoundException, IOException, Illegal private void checkFileExists() throws FileNotFoundException { if (!file.exists()) { String message = "constants.txt file not found in project folder."; - Logger.log(message); + DJLogger.log(message); throw new FileNotFoundException(message); } } @@ -45,7 +45,7 @@ private void parseLineByLine() throws IOException, IllegalArgumentException { if (isNotEmpty(line)) { if (!isWellFormatted(line)) { String message = "Line: " + line + "\nis not of the form 'someDescription' = 'someNumber'"; - Logger.log(message); + DJLogger.log(message); bufferedReader.close(); throw new IllegalArgumentException(message); } @@ -65,23 +65,27 @@ private boolean isWellFormatted(String line) { } private void setThresholdsStrategy(String key, Double value) throws IllegalArgumentException { - if (key.equals("deepHierarchy")) { - thresholds.setDeepHierarchy(value.intValue()); - } else if (key.equals("wideHierarchy")) { - thresholds.setWideHierarchy(value.intValue()); - } else if (key.equals("insufficientModularizationLargePublicInterface")) { - thresholds.setInsufficientModularizationLargePublicInterface(value.intValue()); - } else if (key.equals("insufficientModularizationLargeNumOfMethods")) { - thresholds.setInsufficientModularizationLargeNumOfMethods(value.intValue()); - } else if (key.equals("insufficientModularizationHighComplexity")) { - thresholds.setInsufficientModularizationHighComplexity(value.intValue()); - } else if (key.equals("wideHierarchy")) { - thresholds.setWideHierarchy(value.intValue()); - } else { - String message = "No such threshold: " + key; - Logger.log(message); - throw new IllegalArgumentException(message); - } + switch (key) { + case "deepHierarchy": + thresholds.setDeepHierarchy(value.intValue()); + break; + case "insufficientModularizationLargePublicInterface": + thresholds.setInsufficientModularizationLargePublicInterface(value.intValue()); + break; + case "insufficientModularizationLargeNumOfMethods": + thresholds.setInsufficientModularizationLargeNumOfMethods(value.intValue()); + break; + case "insufficientModularizationHighComplexity": + thresholds.setInsufficientModularizationHighComplexity(value.intValue()); + break; + case "wideHierarchy": + thresholds.setWideHierarchy(value.intValue()); + break; + default: + String message = "No such threshold: " + key; + DJLogger.log(message); + throw new IllegalArgumentException(message); + } } public ThresholdsDTO getThresholds() { diff --git a/src/Designite/smells/designSmells/CodeSmellDetector.java b/src/Designite/smells/designSmells/CodeSmellDetector.java new file mode 100644 index 0000000..d30a64d --- /dev/null +++ b/src/Designite/smells/designSmells/CodeSmellDetector.java @@ -0,0 +1,18 @@ +package Designite.smells.designSmells; + +import Designite.smells.models.CodeSmell; + +import java.util.ArrayList; +import java.util.List; + +public abstract class CodeSmellDetector { + + protected List smells = new ArrayList<>(); + + protected void addToSmells(T smell) { + smells.add(smell); + } + + public abstract T initializeCodeSmell(String smellName); + +} diff --git a/src/Designite/smells/designSmells/DesignSmellDetector.java b/src/Designite/smells/designSmells/DesignSmellDetector.java index c0f0f23..bbe44f4 100644 --- a/src/Designite/smells/designSmells/DesignSmellDetector.java +++ b/src/Designite/smells/designSmells/DesignSmellDetector.java @@ -1,56 +1,48 @@ package Designite.smells.designSmells; -import java.util.ArrayList; -import java.util.List; - import Designite.SourceModel.SourceItemInfo; import Designite.metrics.TypeMetrics; import Designite.smells.ThresholdsDTO; import Designite.smells.models.DesignCodeSmell; -public abstract class DesignSmellDetector { - - private List smells; - - private TypeMetrics typeMetrics; - private SourceItemInfo info; - private ThresholdsDTO thresholdsDTO; - - public DesignSmellDetector(TypeMetrics typeMetrics, SourceItemInfo info) { - this.typeMetrics = typeMetrics; - this.info = info; - - thresholdsDTO = new ThresholdsDTO(); - smells = new ArrayList<>(); - } - - abstract public List detectCodeSmells(); - - public List getSmells() { - return smells; - } - - public DesignCodeSmell initializeCodeSmell(String smellName) { - return new DesignCodeSmell(getSourceItemInfo().getProjectName() - , getSourceItemInfo().getPackageName() - , getSourceItemInfo().getTypeName() - , smellName); - } - - protected void addToSmells(DesignCodeSmell smell) { - smells.add(smell); - } - - protected TypeMetrics getTypeMetrics() { - return typeMetrics; - } - - protected SourceItemInfo getSourceItemInfo() { - return info; - } - - protected ThresholdsDTO getThresholdsDTO() { - return thresholdsDTO; - } +import java.util.ArrayList; +import java.util.List; + +public abstract class DesignSmellDetector extends CodeSmellDetector { + + + private final TypeMetrics typeMetrics; + private final SourceItemInfo info; + private final ThresholdsDTO thresholdsDTO; + + public DesignSmellDetector(TypeMetrics typeMetrics, SourceItemInfo info) { + this.typeMetrics = typeMetrics; + this.info = info; + + thresholdsDTO = new ThresholdsDTO(); + smells = new ArrayList<>(); + } + + abstract public List detectCodeSmells(); + + public List getSmells() { + return smells; + } + + public DesignCodeSmell initializeCodeSmell(String smellName) { + return new DesignCodeSmell(getSourceItemInfo().getProjectName(), getSourceItemInfo().getPackageName(), getSourceItemInfo().getTypeName(), smellName); + } + + protected TypeMetrics getTypeMetrics() { + return typeMetrics; + } + + protected SourceItemInfo getSourceItemInfo() { + return info; + } + + protected ThresholdsDTO getThresholdsDTO() { + return thresholdsDTO; + } } diff --git a/src/Designite/smells/implementationSmells/ImplementationSmellDetector.java b/src/Designite/smells/implementationSmells/ImplementationSmellDetector.java index b70b7ba..7f5abe0 100644 --- a/src/Designite/smells/implementationSmells/ImplementationSmellDetector.java +++ b/src/Designite/smells/implementationSmells/ImplementationSmellDetector.java @@ -1,331 +1,315 @@ package Designite.smells.implementationSmells; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.CatchClause; -import org.eclipse.jdt.core.dom.IfStatement; -import org.eclipse.jdt.core.dom.NumberLiteral; -import org.eclipse.jdt.core.dom.Statement; -import org.eclipse.jdt.core.dom.TryStatement; -import org.eclipse.jdt.core.dom.SwitchCase; -import org.eclipse.jdt.core.dom.SwitchStatement; - -import Designite.SourceModel.SM_Field; -import Designite.SourceModel.SM_LocalVar; -import Designite.SourceModel.SM_Method; -import Designite.SourceModel.SM_Parameter; -import Designite.SourceModel.SourceItemInfo; +import Designite.SourceModel.*; import Designite.metrics.MethodMetrics; import Designite.smells.ThresholdsDTO; +import Designite.smells.designSmells.CodeSmellDetector; import Designite.smells.models.ImplementationCodeSmell; -import Designite.utils.Logger; +import Designite.utils.DJLogger; import Designite.visitors.MethodControlFlowVisitor; import Designite.visitors.NumberLiteralVisitor; +import org.eclipse.jdt.core.dom.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +public class ImplementationSmellDetector extends CodeSmellDetector { + + + private final MethodMetrics methodMetrics; + private final SourceItemInfo info; + private final ThresholdsDTO thresholdsDTO; + + + private static final String ABST_FUNC_CALL_FRM_CTOR = "Abstract Function Call From Constructor"; + private static final String COMPLEX_CONDITIONAL = "Complex Conditional"; + private static final String COMPLEX_METHOD = "Complex Method"; + private static final String EMPTY_CATCH_CLAUSE = "Empty catch clause"; + private static final String LONG_IDENTIFIER = "Long Identifier"; + private static final String LONG_METHOD = "Long Method"; + private static final String LONG_PARAMETER_LIST = "Long Parameter List"; + private static final String LONG_STATEMENT = "Long Statement"; + private static final String MAGIC_NUMBER = "Magic Number"; + private static final String MISSING_DEFAULT = "Missing default"; + + private static final String AND_OPERATOR_REGEX = "\\&\\&"; + private static final String OR_OPERATOR_REGEX = "\\|\\|"; + private static final Pattern EMPTY_BODY_PATTERN = Pattern.compile("^\\{\\s*\\}\\s*$"); + + private static final int LONG_RADIX = 16; + + public ImplementationSmellDetector(MethodMetrics methodMetrics, SourceItemInfo info) { + this.methodMetrics = methodMetrics; + this.info = info; + + thresholdsDTO = new ThresholdsDTO(); + smells = new ArrayList<>(); + } + + public List detectCodeSmells() { + detectAbstractFunctionCallFromConstructor(); + detectComplexConditional(); + detectComplexMethod(); + detectEmptyCatchBlock(); + detectLongIdentifier(); + detectLongMethod(); + detectLongParameterList(); + detectLongStatement(); + detectMagicNumber(); + detectMissingDefault(); + return smells; + } + + public List detectAbstractFunctionCallFromConstructor() { + if (hasAbstractFunctionCallFromConstructor()) { + addToSmells(initializeCodeSmell(ABST_FUNC_CALL_FRM_CTOR)); + } + return smells; + } + + private boolean hasAbstractFunctionCallFromConstructor() { + SM_Method method = methodMetrics.getMethod(); + if (method.isConstructor()) { + for (SM_Method calledMethod : method.getCalledMethods()) { + if (calledMethod.isAbstract()) { + return true; + } + } + } + return false; + } + + public List detectComplexConditional() { + hasComplexConditional(); + return smells; + } + + private void hasComplexConditional() { + MethodControlFlowVisitor visitor = new MethodControlFlowVisitor(); + methodMetrics.getMethod().getMethodDeclaration().accept(visitor); + for (IfStatement ifStatement : visitor.getIfStatements()) { + if (numOfBooleanSubExpressions(ifStatement) >= thresholdsDTO.getComplexCondition()) { + addToSmells(initializeCodeSmell(COMPLEX_CONDITIONAL)); + } + } + } + + private String getBooleanRegex() { + return AND_OPERATOR_REGEX + "|" + OR_OPERATOR_REGEX; + } + + private int numOfBooleanSubExpressions(IfStatement ifStatement) { + return ifStatement.getExpression().toString().split(getBooleanRegex()).length; + } + + public List detectComplexMethod() { + if (hasComplexMethod()) { + addToSmells(initializeCodeSmell(COMPLEX_METHOD)); + } + return smells; + } + + private boolean hasComplexMethod() { + return methodMetrics.getCyclomaticComplexity() >= thresholdsDTO.getComplexMethod(); + } + + public List detectEmptyCatchBlock() { + MethodControlFlowVisitor visitor = new MethodControlFlowVisitor(); + methodMetrics.getMethod().getMethodDeclaration().accept(visitor); + for (TryStatement tryStatement : visitor.getTryStatements()) { + for (Object catchClause : tryStatement.catchClauses()) { + if (!hasBody((CatchClause) catchClause)) { + addToSmells(initializeCodeSmell(EMPTY_CATCH_CLAUSE)); + } + } + } + return smells; + } + + public boolean hasBody(CatchClause catchClause) { + String body = catchClause.getBody().toString(); + return !EMPTY_BODY_PATTERN.matcher(body).matches(); + } + + public List detectLongIdentifier() { + if (hasLongIdentifier()) { + addToSmells(initializeCodeSmell(LONG_IDENTIFIER)); + } + return smells; + } + + private boolean hasLongIdentifier() { + return hasLongParameter() || hasLongLocalVar() || hasLongFieldAccess(); + } + + private boolean hasLongParameter() { + for (SM_Parameter parameter : methodMetrics.getMethod().getParameterList()) { + if (parameter.getName().length() >= thresholdsDTO.getLongIdentifier()) { + return true; + } + } + return false; + } + + private boolean hasLongLocalVar() { + for (SM_LocalVar var : methodMetrics.getMethod().getLocalVarList()) { + if (var.getName().length() >= thresholdsDTO.getLongIdentifier()) { + return true; + } + } + return false; + } + + private boolean hasLongFieldAccess() { + for (SM_Field field : methodMetrics.getMethod().getDirectFieldAccesses()) { + if (field.getName().length() >= thresholdsDTO.getLongIdentifier()) { + return true; + } + } + return false; + } + + public List detectLongMethod() { + if (hasLongMethod()) { + addToSmells(initializeCodeSmell(LONG_METHOD)); + } + return smells; + } + + private boolean hasLongMethod() { + return methodMetrics.getNumOfLines() >= thresholdsDTO.getLongMethod(); + } + + public List detectLongParameterList() { + if (hasLongParameterList()) { + addToSmells(initializeCodeSmell(LONG_PARAMETER_LIST)); + } + return smells; + } + + private boolean hasLongParameterList() { + return methodMetrics.getNumOfParameters() >= thresholdsDTO.getLongParameterList(); + } + + public List detectLongStatement() { + SM_Method currentMethod = methodMetrics.getMethod(); + if (currentMethod.hasBody()) { + String methodBody = currentMethod.getMethodBody(); + hasLongStatement(methodBody); + } + + return smells; + } + + private void hasLongStatement(String methodBody) { + //FIXME is there another non-hard-coded to replace the "\n" + String[] methodStatements = methodBody.split("\n"); + + for (String singleMethodStatement : methodStatements) { + singleMethodStatement = singleMethodStatement.trim().replaceAll("\\s+", " "); + if (isLongStatement(singleMethodStatement)) { + addToSmells(initializeCodeSmell(LONG_STATEMENT)); + } + } + } + + private boolean isLongStatement(String statement) { + return statement.length() > this.thresholdsDTO.getLongStatement(); + } + + public List detectMagicNumber() { + hasMagicNumbers(); + return smells; + } + + private void hasMagicNumbers() { + NumberLiteralVisitor visitor = new NumberLiteralVisitor(); + methodMetrics.getMethod().getMethodDeclaration().accept(visitor); + List literals = visitor.getNumberLiteralsExpressions(); + + if (literals.size() < 1) { + return; + } + + for (NumberLiteral singleNumberLiteral : literals) { + if (isLiteralValid(singleNumberLiteral)) { + addToSmells(initializeCodeSmell(MAGIC_NUMBER)); + } + } + } + + private boolean isLiteralValid(NumberLiteral singleNumberLiteral) { + boolean isValid = isNotZeroOrOne(singleNumberLiteral) && isNotArrayInitialization(singleNumberLiteral); + return isValid; + } + + // 0s and 1s are not considered as Magic Numbers + private boolean isNotZeroOrOne(NumberLiteral singleNumberLiteral) { + String numberToString = singleNumberLiteral.toString().toLowerCase().replaceAll("_", ""); + double literalValue; + try { + // hex case + if (numberToString.startsWith("0x")) { + literalValue = (double) (Long.parseLong(numberToString.replaceAll("0x", "").replaceAll("l", ""), LONG_RADIX)); + // long case + } else if (numberToString.endsWith("l")) { + literalValue = (double) (Long.parseLong(numberToString.replaceAll("l", ""))); + // float case + } else if (numberToString.endsWith("f")) { + literalValue = Float.parseFloat(numberToString.replaceAll("f", "")); + } + // double case + else { + literalValue = Double.parseDouble(numberToString); + } + } catch (NumberFormatException ex) { + String logMessage = "Exception while parsing literal number (during Magic Number detection). " + ex.getMessage(); + DJLogger.log(logMessage); + literalValue = 0.0; + } + return literalValue != 0.0 && literalValue != 1.0; + } + + + // Literals in array initializations (such as int[] arr = {0,1};) are not considered as Magic Numbers + private boolean isNotArrayInitialization(NumberLiteral singleNumberLiteral) { + return singleNumberLiteral.getParent().getNodeType() != ASTNode.ARRAY_INITIALIZER; + } + + public List detectMissingDefault() { + hasMissingDefaults(); + return smells; + } + + private void hasMissingDefaults() { + MethodControlFlowVisitor visitor = new MethodControlFlowVisitor(); + methodMetrics.getMethod().getMethodDeclaration().accept(visitor); + List switchStatements = visitor.getSwitchStatements(); + for (SwitchStatement singleSwitchStatement : switchStatements) { + if (switchIsMissingDefault(singleSwitchStatement)) { + addToSmells(initializeCodeSmell(MISSING_DEFAULT)); + } + } + } + + private boolean switchIsMissingDefault(SwitchStatement switchStatement) { + List statetmentsOfSwitch = switchStatement.statements(); + for (Statement stm : statetmentsOfSwitch) { + if ((stm instanceof SwitchCase) && ((SwitchCase) stm).isDefault()) { + return false; + } + } + return true; + } + + public List getSmells() { + return smells; + } -public class ImplementationSmellDetector { - - private List smells; - - private MethodMetrics methodMetrics; - private SourceItemInfo info; - private ThresholdsDTO thresholdsDTO; - - private static final String ABST_FUNC_CALL_FRM_CTOR = "Abstract Function Call From Constructor"; - private static final String COMPLEX_CONDITIONAL = "Complex Conditional"; - private static final String COMPLEX_METHOD = "Complex Method"; - private static final String EMPTY_CATCH_CLAUSE = "Empty catch clause"; - private static final String LONG_IDENTIFIER = "Long Identifier"; - private static final String LONG_METHOD = "Long Method"; - private static final String LONG_PARAMETER_LIST = "Long Parameter List"; - private static final String LONG_STATEMENT = "Long Statement"; - private static final String MAGIC_NUMBER = "Magic Number"; - private static final String MISSING_DEFAULT = "Missing default"; - - private static final String AND_OPERATOR_REGEX = "\\&\\&"; - private static final String OR_OPERATOR_REGEX = "\\|\\|"; - private static final Pattern EMPTY_BODY_PATTERN = Pattern.compile("^\\{\\s*\\}\\s*$"); - - public ImplementationSmellDetector(MethodMetrics methodMetrics, SourceItemInfo info) { - this.methodMetrics = methodMetrics; - this.info = info; - - thresholdsDTO = new ThresholdsDTO(); - smells = new ArrayList<>(); - } - - public List detectCodeSmells() { - detectAbstractFunctionCallFromConstructor(); - detectComplexConditional(); - detectComplexMethod(); - detectEmptyCatchBlock(); - detectLongIdentifier(); - detectLongMethod(); - detectLongParameterList(); - detectLongStatement(); - detectMagicNumber(); - detectMissingDefault(); - return smells; - } - - public List detectAbstractFunctionCallFromConstructor() { - if (hasAbstractFunctionCallFromConstructor()) { - addToSmells(initializeCodeSmell(ABST_FUNC_CALL_FRM_CTOR)); - } - return smells; - } - - private boolean hasAbstractFunctionCallFromConstructor() { - SM_Method method = methodMetrics.getMethod(); - if (method.isConstructor()) { - for (SM_Method calledMethod : method.getCalledMethods()) { - if (calledMethod.isAbstract()) { - return true; - } - } - } - return false; - } - - public List detectComplexConditional() { - hasComplexConditional(); - return smells; - } - - private void hasComplexConditional() { - MethodControlFlowVisitor visitor = new MethodControlFlowVisitor(); - methodMetrics.getMethod().getMethodDeclaration().accept(visitor); - for (IfStatement ifStatement : visitor.getIfStatements()) { - if (numOfBooleanSubExpressions(ifStatement) >= thresholdsDTO.getComplexCondition()) { - addToSmells(initializeCodeSmell(COMPLEX_CONDITIONAL)); - } - } - } - - private String getBooleaRegex() { - return AND_OPERATOR_REGEX + "|" + OR_OPERATOR_REGEX; - } - - private int numOfBooleanSubExpressions(IfStatement ifStatement) { - return ifStatement.getExpression().toString().split(getBooleaRegex()).length; - } - - public List detectComplexMethod() { - if (hasComplexMethod()) { - addToSmells(initializeCodeSmell(COMPLEX_METHOD)); - } - return smells; - } - - private boolean hasComplexMethod() { - return methodMetrics.getCyclomaticComplexity() >= thresholdsDTO.getComplexMethod(); - } - - public List detectEmptyCatchBlock() { - MethodControlFlowVisitor visitor = new MethodControlFlowVisitor(); - methodMetrics.getMethod().getMethodDeclaration().accept(visitor); - for (TryStatement tryStatement : visitor.getTryStatements()) { - for (Object catchClause : tryStatement.catchClauses()) { - if (!hasBody((CatchClause) catchClause)) { - addToSmells(initializeCodeSmell(EMPTY_CATCH_CLAUSE)); - } - } - } - return smells; - } - - public boolean hasBody(CatchClause catchClause) { - String body = catchClause.getBody().toString(); - return !EMPTY_BODY_PATTERN.matcher(body).matches(); - } - - public List detectLongIdentifier() { - if (hasLongIdentifier()) { - addToSmells(initializeCodeSmell(LONG_IDENTIFIER)); - } - return smells; - } - - private boolean hasLongIdentifier() { - return hasLongParameter() || hasLongLocalVar() || hasLongFieldAccess(); - } - - private boolean hasLongParameter() { - for (SM_Parameter parameter : methodMetrics.getMethod().getParameterList()) { - if (parameter.getName().length() >= thresholdsDTO.getLongIdentifier()) { - return true; - } - } - return false; - } - - private boolean hasLongLocalVar() { - for (SM_LocalVar var : methodMetrics.getMethod().getLocalVarList()) { - if (var.getName().length() >= thresholdsDTO.getLongIdentifier()) { - return true; - } - } - return false; - } - - private boolean hasLongFieldAccess() { - for (SM_Field field : methodMetrics.getMethod().getDirectFieldAccesses()) { - if (field.getName().length() >= thresholdsDTO.getLongIdentifier()) { - return true; - } - } - return false; - } - - public List detectLongMethod() { - if (hasLongMethod()) { - addToSmells(initializeCodeSmell(LONG_METHOD)); - } - return smells; - } - - private boolean hasLongMethod() { - return methodMetrics.getNumOfLines() >= thresholdsDTO.getLongMethod(); - } - - public List detectLongParameterList() { - if (hasLongParameterList()) { - addToSmells(initializeCodeSmell(LONG_PARAMETER_LIST)); - } - return smells; - } - - private boolean hasLongParameterList() { - return methodMetrics.getNumOfParameters() >= thresholdsDTO.getLongParameterList(); - } - - public List detectLongStatement() { - SM_Method currentMethod = methodMetrics.getMethod(); - if(currentMethod.hasBody()) { - String methodBody = currentMethod.getMethodBody(); - hasLongStatement(methodBody); - } - - return smells; - } - - private void hasLongStatement(String methodBody) { - //FIXME is there another non-hard-coded to replace the "\n" - String[] methodStatements = methodBody.split("\n"); - - for(String singleMethodStatement : methodStatements) { - singleMethodStatement = singleMethodStatement.trim().replaceAll("\\s+", " "); - if(isLongStatement(singleMethodStatement)) { - addToSmells(initializeCodeSmell(LONG_STATEMENT)); - } - } - } - - private boolean isLongStatement(String statement) { - return statement.length() > this.thresholdsDTO.getLongStatement(); - } - - public List detectMagicNumber() { - hasMagicNumbers(); - return smells; - } - - private void hasMagicNumbers() { - NumberLiteralVisitor visitor = new NumberLiteralVisitor(); - methodMetrics.getMethod().getMethodDeclaration().accept(visitor); - List literals = visitor.getNumberLiteralsExpressions(); - - if( literals.size() < 1 ) { - return; - } - - for(NumberLiteral singleNumberLiteral : literals) { - if( isLiteralValid(singleNumberLiteral) ) { - addToSmells(initializeCodeSmell(MAGIC_NUMBER)); - } - } - } - - private boolean isLiteralValid(NumberLiteral singleNumberLiteral) { - boolean isValid = isNotZeroOrOne(singleNumberLiteral) && isNotArrayInitialization(singleNumberLiteral); - return isValid; - } - - // 0s and 1s are not considered as Magic Numbers - private boolean isNotZeroOrOne(NumberLiteral singleNumberLiteral) { - String numberToString = singleNumberLiteral.toString().toLowerCase().replaceAll("_", ""); - double literalValue = 0.0; - try { - // hex case - if(numberToString.startsWith("0x")) { - literalValue = (double)(Long.parseLong(numberToString.replaceAll("0x", "").replaceAll("l", ""),16)); - // long case - } else if(numberToString.endsWith("l")) { - literalValue = (double)(Long.parseLong(numberToString.replaceAll("l", ""))); - // float case - } else if(numberToString.endsWith("f")) { - literalValue = Float.parseFloat(numberToString.replaceAll("f", "")); - } - // double case - else { - literalValue = Double.parseDouble(numberToString); - } - } catch (NumberFormatException ex) { - String logMessage = "Exception while parsing literal number (during Magic Number detection). " + ex.getMessage(); - Logger.log(logMessage); - literalValue = 0.0; - } - return literalValue != 0.0 && literalValue != 1.0; - } - - - // Literals in array initializations (such as int[] arr = {0,1};) are not considered as Magic Numbers - private boolean isNotArrayInitialization(NumberLiteral singleNumberLiteral) { - return singleNumberLiteral.getParent().getNodeType() != ASTNode.ARRAY_INITIALIZER; - } - - public List detectMissingDefault() { - hasMissingDefaults(); - return smells; - } - - private void hasMissingDefaults() { - MethodControlFlowVisitor visitor = new MethodControlFlowVisitor(); - methodMetrics.getMethod().getMethodDeclaration().accept(visitor); - List switchStatements = visitor.getSwitchStatements(); - for(SwitchStatement singleSwitchStatement : switchStatements) { - if(switchIsMissingDefault(singleSwitchStatement)) { - addToSmells(initializeCodeSmell(MISSING_DEFAULT)); - } - } - } - - private boolean switchIsMissingDefault(SwitchStatement switchStatement) { - List statetmentsOfSwitch = switchStatement.statements(); - for(Statement stm : statetmentsOfSwitch) { - if ((stm instanceof SwitchCase) && ((SwitchCase)stm).isDefault()) { - return false; - } - } - return true; - } - - public List getSmells() { - return smells; - } - - private ImplementationCodeSmell initializeCodeSmell(String smellName) { - return new ImplementationCodeSmell(info.getProjectName() - , info.getPackageName() - , info.getTypeName() - , info.getMethodName() - , smellName); - } - - private void addToSmells(ImplementationCodeSmell smell) { - smells.add(smell); - } + @Override + public ImplementationCodeSmell initializeCodeSmell(String smellName) { + return new ImplementationCodeSmell(info.getProjectName(), info.getPackageName(), info.getTypeName(), info.getMethodName(), smellName); + } } diff --git a/src/Designite/utils/CSVUtils.java b/src/Designite/utils/CSVUtils.java index 40be67b..b510852 100644 --- a/src/Designite/utils/CSVUtils.java +++ b/src/Designite/utils/CSVUtils.java @@ -26,7 +26,7 @@ private static void createDirIfNotExists(File dir) { System.out.print("oops, couldn't create the directory " + dir); } catch (Exception e) { e.printStackTrace(); - Logger.log(e.getMessage()); + DJLogger.log(e.getMessage()); } } } @@ -56,7 +56,7 @@ private static void createCSVFile(String path, String header) { bufferedWriter.close(); } catch(IOException e) { e.printStackTrace(); - Logger.log(e.getMessage()); + DJLogger.log(e.getMessage()); } } @@ -69,7 +69,7 @@ public static void addToCSVFile(String path, String row) { bufferedWriter.close(); } catch(IOException e) { e.printStackTrace(); - Logger.log(e.getMessage()); + DJLogger.log(e.getMessage()); } } @@ -85,7 +85,7 @@ public static void addAllToCSVFile(String path, List collection) { bufferedWriter.close(); } catch(IOException e) { e.printStackTrace(); - Logger.log(e.getMessage()); + DJLogger.log(e.getMessage()); } } diff --git a/src/Designite/utils/Constants.java b/src/Designite/utils/Constants.java index d338f7b..e6352a6 100644 --- a/src/Designite/utils/Constants.java +++ b/src/Designite/utils/Constants.java @@ -54,5 +54,5 @@ public class Constants { + ",Method Name" + ",Code Smell" + "\n"; - public static final boolean DEBUG = false; + public static final boolean DEBUG = true; } diff --git a/src/Designite/utils/DJLogger.java b/src/Designite/utils/DJLogger.java new file mode 100644 index 0000000..596b5a6 --- /dev/null +++ b/src/Designite/utils/DJLogger.java @@ -0,0 +1,59 @@ +package Designite.utils; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Calendar; + + +public class DJLogger { + + // This variable must be private, making it public may expose sensitive information + private String logFile; + + private final static DJLogger DJ_LOGGER = getInstance(); + + public static DJLogger getInstance() { + if (DJ_LOGGER == null) { + return new DJLogger(); + } + return DJ_LOGGER; + } + + /** + * Ensure to set {@code outputDirectory} before program execution, + * as it creates log files in the specified + * directory. Failing to provide a valid directory may result in errors. + * @param outputDirectory + * @throws FileNotFoundException + */ + public void setOutputDirectory(String outputDirectory) throws FileNotFoundException { + String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(Calendar.getInstance().getTime()); + String logAbsolutePath = outputDirectory + File.separator + "DesigniteLog" + timeStamp + ".txt"; + FileManager.getInstance().createFileIfNotExists(logAbsolutePath); + this.logFile = logAbsolutePath; + } + + private DJLogger() { + } + + /** + * Logs the {@code logMessage} into the file. + * @param logMessage + */ + public static void log(String logMessage) { + System.out.println(logMessage); + if (DJ_LOGGER.logFile == null) { + //Commented the following line just to make the execution non-verbose + //System.out.println("Log file path has been not set. Logging not support."); + return; + } + try (Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(DJ_LOGGER.logFile, true), StandardCharsets.UTF_8))) { + writer.write(logMessage + "\n"); + // Redundant close -> try-catch closes the file after execution. + // writer.close(); + } catch (IOException ex) { + System.out.println("Exception during logging. " + ex.getMessage()); + } + } +} diff --git a/src/Designite/utils/FileManager.java b/src/Designite/utils/FileManager.java new file mode 100644 index 0000000..8f5d717 --- /dev/null +++ b/src/Designite/utils/FileManager.java @@ -0,0 +1,125 @@ +package Designite.utils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public class FileManager { + + private static FileManager fileManager; + + /** + * The Singleton Design principle has been applied to manage one object only throughout the program execution. + * @return {@code Instance of} {@link FileManager} + */ + public static FileManager getInstance() { + if (fileManager == null) { + fileManager = new FileManager(); + } + return fileManager; + } + + private FileManager() { + } + + public FileManager(String sourcePath) { + listFiles(sourcePath); + } + + /** + * Returns list of all files from a nested folder, recursively traversing from + * the deepest level of folder tree. + * @param sourcePath + * @return + */ + public List listFiles(String sourcePath) { + List sourceFileList = new ArrayList(); + this.listFiles(sourcePath, sourceFileList); + return sourceFileList; + } + + /** + * Returns list of all files from a nested folder, recursively traversing tree from + * the deepest level of folder tree. + * @param sourcePath + * @param sourceFileList + * @return List + */ + private List listFiles(String sourcePath, List sourceFileList) { + File file; + try { + file = new File(sourcePath); + if (file.isFile() && file.getName().endsWith(".java")) { + sourceFileList.add(file.getAbsolutePath()); + } else if (file.isDirectory()) { + sourceFileList.addAll(listFilesFromFolder(file.getAbsolutePath())); + } else { + // help menu + // This block is a dead code. Moreover, It does not provide a resolution if the files are not found. + System.out.println("No file found to be analyzed."); + System.out.println("Usage instructions: "); + } + } catch (Exception e) { + e.printStackTrace(); + } + return sourceFileList; + } + + /** + * Returns List of files in the specified {@code folderPath} + * @param folderPath + * @return List + */ + public List listFilesFromFolder(String folderPath) { + File file = new File(folderPath); + File[] paths; + List fileList = new ArrayList(); + paths = file.listFiles(); + for (File path : paths) { + if (path.isFile() && path.getAbsolutePath().endsWith(".java")) { + fileList.add(path.getAbsolutePath()); + } else if (path.isDirectory()) { + fileList.addAll(listFilesFromFolder(path.getAbsolutePath())); + } + } + return fileList; + } + + + /** + * Reads all lines from the file specified in {@code sourcePath}. + * @param sourcePath + * @return + */ + public String readFileToString(String sourcePath) { + try { + return new String(Files.readAllBytes(Paths.get(sourcePath))); + } catch (IOException e) { + e.printStackTrace(); + return ""; + } + } + + + /** + * Creates a new file at the specified path if it doesn't exist. + * @param filePath + * @return + * @throws FileNotFoundException + */ + public PrintWriter createFileIfNotExists(String filePath) throws FileNotFoundException { + File file = new File(filePath); + File parent = file.getParentFile(); + if (parent.isDirectory() && !parent.exists()) { + parent.mkdirs(); + } + return new PrintWriter(file); + } + + +} diff --git a/src/Designite/utils/FileReader.java b/src/Designite/utils/FileReader.java deleted file mode 100644 index 12c36a2..0000000 --- a/src/Designite/utils/FileReader.java +++ /dev/null @@ -1,53 +0,0 @@ -package Designite.utils; - -import java.io.File; -import java.util.ArrayList; - -public class FileReader { - private ArrayList pathList = new ArrayList(); - public FileReader(String sourcePath) { - listFiles(sourcePath); - } - - public ArrayList getPathList() { - return pathList; - } - - // keeping all java files from the given path in a list - public void listFiles(String sourcePath) { - File f = null; - - try { - f = new File(sourcePath); - - if (f.isFile() && f.getAbsolutePath().endsWith(".java")) { - pathList.add(f.getAbsolutePath()); - } else if (f.isDirectory()) { - getFilesFromFolder(f.getAbsolutePath()); - } else { - // help menu - System.out.println("No file found to be analyzed."); - System.out.println("Usage instructions: "); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - // adding java files of a folder in the List - public void getFilesFromFolder(String folderPath) { - File f = new File(folderPath); - File[] paths; - - paths = f.listFiles(); - - for (File path : paths) { - if (path.isFile() && path.getAbsolutePath().endsWith(".java")) { - pathList.add(path.getAbsolutePath()); - } else if (path.isDirectory()) { - getFilesFromFolder(path.getAbsolutePath()); - } - } - } -} diff --git a/src/Designite/utils/Logger.java b/src/Designite/utils/Logger.java deleted file mode 100644 index 86a7d4e..0000000 --- a/src/Designite/utils/Logger.java +++ /dev/null @@ -1,29 +0,0 @@ -package Designite.utils; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.nio.charset.StandardCharsets; - -public class Logger { - public static String logFile = null; - - public static void log(String str) { - System.out.println(str); - if (logFile == null) { - //Commented the following line just to make the execution non-verbose - //System.out.println("Log file path has been not set. Logging not support."); - return; - } - try (Writer writer = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(logFile, true), StandardCharsets.UTF_8))) { - writer.write(str + "\n"); - writer.close(); - } catch (IOException ex) { - System.out.println("Exception during logging. " + ex.getMessage()); - } - } -} diff --git a/tests/DesigniteTests/DesigniteTests/DesigniteTests.java b/tests/DesigniteTests/DesigniteTests/DesigniteTests.java index 2a3caa4..3f7b526 100644 --- a/tests/DesigniteTests/DesigniteTests/DesigniteTests.java +++ b/tests/DesigniteTests/DesigniteTests/DesigniteTests.java @@ -6,62 +6,42 @@ import java.io.IOException; public abstract class DesigniteTests { - - protected static final String PARAMETER_TEST_INPUT_FILE_PATH = getTestingPath() + File.separator + "parameterTestInput.txt"; - protected static final String CALLED_METHOD_TEST_INPUT_FILE_PATH = getTestingPath() + File.separator + "calledMethodTestInput.txt"; - protected static final String TEST_BATCH_FILE_PATH = getTestingPath() + File.separator + "testBatchFile.txt"; - protected static final String IN_BATCH_FILE_PATH = getTestingPath() + File.separator + "inBatchFile.txt"; - protected static final String METRICS_FILE_PATH = getTestingPath() + File.separator + "metricsFile.txt"; - protected static final String CODE_SMELLS_FILE_PATH = getTestingPath() + File.separator + "codeSmellsFile.txt"; - - protected static final String PARAMETER_TEST_INPUT_FILE_CONTENT = "[Source folder]\n" - + getTestingPath() + File.separator + "test_inputs"; - protected static final String CALLED_METHOD_TEST_INPUT_FILE_CONTENT = "[Source folder]\n" - + getTestingPath() + File.separator + "test_inputs2"; - protected static final String TEST_BATCH_FILE_CONTENT = "[Source folder]\n" - + getTestingPath() + File.separator + "test_package"; - protected static final String IN_BATCH_FILE_CONTENT = "[Source folder]\n" - + System.getProperty("user.dir") + "\n\n" - + "[Output folder]\n" - + System.getProperty("user.dir") + File.separator + "temp"; - protected static final String IN_BATCH_FILE_CONTENT_SRC = "[Source folder]\n" - + System.getProperty("user.dir") - + File.separator + "src" + "\n\n" - + "[Output folder]\n" - + System.getProperty("user.dir") + File.separator + "temp"; - protected static final String IN_BATCH_FILE_CONTENT_SOURCE = "[Source folder]\n" - + System.getProperty("user.dir") - + File.separator + "source" + "\n\n" - + "[Output folder]\n" - + System.getProperty("user.dir") + File.separator + "temp"; - protected static final String METRICS_FILE_CONTENT = "[Source folder]\n" - + getTestingPath() + File.separator + "metrics" + "\n\n" - + "[Output folder]\n" - + System.getProperty("user.dir") + File.separator + "temp"; - protected static final String CODE_SMELLS_FILE_CONTENT = "[Source folder]\n" - + getTestingPath() + File.separator + "codeSmells"; - - protected static String getTestingPath() { - String path = System.getProperty("user.dir") + - File.separator + "tests" + - File.separator + "TestFiles"; - - return path; - } - - protected void createFileForArguments(String path, String content) { - try { - File file = new File(path); - if (!file.exists()) { - file.createNewFile(); - } - FileWriter fileWriter = new FileWriter(path, false); - BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); - bufferedWriter.write(content); - bufferedWriter.close(); - } catch(IOException e) { - e.printStackTrace(); - } - } + + protected static final String PARAMETER_TEST_INPUT_FILE_PATH = getTestingPath() + File.separator + "parameterTestInput.txt"; + protected static final String CALLED_METHOD_TEST_INPUT_FILE_PATH = getTestingPath() + File.separator + "calledMethodTestInput.txt"; + protected static final String TEST_BATCH_FILE_PATH = getTestingPath() + File.separator + "testBatchFile.txt"; + protected static final String IN_BATCH_FILE_PATH = getTestingPath() + File.separator + "inBatchFile.txt"; + protected static final String METRICS_FILE_PATH = getTestingPath() + File.separator + "metricsFile.txt"; + protected static final String CODE_SMELLS_FILE_PATH = getTestingPath() + File.separator + "codeSmellsFile.txt"; + + protected static final String PARAMETER_TEST_INPUT_FILE_CONTENT = "[Source folder]\n" + getTestingPath() + File.separator + "test_inputs"; + protected static final String CALLED_METHOD_TEST_INPUT_FILE_CONTENT = "[Source folder]\n" + getTestingPath() + File.separator + "test_inputs2"; + protected static final String TEST_BATCH_FILE_CONTENT = "[Source folder]\n" + getTestingPath() + File.separator + "test_package"; + protected static final String IN_BATCH_FILE_CONTENT = "[Source folder]\n" + System.getProperty("user.dir") + "\n\n" + "[Output folder]\n" + System.getProperty("user.dir") + File.separator + "temp"; + protected static final String IN_BATCH_FILE_CONTENT_SRC = "[Source folder]\n" + System.getProperty("user.dir") + File.separator + "src" + "\n\n" + "[Output folder]\n" + System.getProperty("user.dir") + File.separator + "temp"; + protected static final String IN_BATCH_FILE_CONTENT_SOURCE = "[Source folder]\n" + System.getProperty("user.dir") + File.separator + "source" + "\n\n" + "[Output folder]\n" + System.getProperty("user.dir") + File.separator + "temp"; + protected static final String METRICS_FILE_CONTENT = "[Source folder]\n" + getTestingPath() + File.separator + "metrics" + "\n\n" + "[Output folder]\n" + System.getProperty("user.dir") + File.separator + "temp"; + protected static final String CODE_SMELLS_FILE_CONTENT = "[Source folder]\n" + getTestingPath() + File.separator + "codeSmells"; + + protected static String getTestingPath() { + String path = System.getProperty("user.dir") + File.separator + "tests" + File.separator + "TestFiles"; + + return path; + } + + protected void createFileForArguments(String path, String content) { + try { + File file = new File(path); + if (!file.exists()) { + file.createNewFile(); + } + FileWriter fileWriter = new FileWriter(path, false); + BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); + bufferedWriter.write(content); + bufferedWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/tests/DesigniteTests/DesigniteTests/InputArgsTest.java b/tests/DesigniteTests/DesigniteTests/InputArgsTest.java index 34e5b20..3dda2bb 100644 --- a/tests/DesigniteTests/DesigniteTests/InputArgsTest.java +++ b/tests/DesigniteTests/DesigniteTests/InputArgsTest.java @@ -6,7 +6,7 @@ import org.junit.Test; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; public class InputArgsTest extends DesigniteTests { @@ -39,4 +39,4 @@ public void testInputArgs_getProjectName_src() { String currentProjectDir = new File(System.getProperty("user.dir")).getName(); assertEquals(currentProjectDir, args.getProjectName()); } -} \ No newline at end of file +} diff --git a/tests/DesigniteTests/DesigniteTests/SM_FieldTest.java b/tests/DesigniteTests/DesigniteTests/SM_FieldTest.java index 84d0f2f..27fc5da 100644 --- a/tests/DesigniteTests/DesigniteTests/SM_FieldTest.java +++ b/tests/DesigniteTests/DesigniteTests/SM_FieldTest.java @@ -9,7 +9,7 @@ import org.junit.Before; import org.junit.Test; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.SourceModel.AccessStates; import Designite.SourceModel.SM_Field; import Designite.SourceModel.SM_Package; diff --git a/tests/DesigniteTests/DesigniteTests/SM_LocalVarTests.java b/tests/DesigniteTests/DesigniteTests/SM_LocalVarTests.java index e26fce9..b42287f 100644 --- a/tests/DesigniteTests/DesigniteTests/SM_LocalVarTests.java +++ b/tests/DesigniteTests/DesigniteTests/SM_LocalVarTests.java @@ -9,7 +9,7 @@ import org.junit.Before; import org.junit.Test; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.SourceModel.SM_LocalVar; import Designite.SourceModel.SM_Method; import Designite.SourceModel.SM_Project; diff --git a/tests/DesigniteTests/DesigniteTests/SM_MethodTest.java b/tests/DesigniteTests/DesigniteTests/SM_MethodTest.java index 6df869d..d4419ad 100644 --- a/tests/DesigniteTests/DesigniteTests/SM_MethodTest.java +++ b/tests/DesigniteTests/DesigniteTests/SM_MethodTest.java @@ -10,7 +10,7 @@ import org.junit.Before; import org.junit.Test; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.SourceModel.AccessStates; import Designite.SourceModel.SM_Method; import Designite.SourceModel.SM_Package; diff --git a/tests/DesigniteTests/DesigniteTests/SM_Method_CalledMethodsTests.java b/tests/DesigniteTests/DesigniteTests/SM_Method_CalledMethodsTests.java index 224640d..f9c533a 100644 --- a/tests/DesigniteTests/DesigniteTests/SM_Method_CalledMethodsTests.java +++ b/tests/DesigniteTests/DesigniteTests/SM_Method_CalledMethodsTests.java @@ -8,7 +8,7 @@ import org.junit.Before; import org.junit.Test; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.SourceModel.SM_Method; import Designite.SourceModel.SM_Package; import Designite.SourceModel.SM_Project; diff --git a/tests/DesigniteTests/DesigniteTests/SM_PackageTest.java b/tests/DesigniteTests/DesigniteTests/SM_PackageTest.java index 3bcdec7..e233339 100644 --- a/tests/DesigniteTests/DesigniteTests/SM_PackageTest.java +++ b/tests/DesigniteTests/DesigniteTests/SM_PackageTest.java @@ -9,7 +9,7 @@ import org.junit.Before; import org.junit.Test; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.SourceModel.SM_Package; import Designite.SourceModel.SM_Project; @@ -28,7 +28,7 @@ public void SM_Package_positive_case() { for (SM_Package pkg : pkgList) { if (pkg.getName().equals("Designite")) - assertEquals(pkg.getTypeList().size(), 2); + assertEquals(pkg.getTypeList().size(), 1); if (pkg.getName().equals("Designite.SourceModel")) //Added additional class in the package. assertEquals(21, pkg.getTypeList().size()); diff --git a/tests/DesigniteTests/DesigniteTests/SM_ParameterTest.java b/tests/DesigniteTests/DesigniteTests/SM_ParameterTest.java index 69cc16e..473c8d2 100644 --- a/tests/DesigniteTests/DesigniteTests/SM_ParameterTest.java +++ b/tests/DesigniteTests/DesigniteTests/SM_ParameterTest.java @@ -8,7 +8,7 @@ import org.junit.Before; import org.junit.Test; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.SourceModel.SM_Method; import Designite.SourceModel.SM_Parameter; import Designite.SourceModel.SM_Project; diff --git a/tests/DesigniteTests/DesigniteTests/SM_ProjectTest.java b/tests/DesigniteTests/DesigniteTests/SM_ProjectTest.java index d504231..f16526f 100644 --- a/tests/DesigniteTests/DesigniteTests/SM_ProjectTest.java +++ b/tests/DesigniteTests/DesigniteTests/SM_ProjectTest.java @@ -3,12 +3,10 @@ import static org.junit.Assert.*; import java.io.File; -import java.util.Iterator; import org.junit.Test; -import Designite.InputArgs; -import Designite.SourceModel.SM_Package; +import Designite.ArgumentParser.InputArgs; import Designite.SourceModel.SM_Project; public class SM_ProjectTest extends DesigniteTests { @@ -20,7 +18,7 @@ public void testSM_Project_positive_case() { /*for (SM_Package pkg : project.getPackageList()) System.out.println(pkg.getName());*/ - assertEquals(21, project.getPackageCount()); + assertEquals(22, project.getPackageCount()); } diff --git a/tests/DesigniteTests/DesigniteTests/SM_TypeTest.java b/tests/DesigniteTests/DesigniteTests/SM_TypeTest.java index 25c7f15..3ed085b 100644 --- a/tests/DesigniteTests/DesigniteTests/SM_TypeTest.java +++ b/tests/DesigniteTests/DesigniteTests/SM_TypeTest.java @@ -11,7 +11,7 @@ import org.junit.Before; import org.junit.Test; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.SourceModel.AccessStates; import Designite.SourceModel.SM_Package; import Designite.SourceModel.SM_Project; diff --git a/tests/DesigniteTests/DesigniteTests/metrics/MethodMetricsTest.java b/tests/DesigniteTests/DesigniteTests/metrics/MethodMetricsTest.java index f7a589f..dc56a04 100644 --- a/tests/DesigniteTests/DesigniteTests/metrics/MethodMetricsTest.java +++ b/tests/DesigniteTests/DesigniteTests/metrics/MethodMetricsTest.java @@ -7,7 +7,7 @@ import org.junit.Before; import org.junit.Test; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.SourceModel.SM_Method; import Designite.SourceModel.SM_Project; import Designite.SourceModel.SM_Type; diff --git a/tests/DesigniteTests/DesigniteTests/metrics/TypeMetricsTest.java b/tests/DesigniteTests/DesigniteTests/metrics/TypeMetricsTest.java index 2ca268d..0eb5698 100644 --- a/tests/DesigniteTests/DesigniteTests/metrics/TypeMetricsTest.java +++ b/tests/DesigniteTests/DesigniteTests/metrics/TypeMetricsTest.java @@ -7,7 +7,7 @@ import org.junit.Before; import org.junit.Test; -import Designite.InputArgs; +import Designite.ArgumentParser.InputArgs; import Designite.SourceModel.SM_Package; import Designite.SourceModel.SM_Project; import Designite.SourceModel.SM_Type;