package husacct.analyse.task.analyse.csharp.generators; import husacct.analyse.infrastructure.antlr.csharp.CSharpParser; import husacct.analyse.task.analyse.VisibilitySet; import java.util.LinkedList; import java.util.Stack; import org.antlr.runtime.CharStream; import org.antlr.runtime.tree.CommonTree; import org.antlr.runtime.tree.Tree; import org.apache.log4j.Logger; public class CSharpGeneratorToolkit { private static final String EMPTYSTRING = ""; private static final String DOT = "."; private static final String COMMA = ","; private static final Logger logger = Logger.getLogger(CSharpGeneratorToolkit.class); /** * Returns the parentname from the stack: IE stack is C.B.A -> "A.B.C" * @param parentStack */ public static String getNameFromStack(Stack<String> parentStack) { String result = EMPTYSTRING; for (String parentNamePart : parentStack) { result += parentNamePart + DOT; } return result.length() > 0 ? result.substring(0, result.length() - 1) : EMPTYSTRING; } /** * Inserts a dot when parentName is not empty * @param parentName */ public static String potentiallyInsertDot(String parentName) { if ((parentName == null) || (parentName.equals(""))) return ""; else return "."; } /** * Concatenates two strings and inserts a dot when parentName != null * @param parentName * @param name */ public static String getUniqueName(String parentName, String name) { String result = parentName + potentiallyInsertDot(parentName) + name; return result.endsWith(DOT) ? result.substring(0, result.length() -1) : result; } /** * Removes a DOT at the beginning of the given name * @param name */ public static String removeDotAtBeginningOfName(String name) { return name.startsWith(DOT) ? name.substring(1, name.length()) : name; } /** * Removes a DOT at the end of the given name * @param name */ public static String removeDotAtEndOfName(String name) { return name.endsWith(DOT) ? name.substring(0, name.length() -1) : name; } /** * Concatenates two strings by passing through to getUniqueName(String, String) */ public static String belongsToClass(String namespaces, String classes) { return getUniqueName(namespaces, classes); } /** * Checks whether or not a Tree has a modifier ABSTRACT (and thus is abstract) * Especially useful when checking classes and methods; * @param tree The Tree which to check for a modifier * @return whether or not the given Tree has a modifier ABSTRACT */ public static boolean isAbstract(Tree tree) { CommonTree ct = (CommonTree) tree; CommonTree modifierList = (CommonTree) ct.getFirstChildWithType(CSharpParser.MODIFIERS); if (modifierList == null || modifierList.getChildCount() < 1) { return false; } else { return modifierList.getFirstChildWithType(CSharpParser.ABSTRACT) != null; } } /** * Gets the visibility from a Tree by making use of the VisibilitySet. * @param tree The tree to get the visibility from. * @return The visiblity of this part of the Tree (IE "public", "package", etc.) */ public static String getVisibility(Tree tree) { CommonTree ct = (CommonTree) tree; CommonTree modifierList = (CommonTree) ct.getFirstChildWithType(CSharpParser.MODIFIERS); if (modifierList == null || modifierList.getChildCount() < 1) { return VisibilitySet.DEFAULT.toString(); } else { String found = modifierList.getChild(0).toString(); if (VisibilitySet.isValidVisibillity(found)) { return found; } else { return VisibilitySet.DEFAULT.toString(); } } } /** * Retrieves a descendant from the ancestor by walking the tree by getting children with the given types. * (IE: walkTree(myTree, MODIFIERS, ABSTRACT) gets the child from myTree with type MODIFIERS and from that tree * retrieves the child with ABSTRACT) * Especially useful when walking a tree from which is known how it's built. * @param ancestor The parent to get the descendant from. * @param types the list of types to walk the tree sequentially. * @return The descendant which matches the given types. */ public static CommonTree findHierarchicalSequenceOfTypes(CommonTree ancestor, int... types) { CommonTree currentParent = ancestor; for (int i = 0; i < types.length; i++) { if (currentParent == null) { return null; } currentParent = (CommonTree) currentParent.getFirstChildWithType(types[i]); } return currentParent; } /** * Checks whether or not a parent has a certain typed child, including null-check */ public static boolean hasChild(CommonTree parent, int type) { if (parent == null) { return false; } return parent.getFirstChildWithType(type) != null; } /** * Deletes all children from a certain Tree */ public static void deleteTreeChild(Tree treeNode) { for (int child = 0; child < treeNode.getChildCount(); child++) { treeNode.deleteChild(child); } } /** * Creates and returns a String with the names from a Stack, delimited by a comma * (IE: ["A", "B", "C"] returns "A,B,C") */ public static String createCommaSeperatedString(Stack<String> names) { String result = EMPTYSTRING; for (String parentNamePart : names) { result += parentNamePart + COMMA; } return result.length() > 0 ? result.substring(0, result.length() - 1) : EMPTYSTRING; } /** * Retrieves the Namespace and type parts from a Tree. This occurs in an AST when a variable is * declared with package and/or classnames (when there's no import, for example: * 'public A.B.MyClass mc = new A.B.MyClass();') */ public static String getTypeNameAndParts(CommonTree tree) { String returnValue = EMPTYSTRING; try { if (tree != null) { CommonTree typenameTree; if (tree.getType() == CSharpParser.NAMESPACE_OR_TYPE_NAME) { typenameTree = tree; } else { typenameTree = (CommonTree) tree.getFirstChildWithType(CSharpParser.NAMESPACE_OR_TYPE_NAME); // NAMESPACE_OR_TYPE_NAME } if (typenameTree != null) { returnValue = getComplete_NAMESPACE_OR_TYPE_NAME_String(typenameTree); } else { typenameTree = (CommonTree) tree.getFirstChildWithType(CSharpParser.STRING); if (typenameTree != null) { returnValue += tree.getFirstChildWithType(CSharpParser.STRING).getText(); } else { typenameTree = (CommonTree) tree.getFirstChildWithType(CSharpParser.INT); if (typenameTree != null) { returnValue += tree.getFirstChildWithType(CSharpParser.INT).getText(); } else { typenameTree = (CommonTree) tree.getFirstChildWithType(CSharpParser.BOOL); if (typenameTree != null) { returnValue += tree.getFirstChildWithType(CSharpParser.BOOL).getText(); } } } } } } catch (Exception e) { logger.warn("Exception: " + e + ", in getTypeNameAndParts()"); //e.printStackTrace(); } return returnValue; } public static String getComplete_NAMESPACE_OR_TYPE_NAME_String(CommonTree tree) { String returnValue = ""; try { int treeType = tree.getType(); switch(treeType) { case CSharpParser.NAMESPACE_OR_TYPE_NAME: case CSharpParser.NAMESPACE_OR_TYPE_PART: boolean isFirstSubString = true; for (int i = 0; i < tree.getChildCount(); i++) { String subString= getComplete_NAMESPACE_OR_TYPE_NAME_String((CommonTree) tree.getChild(i)); if ((subString != null) && !subString.equals("")) { if (isFirstSubString) { returnValue += subString; isFirstSubString = false; } else { if (tree.getChild(i).getType() == CSharpParser.TYPE_ARGUMENT_LIST) { // In case of generic classes, add the parameters as <p1>, <p1, p2>, etc. returnValue += subString; } else { returnValue += "." + subString; } } } } break; case CSharpParser.TYPE_ARGUMENT_LIST: // In case of generic classes, add the parameters as <p1>, <p1, p2>, etc. String parameters = ""; int nrOfParameters = tree.getChildCount(); if (nrOfParameters > 0) { for (int f = 0; f < nrOfParameters; f++) { String subString= getComplete_NAMESPACE_OR_TYPE_NAME_String((CommonTree) tree.getChild(f)); if ((subString != null) && subString != null) { if (f == 0) { parameters += "p" + 1; } else { parameters += ", p" + (f+1); } } } } returnValue += "<"+ parameters + ">"; break; case CSharpParser.IDENTIFIER: returnValue += tree.getText(); break; case CSharpParser.QUALIFIED_IDENTIFIER: // ? Not encountered within test with Limaki returnValue = tree.getChild(0).getText(); break; case CSharpParser.THIS: returnValue = ""; break; case CSharpParser.BASE: returnValue = "superBaseClass"; break; case CSharpParser.DOT: // "." String left = getComplete_NAMESPACE_OR_TYPE_NAME_String((CommonTree) tree.getChild(0)); String right = getComplete_NAMESPACE_OR_TYPE_NAME_String((CommonTree) tree.getChild(1)); if ((left.equals("")) || (right.equals(""))) { returnValue += left + right; } else { returnValue += left + "." + right; } break; } } catch (Exception e) { logger.error("Exception: "+ e); } return returnValue; } /** * Checks whether or not a tree is of a certain type, including null-check */ public static boolean isOfType(CommonTree tree, int type) { if (tree == null) { return false; } return tree.getType() == type; } /** * Gets a descendant from a ancestor with a certain type. This method walks * the tree breadth-first, to make sure it is the closest relative from the ancestor. */ public static CommonTree getFirstDescendantWithType(CommonTree root, int type) { LinkedList<CommonTree> queue = new LinkedList<>(); queue.add(root); while(!queue.isEmpty()) { CommonTree first = queue.removeFirst(); if (first != null) { for (int i = 0; i < first.getChildCount(); i++) { CommonTree child = (CommonTree)first.getChild(i); if (isOfType(child, type)) return child; queue.addLast(child); } } } return null; } /** * Returns the file path of a certain tree, by checking the source name of the input stream. * Not every Token has an inputstream, so might return NO FILE FOUND. */ public static String tryToGetFilePath(CommonTree tree) { if (tree == null) return "NO FILE FOUND"; CharStream charStream = tree.getToken().getInputStream(); if (charStream == null) return tryToGetFilePath((CommonTree)tree.getParent()); else return tree.getToken().getInputStream().getSourceName(); } }