package husacct.analyse.task.analyse.csharp.generators;
import husacct.analyse.domain.IModelCreationService;
import husacct.analyse.domain.famix.FamixCreationServiceImpl;
import husacct.analyse.infrastructure.antlr.csharp.CSharpParser;
import husacct.common.enums.DependencySubTypes;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;
public class CSharpAttributeAndLocalVariableGenerator extends CSharpGenerator{
private String name;
private String accessControlQualifier;
private boolean hasClassScope;
private boolean isFinal;
private String belongsToClass; // Unique name, including packages
private int lineNumber;
private String belongsToMethod; // Alleen voor local variables
private String declareType; // Type of the attribute. In case of an instance variable with a generic type e.g. ArrayList<Person>, this value is ArrayList.
private String typeInClassDiagram; // E.g. in case of an instance variable with a generic type ArrayList<Person>, this value is Person.
private boolean isComposite; // False if the type allows one value only, like Person; True in case of a generic type, or e.g. Person[].
private boolean isLocalVariable;
private int levelOfRecursionWithinGenericType;
private DependencySubTypes dependencySubType;
private CSharpInvocationGenerator csharpInvocationGenerator;
private IModelCreationService modelService = new FamixCreationServiceImpl();
public void generateAttributeToDomain(Tree treeNode, String packageAndClassName) {
initialize();
this.belongsToClass = packageAndClassName;
dependencySubType = DependencySubTypes.DECL_INSTANCE_VAR;
/* Test helpers
if (belongsToClass.contains("DeclarationVariableInstance_GenericType_OneTypeParameter")) {
boolean breakpoint = true;
} */
walkThroughAST(treeNode);
createAttributeObject();
}
public void generateLocalVariableToDomain(Tree treeNode, String belongsToClass, String belongsToMethod) {
initialize();
isLocalVariable = true;
dependencySubType = DependencySubTypes.DECL_LOCAL_VAR;
this.belongsToClass = belongsToClass;
this.belongsToMethod = belongsToMethod;
walkThroughAST(treeNode);
createLocalVariableObject();
}
public void generateLocalVariableForEachLoopToDomain(String packageAndClassName, String belongsToMethod, String name, String type, int line) {
initialize();
isLocalVariable = true;
dependencySubType = DependencySubTypes.DECL_LOCAL_VAR;
this.belongsToClass = packageAndClassName;
this.belongsToMethod = belongsToMethod;
this.name = name;
this.declareType = type;
this.lineNumber = line;
createLocalVariableObject();
}
private void initialize(){
hasClassScope = false;
isFinal = false;
isComposite = false;
typeInClassDiagram = "";
name = "";
accessControlQualifier = "";
belongsToClass = "";
belongsToMethod = "";
declareType = "";
lineNumber = 0;
levelOfRecursionWithinGenericType = 0;
isLocalVariable = false;
dependencySubType = null;
}
private void walkThroughAST(Tree treeNode) {
for(int i = 0; i < treeNode.getChildCount(); i++){
Tree child = treeNode.getChild(i);
boolean walkThroughChildren = true;
switch(child.getType()){
// The first three cases are the default ones for a variable declaration, in order of appearance
case CSharpParser.MODIFIERS:
setModifiers(child);
walkThroughChildren = false;
break;
case CSharpParser.TYPE:
setType(child);
walkThroughChildren = false;
break;
case CSharpParser.IDENTIFIER: // There is only one, since multiple names after a type result in multiple calls to this class.
setName(child);
walkThroughChildren = false;
break;
// Assignment statements are passed to a suitable method of csharpInvocationGenerator
case CSharpParser.UNARY_EXPRESSION:
csharpInvocationGenerator = new CSharpInvocationGenerator(this.belongsToClass);
csharpInvocationGenerator.generateMethodInvocToDomain((CommonTree) child, this.belongsToMethod);
walkThroughChildren = false;
break;
}
if (walkThroughChildren) {
walkThroughAST(child);
}
}
}
private void setModifiers(Tree ModifierList) {
accessControlQualifier = "package";
hasClassScope = false;
isFinal = false;
for (int i = 0; i < ModifierList.getChildCount(); i++) {
int treeType = ModifierList.getChild(i).getType();
switch(treeType)
{
case CSharpParser.PRIVATE:
accessControlQualifier = "private";
break;
case CSharpParser.PUBLIC:
accessControlQualifier = "public";
break;
case CSharpParser.PROTECTED:
accessControlQualifier = "protected";
break;
case CSharpParser.STATIC:
hasClassScope = true;
break;
case CSharpParser.READONLY: case CSharpParser.CONST:
isFinal = true;
break;
}
}
// Set dependencySubType
if (!isLocalVariable) {
if (hasClassScope) {
dependencySubType = DependencySubTypes.DECL_CLASS_VAR;
} else {
dependencySubType = DependencySubTypes.DECL_INSTANCE_VAR;
}
}
}
private void setType(Tree typeNode) {
CommonTree typeTree = (CommonTree) typeNode;
// Determine the type of the declared variable
csharpInvocationGenerator = new CSharpInvocationGenerator(this.belongsToClass);
String foundType = csharpInvocationGenerator.getCompleteToString(typeTree, belongsToClass, dependencySubType);
if ((foundType != null) && !foundType.equals("")) {
this.declareType = foundType;
}
// Check if the types is a generic type, e.g.: List<Type1>, Dictionary<Type2, Type3>
CommonTree typeArgumentList = CSharpGeneratorToolkit.getFirstDescendantWithType(typeTree, CSharpParser.TYPE_ARGUMENT_LIST);
if (typeArgumentList != null) {
this.isComposite = true;
addGenericTypeParameters(typeArgumentList);
}
// Check if an array is declared.
CommonTree rankSpecifier = CSharpGeneratorToolkit.getFirstDescendantWithType(typeTree, CSharpParser.RANK_SPECIFIER);
if (rankSpecifier != null) {
this.isComposite = true;
this.typeInClassDiagram = declareType;
}
}
// Detects generic type parameters, also in complex types, like: HashMap<ProfileDAO, ArrayList<FriendsDAO>>>
private void addGenericTypeParameters(CommonTree genericType) {
csharpInvocationGenerator = null;
int numberOfTypeParameters = genericType.getChildCount();
for (int j = 0; j < numberOfTypeParameters; j++) {
CommonTree parameterTypeOfGenericTree = (CommonTree) genericType.getChild(j);
// Check if parameterTypeOfGenericTree contains a generic type arg list. If so, handle it recursively.
CommonTree genericTypeRecursive = CSharpGeneratorToolkit.getFirstDescendantWithType(parameterTypeOfGenericTree, CSharpParser.TYPE_ARGUMENT_LIST);
if (genericTypeRecursive != null) {
levelOfRecursionWithinGenericType ++; // Needed to prevent that this.typeInClassDiagram is set with a type included in a recursive generic type.
addGenericTypeParameters(genericTypeRecursive);
} else {
CommonTree qualifiedType = CSharpGeneratorToolkit.getFirstDescendantWithType(parameterTypeOfGenericTree, CSharpParser.NAMESPACE_OR_TYPE_NAME);
if (qualifiedType != null) {
csharpInvocationGenerator = new CSharpInvocationGenerator(this.belongsToClass);
String parameterTypeOfGeneric = csharpInvocationGenerator.getCompleteToString(qualifiedType, belongsToClass, dependencySubType);
if (parameterTypeOfGeneric != null) {
if (!hasClassScope && (levelOfRecursionWithinGenericType == 0)) {
if (numberOfTypeParameters == 1) {
this.typeInClassDiagram = parameterTypeOfGeneric; // E.g. List<Person>, the type of the first TypeParameter is set.
} else if ((numberOfTypeParameters == 2) && (j == 1)) {
this.typeInClassDiagram = parameterTypeOfGeneric; // E.g. Dictionary<String, Person>, the type of the second TypeParameter is set.
}
}
int currentLineNumber = qualifiedType.getLine();
modelService.createTypeParameter(belongsToClass, currentLineNumber, parameterTypeOfGeneric);
}
}
}
}
}
private void setName(Tree identifierTree) {
if(identifierTree != null){
this.name = identifierTree.getText();
this.lineNumber = identifierTree.getLine();
}
}
private void createAttributeObject() {
if ((declareType != null) && !declareType.equals("")) {
if(SkippableTypes.isSkippable(declareType)){
modelService.createAttributeOnly(hasClassScope, isFinal, accessControlQualifier, belongsToClass, declareType, name, belongsToClass + "." + name, lineNumber, typeInClassDiagram, isComposite);
} else {
modelService.createAttribute(hasClassScope, isFinal, accessControlQualifier, belongsToClass, declareType, name, belongsToClass + "." + name, lineNumber, typeInClassDiagram, isComposite);
}
}
}
private void createLocalVariableObject() {
if ((declareType != null) && !declareType.equals("")) {
if(SkippableTypes.isSkippable(declareType)){
modelService.createLocalVariableOnly(belongsToClass, declareType, name, belongsToClass + "." + belongsToMethod + "." + name, lineNumber, belongsToMethod);
} else {
modelService.createLocalVariable(belongsToClass, declareType, name, belongsToClass + "." + belongsToMethod + "." + name, lineNumber, belongsToMethod);
}
}
}
}