/* This file is part of Green. * * Copyright (C) 2005 The Research Foundation of State University of New York * All Rights Under Copyright Reserved, The Research Foundation of S.U.N.Y. * * Green is free software, licensed under the terms of the Eclipse * Public License, version 1.0. The license is available at * http://www.eclipse.org/legal/epl-v10.html */ package edu.buffalo.cse.green.relationships; import static org.eclipse.jdt.core.IJavaElement.FIELD; import static org.eclipse.jdt.core.IJavaElement.LOCAL_VARIABLE; import static org.eclipse.jdt.core.dom.Modifier.STATIC; import java.util.AbstractList; import java.util.ArrayList; import java.util.List; import java.util.Stack; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.ILocalVariable; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTMatcher; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.Initializer; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.ui.actions.OrganizeImportsAction; import edu.buffalo.cse.green.editor.DiagramEditor; /** * A parent for generators/recognizer/removers. * * The functionality of this class is as follows: * -It must allow subclasses visit only appropriate nodes. For example, nodes * that represent type declarations and methods. * -It must keep an up-to-the-minute listing of accessible fields and methods * for the purposes of preventing duplicate field/method names in code * generation and removing pieces of code, as well as helping the * relationship recognizers extract pieces of code that uniquely identify a * relationship's constituents. * * @author bcmartin * @author dk29 */ public abstract class RelationshipVisitor extends ASTVisitor { private ASTMatcher _matcher = new ASTMatcher(); private MethodDeclaration _methodDeclaration; private AST _ast; private CompilationUnit _cu; private boolean _inConstructor = false; private List<String> _locals; private List<String> _parameters; private List<ILocalVariable> _parameterVars; private Stack<DeclarationInfoProvider> _typeStack; /** * @param element - The member element. * @return A <code>CompilationUnit</code> representing the structure of the * source code of the element. */ public static CompilationUnit getCompilationUnit(IMember element) { ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setResolveBindings(true); if (element.isBinary()) { parser.setSource((IClassFile) element .getAncestor(IJavaElement.CLASS_FILE)); } else { parser.setSource((ICompilationUnit) element .getAncestor(IJavaElement.COMPILATION_UNIT)); } return (CompilationUnit) parser.createAST(null); } protected RelationshipVisitor() { _typeStack = new Stack<DeclarationInfoProvider>(); _locals = new ArrayList<String>(); _parameters = new ArrayList<String>(); _parameterVars = new ArrayList<ILocalVariable>(); } /** * Runs the visitor on the given <code>CompilationUnit</code>. * * @param cu - The compilation unit. */ public void accept(CompilationUnit cu) { _ast = cu.getAST(); _cu = cu; preVisit(); run(cu, null); } /** * This method is called before visiting occurs to allow a visitor to * collect any information necessary to perform the desired behavior. */ protected void preVisit() {} /** * @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.MethodDeclaration) */ public final void endVisit(MethodDeclaration node) { _methodDeclaration = null; _parameters.clear(); _parameterVars.clear(); _inConstructor = false; } /** * @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.TypeDeclaration) */ public final void endVisit(EnumDeclaration node) { endVisit((TypeDeclaration) null); } /** * @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.TypeDeclaration) */ public /*final*/ void endVisit(TypeDeclaration node) { _typeStack.pop(); } /** * @return The AST for the compilation unit. */ protected AST getAST() { return _ast; } /** * @return The compilation unit. */ protected CompilationUnit getCompilationUnit() { return _cu; } /** * @param element - The element. * @return The <code>CompilationUnit</code> representing the given element. */ public CompilationUnit getCompilationUnit(IClassFile element) { ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setResolveBindings(true); parser.setSource(element); return (CompilationUnit) parser.createAST(null); } /** * @param element - The element. * @return The <code>CompilationUnit</code> representing the given element. */ public CompilationUnit getCompilationUnit(ICompilationUnit element) { ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setResolveBindings(true); parser.setSource(element); return (CompilationUnit) parser.createAST(null); } /** * @param element - The element. * @return The <code>CompilationUnit</code> representing the given element. */ public CompilationUnit getCompilationUnit(IType element) { if (element.isBinary()) { return getCompilationUnit((IClassFile) element .getAncestor(IJavaElement.CLASS_FILE)); } else { return getCompilationUnit((ICompilationUnit) element .getAncestor(IJavaElement.COMPILATION_UNIT)); } } /** * @param provider - The type information provider. * @return the Java element representing the given type. */ public IType getType(DeclarationInfoProvider provider) { return getType(provider.getDeclaration().resolveBinding()); } /** * @param type - The given <code>Type</code>. * @return The <code>IType</code> bound to the given type. */ public IType getType(Type type) { return getType(type.resolveBinding()); } /** * @param binding - The given <code>ITypeBinding</code>. * @return The <code>IType</code> bound to the given type. */ public IType getType(ITypeBinding binding) { return (IType) binding.getJavaElement(); } /** * Handles visiting the given node in subclasses. * * @param node - The node. * @return True if further processing should occur, false otherwise. */ protected abstract boolean process(DeclarationInfoProvider node); /** * Called to run the visitor. * * @param cu - The compilation unit to visit. * @param cache - The cache of relationships. */ protected abstract void run(CompilationUnit cu, RelationshipCache cache); /** * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodDeclaration) * * @author Gene Wang */ public final boolean visit(MethodDeclaration node) { //Ignore all static children if ((node.getModifiers() & STATIC) != 0) return false; _inConstructor = node.isConstructor(); _methodDeclaration = node; // retrieve the simple names of local variables and the elements they // represent for (SingleVariableDeclaration varDec : (AbstractList<SingleVariableDeclaration>) node .parameters()) { SimpleName parameter = varDec.getName(); _parameters.add(parameter.getIdentifier()); _parameterVars.add((ILocalVariable) parameter.resolveBinding() .getJavaElement()); } return true; } /** * Ensures no recognizers attempt to use initializer blocks for relationships * * @author Gene Wang */ public boolean visit(Initializer node) { return false; } /** * Ensures no recognizers attempt to use static fields for relationships * * @author Gene Wang */ public boolean visit(SingleVariableDeclaration svd) { if(isField(svd.getName()) && (svd.getModifiers() & STATIC) != 0) return false; return true; } /** * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.EnumDeclaration) */ public final boolean visit(EnumDeclaration node) { return visit(DeclarationInfoProvider.getInfoProvider(node)); } /** * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeDeclaration) */ public final boolean visit(TypeDeclaration node) { return visit(DeclarationInfoProvider.getInfoProvider(node)); } /** * Processes type info as an <code>AbstractTypeDeclaration</code> node is * visited. This is necessary to get around the problem caused by enums * having a different node from classes and interfaces. * * @param node - The <code>DeclarationInfoProvider</code> being visited. * @return <code>true</code> if the children of this node should be visited, * and <code>false</code> if the children of this node should be skipped. */ public final boolean visit(DeclarationInfoProvider node) { _typeStack.push(node); return process(node); } /** * @param var - The <code>ILocalVariable</code> to check * @return True if the name is bound to a local variable, false otherwise */ private boolean isLocalVariable(ILocalVariable var) { return !isParameter(var); } /** * @param var - The <code>ILocalVariable</code> to check * @return True if the name is bound to a parameter, false otherwise */ private boolean isParameter(ILocalVariable var) { for (ILocalVariable param : _parameterVars) { if (param.equals(var)) { return true; } } return false; } /** * @param name - The <code>Name</code> to check * @return True if the name is bound to a field, false otherwise */ protected boolean isField(Name name) { IBinding binding = name.resolveBinding(); if (binding == null) return false; return binding.getJavaElement().getElementType() == FIELD; } /** * @param name - The <code>Name</code> to check * @return True if the name is bound to a parameter, false otherwise */ protected boolean isParameter(Name name) { IBinding binding = name.resolveBinding(); if (binding == null) return false; IJavaElement element = binding.getJavaElement(); //element is null when binding is for a primitive or void, //in which case, we would not have any relationships. if(element == null) return false; if (!(element.getElementType() == LOCAL_VARIABLE)) return false; return isParameter((ILocalVariable) element); } /** * @param name - The <code>Name</code> to check * @return True if the name is bound to a local variable, false otherwise */ protected boolean isLocalVariable(Name name) { IBinding binding = name.resolveBinding(); if (binding == null) return false; IJavaElement element = binding.getJavaElement(); if (element == null) return false; if (!(element.getElementType() == LOCAL_VARIABLE)) return false; return isLocalVariable((ILocalVariable) element); } /** * @param name - The name of the field * @return True if the field is already declared, false otherwise */ protected boolean isFieldDeclared(String name) { return getFieldNames().contains(name); } /** * Performs import organization on the given <code>ICompilationUnit</code>. * * @param cu - The <code>ICompilationUnit</code>. */ protected void organizeImports(ICompilationUnit cu) { new OrganizeImportsAction( DiagramEditor.getActiveEditor().getSite()).runOnMultiple( new ICompilationUnit[] { cu }); } /** * Performs import organization on the given <code>IType</code>'s * compilation unit. * * @param type - The <code>IType</code>. */ protected void organizeImports(IType type) { organizeImports(type.getCompilationUnit()); } /** * @return true if we're in a constructor's node, false otherwise. */ protected boolean inConstructor() { return _inConstructor; } /** * @return The <code>IType</code> corresponding to the current type being * visited. */ protected IType getCurrentType() { AbstractTypeDeclaration typeDec = getCurrentTypeDeclaration(); return (IType) typeDec.resolveBinding().getJavaElement(); } /** * @return A list of <code>String</code>s representing the names of * local variables in the scope of the current method. */ protected List<String> getLocalDeclarations() { return _locals; } /** * @return A list of <code>String</code>s representing the names of * parameters in the scope of the current method. */ protected List<String> getParameterDeclarations() { return _parameters; } /** * @return The <code>MethodDeclaration</code> of the method most recently * visited. */ protected MethodDeclaration getMethodDeclaration() { return _methodDeclaration; } /** * @return The shared AST subtree matcher instance. */ protected ASTMatcher getMatcher() { return _matcher; } /** * @return The type info of the currently visited type. */ protected DeclarationInfoProvider getCurrentTypeInfo() { return _typeStack.peek(); } /** * @return The body declarations of the currently visited type. */ protected List<BodyDeclaration> getBodyDeclarations() { return (AbstractList<BodyDeclaration>) getCurrentTypeInfo().bodyDeclarations(); } /** * @return The <code>AbstractTypeDeclaration</code> corresponding to the * current type. */ protected AbstractTypeDeclaration getCurrentTypeDeclaration() { return getCurrentTypeInfo().getDeclaration(); } /** * @return A list of names of the fields in the compilation unit as String * objects. */ protected List<String> getFieldNames() { List<String> fieldNames = new ArrayList<String>(); for (DeclarationInfoProvider typeInfo : _typeStack) { fieldNames.addAll( DeclarationInfoProvider.getFieldNames(typeInfo.getFields())); } return fieldNames; } /** * @return A list of all the <code>FieldDeclaration</code> nodes in the * current type. */ protected List<FieldDeclaration> getFields() { return getCurrentTypeInfo().getFields(); } }