package husacct.analyse.task.analyse.csharp;
import static husacct.analyse.task.analyse.csharp.generators.CSharpGeneratorToolkit.*;
import husacct.analyse.infrastructure.antlr.csharp.CSharpParser;
import husacct.analyse.infrastructure.antlr.csharp.CSharpParser.compilation_unit_return;
import husacct.analyse.task.analyse.csharp.generators.*;
import java.util.Stack;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;
import org.apache.log4j.Logger;
public class CSharpTreeConvertController {
CSharpAnalyser cSharpAnalyser;
String sourceFilePath = "";
int numberOfLinesOfCode = 0;
CSharpUsingGenerator csUsingGenerator;
CSharpNamespaceGenerator csNamespaceGenerator;
CSharpClassGenerator csClassGenerator;
CSharpEnumGenerator csEnumGenerator;
CSharpInheritanceGenerator csInheritanceGenerator;
CSharpAttributeAndLocalVariableGenerator csAttributeGenerator;
CSharpPropertyGenerator csPropertyGenerator;
CSharpMethodGeneratorController csMethodeGenerator;
CSharpLamdaGenerator csLamdaGenerator;
String noNameSpaceString = "";
Stack<String> namespaceStack = new Stack<>();
Stack<String> classNameStack = new Stack<>();
private Logger logger = Logger.getLogger(CSharpTreeConvertController.class);
public CSharpTreeConvertController(CSharpAnalyser cSharpAnalyser) { // Constructor call for each source file.
this.cSharpAnalyser = cSharpAnalyser;
csUsingGenerator = new CSharpUsingGenerator();
csNamespaceGenerator = new CSharpNamespaceGenerator();
csClassGenerator = new CSharpClassGenerator();
csEnumGenerator = new CSharpEnumGenerator();
csInheritanceGenerator = new CSharpInheritanceGenerator();
csAttributeGenerator = new CSharpAttributeAndLocalVariableGenerator();
csPropertyGenerator = new CSharpPropertyGenerator();
csMethodeGenerator = new CSharpMethodGeneratorController();
csLamdaGenerator = new CSharpLamdaGenerator();
}
public void delegateDomainObjectGenerators(final CSharpParser cSharpParser, String sourceFilePath, int nrOfLinesOfCode) throws RecognitionException {
final CommonTree compilationCommonTree = getCompilationTree(cSharpParser);
this.sourceFilePath = sourceFilePath;
this.numberOfLinesOfCode = nrOfLinesOfCode;
recalculateLocInCaseOfMultipleClasses(compilationCommonTree);
// Analyse the AST and create domain objects for the relevant code elements.
delegateASTToGenerators(compilationCommonTree);
}
private CommonTree getCompilationTree(final CSharpParser cSharpParser) throws RecognitionException {
final compilation_unit_return compilationUnit = cSharpParser.compilation_unit();
return (CommonTree) compilationUnit.getTree();
}
private void delegateASTToGenerators(CommonTree tree) {
/* Test and Debug
if (sourceFilePath.contains("CallClassMethod_ClassWithoutNamespace")) {
boolean breakpoint = true;
} */
if (isTreeAvailable(tree)) {
for (int i = 0; i < tree.getChildCount(); i++) {
CommonTree treeNode = (CommonTree) tree.getChild(i);
int nodeType = treeNode.getType();
switch (nodeType) {
case CSharpParser.USING_DIRECTIVES:
saveUsing(treeNode);
deleteTreeChild(treeNode);
break;
case CSharpParser.NAMESPACE:
CommonTree namespaceTree = treeNode;
namespaceStack.push(delegateNamespace(namespaceTree));
delegateASTToGenerators(namespaceTree);
namespaceStack.pop();
break;
case CSharpParser.CLASS:
case CSharpParser.INTERFACE:
case CSharpParser.ENUM:
case CSharpParser.STRUCT:
CommonTree classTree = treeNode;
boolean isInterface = false;
boolean isEnumeration = false;
if (nodeType == CSharpParser.INTERFACE) {
isInterface = true;
} else if (nodeType == CSharpParser.ENUM) {
isEnumeration = true;
}
boolean isInner = classNameStack.size() > 0;
classNameStack.push(delegateClass(classTree, isInner, isInterface, isEnumeration));
if (!isInner) {
delegateUsings();
}
delegateASTToGenerators(classTree);
classNameStack.pop();
break;
case CSharpParser.EXTENDS_OR_IMPLEMENTS:
case CSharpParser.IMPLEMENTS:
delegateInheritanceDefinition(treeNode);
deleteTreeChild(treeNode);
break;
case CSharpParser.VARIABLE_DECLARATOR:
delegateAttribute(treeNode);
deleteTreeChild(treeNode);
break;
case CSharpParser.PROPERTY_DECL:
delegateProperty(treeNode);
deleteTreeChild(treeNode);
break;
case CSharpParser.METHOD_DECL:
case CSharpParser.CONSTRUCTOR_DECL:
delegateMethod(treeNode);
deleteTreeChild(treeNode);
break;
case CSharpParser.DELEGATE:
delegateDelegate(treeNode);
break;
default:
delegateASTToGenerators(treeNode);
}
}
}
}
private boolean isTreeAvailable(Tree tree) {
if (tree != null) {
return true;
}
return false;
}
/* In case of multiple first-level classes in the file, numberOfLinesOfCode needs to be recalculated.
* In these (exceptional) cases, numberOfLinesOfCode is not determined exactly per class, but as average LOC per class in the file.
* To test, use Limada-source code: Limaki.View\Limaki.View\Visualizers\ImageDisplay.cs. It contains two namespaces and four types.
*/
private void recalculateLocInCaseOfMultipleClasses(CommonTree tree) {
int numberOfTypesInSourceFile = calculateNumberOfClassesInSourceFile(tree);
if (numberOfTypesInSourceFile > 1) {
numberOfLinesOfCode = numberOfLinesOfCode / numberOfTypesInSourceFile;
}
}
private int calculateNumberOfClassesInSourceFile(CommonTree tree) {
int numberOfTypesInSourceFile = 0;
CommonTree namespaceMembersTree = CSharpGeneratorToolkit.getFirstDescendantWithType(tree, CSharpParser.NAMESPACE_MEMBER_DECLARATIONS);
if (namespaceMembersTree != null) {
int nrOfMembers = namespaceMembersTree.getChildCount();
for (int i = 0; i < nrOfMembers; i++) {
CommonTree memberTree = (CommonTree) namespaceMembersTree.getChild(i);
int nodeType = memberTree.getType();
switch (nodeType) {
case CSharpParser.NAMESPACE:
numberOfTypesInSourceFile = numberOfTypesInSourceFile + calculateNumberOfClassesInSourceFile(memberTree);
break;
case CSharpParser.CLASS:
case CSharpParser.INTERFACE:
case CSharpParser.ENUM:
case CSharpParser.STRUCT:
numberOfTypesInSourceFile ++;
break;
default:
// Do nothing
}
}
}
return numberOfTypesInSourceFile;
}
private void saveUsing(CommonTree usingTree) {
csUsingGenerator.addUsings(usingTree);
}
private void delegateUsings() {
csUsingGenerator.generateToDomain(createPackageAndClassName(classNameStack));
}
private String delegateNamespace(CommonTree namespaceTree) {
return csNamespaceGenerator.generateModel(CSharpGeneratorToolkit.getNameFromStack(namespaceStack), namespaceTree);
}
private String delegateClass(CommonTree classTree, boolean isInnerClass, boolean isInterface, boolean isEnumeration) {
String analysedClass;
if (isInnerClass) {
analysedClass = csClassGenerator.generateToModel(sourceFilePath, 0, classTree, getNameSpaceName(), getNameFromStack(classNameStack), isInterface, isEnumeration);
if (analysedClass == null){
analysedClass = "";
// logger.warn("Inner class not added of parent: " + getParentName(namespaceStack));
}
} else {
analysedClass = csClassGenerator.generateToDomain(sourceFilePath, numberOfLinesOfCode, classTree, getNameSpaceName(), isInterface, isEnumeration);
}
return analysedClass;
}
private void delegateInheritanceDefinition(CommonTree inheritanceTree) {
csInheritanceGenerator.generateToDomain(inheritanceTree, createPackageAndClassName(classNameStack));
}
private void delegateAttribute(CommonTree attributeTree) {
if (attributeTree.toStringTree().contains("= >")) {
csLamdaGenerator.delegateLambdaToBuffer(attributeTree, createPackageAndClassName(classNameStack), "");
} else {
csAttributeGenerator.generateAttributeToDomain(attributeTree, createPackageAndClassName(classNameStack));
}
}
private void delegateProperty(CommonTree propertyTree) {
csPropertyGenerator.generateProperyToDomain(propertyTree, createPackageAndClassName(classNameStack));
}
private void delegateMethod(CommonTree methodTree) {
csMethodeGenerator.generateMethodToDomain(methodTree, createPackageAndClassName(classNameStack));
}
private void delegateDelegate(CommonTree lamdaTree) {
csLamdaGenerator.delegateDelegateToBuffer(lamdaTree, createPackageAndClassName(classNameStack));
}
/**
* Retrieves the package and classname concatenated with dots.
* ([A,B] and [C,D] becomes "A.B.C.D")
* @return The package and classname concatenated with dots.
*/
private String createPackageAndClassName(Stack<String> classStack) {
String namespaces = getNameSpaceName();
String classes = getNameFromStack(classStack);
return getUniqueName(namespaces, classes);
}
private String getNameSpaceName() {
String namespace = "";
namespace = getNameFromStack(namespaceStack);
if (namespace.equals("")) {
if (noNameSpaceString.equals("")) {
// Create a No_Namespace package, extended with the directories in the sourceFilePath - projectPath.
String projectPath = cSharpAnalyser.getProjectPath();
char separator = '/';
projectPath = projectPath.replace(separator, '_');
String sourceFilePathReplace = sourceFilePath.replace('\\',separator);
sourceFilePathReplace = sourceFilePathReplace.replace(cSharpAnalyser.getFileExtension(), "");
int positionLastSeparator = sourceFilePathReplace.lastIndexOf(separator);
String sourceFilePathWithoutProjectPath = "";
if (positionLastSeparator >= 0) {
sourceFilePathReplace = sourceFilePathReplace.substring(0, positionLastSeparator);
sourceFilePathReplace = sourceFilePathReplace.replace(separator, '_');
if (sourceFilePathReplace.contains(projectPath)) {
sourceFilePathWithoutProjectPath = sourceFilePathReplace.replaceAll(projectPath, "");
}
}
namespace = csNamespaceGenerator.generateNo_Namespace(sourceFilePathWithoutProjectPath);
noNameSpaceString = namespace;
logger.info(" Class without namespace. Created namespace: " + namespace);
} else {
namespace = noNameSpaceString;
}
}
return namespace;
}
}