Java tutorial
/** * Copyright (c) 2006, 2007, 2008 Marwan Abi-Antoun, Jonathan Aldrich, Nels E. Beckman, * Kevin Bierhoff, David Dickey, Ciera Jaspan, Thomas LaToza, Gabriel Zenarosa, and others. * * This file is part of Crystal. * * Crystal is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Crystal is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Crystal. If not, see <http://www.gnu.org/licenses/>. */ package edu.cmu.cs.crystal.internal; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaModel; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IParent; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.EnumConstantDeclaration; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.ImportDeclaration; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.PackageDeclaration; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.TypeDeclarationStatement; import org.eclipse.jdt.core.dom.VariableDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import edu.cmu.cs.crystal.util.Box; import edu.cmu.cs.crystal.util.Lambda; import edu.cmu.cs.crystal.util.Option; /** * A collection of methods used to extract useful data from the workspace. * These methods are used by the framework and should not be used by users * of the framework. * * You can access must of the data collected from these methods via the * Crystal class. * * @author David Dickey * */ public class WorkspaceUtilities { private static final Logger log = Logger.getLogger(WorkspaceUtilities.class.getName()); /** * Traverses the workspace for CompilationUnits. * * @return the list of all CompilationUnits in the workspace or * <code>null</code> if no comp units were found. */ public static List<ICompilationUnit> scanForCompilationUnits() { IWorkspace workspace = ResourcesPlugin.getWorkspace(); if (workspace == null) { log.warning("No workspace"); return null; } IWorkspaceRoot root = workspace.getRoot(); if (root == null) { log.warning("No workspace root"); return null; } IJavaModel javaModel = JavaCore.create(root); if (javaModel == null) { log.warning("No Java Model in workspace"); return null; } // Get all CompilationUnits return collectCompilationUnits(javaModel); } /** * A recursive traversal of the IJavaModel starting from the given * element to collect all ICompilationUnits. * Each compilation unit corresponds to each java file. * * @param javaElement a node in the IJavaModel that will be traversed * @return a list of compilation units or <code>null</code> if no comp units are found */ public static List<ICompilationUnit> collectCompilationUnits(IJavaElement javaElement) { List<ICompilationUnit> list = null, temp = null; // We are traversing the JavaModel for COMPILATION_UNITs if (javaElement.getElementType() == IJavaElement.COMPILATION_UNIT) { list = new ArrayList<ICompilationUnit>(); list.add((ICompilationUnit) javaElement); return list; } // Non COMPILATION_UNITs will have to be further traversed if (javaElement instanceof IParent) { IParent parent = (IParent) javaElement; // Do not traverse PACKAGE_FRAGMENT_ROOTs that are ReadOnly // this ignores libraries and .class files if (javaElement.getElementType() == IJavaElement.PACKAGE_FRAGMENT_ROOT && javaElement.isReadOnly()) { return null; } // Traverse try { if (parent.hasChildren()) { IJavaElement[] children = parent.getChildren(); for (int i = 0; i < children.length; i++) { temp = collectCompilationUnits(children[i]); if (temp != null) if (list == null) list = temp; else list.addAll(temp); } } } catch (JavaModelException jme) { log.log(Level.SEVERE, "Problem traversing Java model element: " + parent, jme); } } else { log.warning("Encountered a model element that's not a comp unit or parent: " + javaElement); } return list; } /** * Goes through a list of compilation units and parses them. The act of parsing * creates the AST structures from the source code. * * @param compilationUnits the list of compilation units to parse * @return the mapping from compilation unit to the AST roots of each */ public static Map<ICompilationUnit, ASTNode> parseCompilationUnits(List<ICompilationUnit> compilationUnits) { if (compilationUnits == null) throw new CrystalRuntimeException("null list of compilation units"); Map<ICompilationUnit, ASTNode> parsedCompilationUnits = new HashMap<ICompilationUnit, ASTNode>(); Iterator<ICompilationUnit> iter = compilationUnits.iterator(); ICompilationUnit compUnit = null; ASTParser parser = null; ASTNode node = null; for (; iter.hasNext();) { compUnit = iter.next(); parser = ASTParser.newParser(AST.JLS3); parser.setResolveBindings(true); parser.setSource(compUnit); node = parser.createAST(null); parsedCompilationUnits.put(compUnit, node); } return parsedCompilationUnits; } /** * Collects all top level methods from CompilationUnits. * * (Embedded Methods are currently not collected.) * * @param compilationUnitToASTNode the mapping of CompilationUnits to preparsed ASTNodes * @return the list of all top level methods within the CompilationUnits */ public static List<MethodDeclaration> scanForMethodDeclarations( Map<ICompilationUnit, ASTNode> compilationUnitToASTNode) { if (compilationUnitToASTNode == null) throw new CrystalRuntimeException("null map of compilation units to ASTNodes"); // Create an empty list List<MethodDeclaration> methodList = new LinkedList<MethodDeclaration>(); List<MethodDeclaration> tempMethodList; // Get all CompilationUnits and look for MethodDeclarations in each Set<ICompilationUnit> compUnits = compilationUnitToASTNode.keySet(); Iterator<ICompilationUnit> compUnitIterator = compUnits.iterator(); ICompilationUnit icu; for (; compUnitIterator.hasNext();) { icu = compUnitIterator.next(); tempMethodList = scanForMethodDeclarationsFromAST(compilationUnitToASTNode.get(icu)); methodList.addAll(tempMethodList); } return methodList; } /** * Collects all top level methods from an AST including embedded methods. * * @param node the root of an AST * @return all top level methods within the AST */ public static List<MethodDeclaration> scanForMethodDeclarationsFromAST(ASTNode node) { if (node == null) throw new CrystalRuntimeException("AST tree not found from ICompilationUnit"); // Visitor Class class MethodFindVisitor extends ASTVisitor { List<MethodDeclaration> methodList; public MethodFindVisitor(List<MethodDeclaration> inMethodList) { methodList = inMethodList; } // Visit MethodDeclarations public boolean visit(MethodDeclaration methodDeclaration) { methodList.add(methodDeclaration); // false returns us back, instead of traversing further down return true; } } // Create an empty list, populate methods by traversing using the visitor List<MethodDeclaration> methodList = new LinkedList<MethodDeclaration>(); MethodFindVisitor visitor = new MethodFindVisitor(methodList); node.accept(visitor); return methodList; } public static Map<String, ASTNode> scanForBindings(Map<ICompilationUnit, ASTNode> compilationUnitToASTNode) { if (compilationUnitToASTNode == null) throw new CrystalRuntimeException("null map of compilation units to ASTNodes"); Map<String, ASTNode> bindings = new HashMap<String, ASTNode>(); // Get all CompilationUnits and look for MethodDeclarations in each Set<ICompilationUnit> compUnits = compilationUnitToASTNode.keySet(); Iterator<ICompilationUnit> compUnitIterator = compUnits.iterator(); ICompilationUnit icu; for (; compUnitIterator.hasNext();) { icu = compUnitIterator.next(); ASTNode node = compilationUnitToASTNode.get(icu); node.accept(new BindingsCollectorVisitor(bindings)); } return bindings; } public static Map<String, ASTNode> scanForBindings(ICompilationUnit compUnit, ASTNode node) { Map<String, ASTNode> bindings = new HashMap<String, ASTNode>(); node.accept(new BindingsCollectorVisitor(bindings)); return bindings; } /** * Returns the list of compilation units for a given list of file names. * All compilation units that <i>contain</i> one of the given strings are * returned. * @param files List of file names to search for. They will be compared to * the result of {@link #getWorkspaceRelativeName(IJavaElement)}. * @return List of compilation units for a given list of file names. */ public static List<ICompilationUnit> findCompilationUnits(List<String> files) { List<ICompilationUnit> allCompUnits = WorkspaceUtilities.scanForCompilationUnits(); int foundCount = 0; ICompilationUnit[] resultArray = new ICompilationUnit[files.size()]; for (ICompilationUnit compUnit : allCompUnits) { String relativeName = getWorkspaceRelativeName(compUnit); for (int i = 0; i < files.size(); i++) { if (relativeName.indexOf(files.get(i)) >= 0) { resultArray[i] = compUnit; ++foundCount; } } } List<ICompilationUnit> result; if (foundCount == files.size()) result = Arrays.asList(resultArray); else { result = new ArrayList<ICompilationUnit>(foundCount); for (ICompilationUnit compUnit : resultArray) { if (compUnit != null) result.add(compUnit); } } return result; } /** * Walks up the Java model hierarchy and separates the names of encountered * elements by forward slashes * @param element * @return Symbolic name of the given Java element relative to the workspace root */ public static String getWorkspaceRelativeName(IJavaElement element) { String result = element.getElementName(); while (element.getParent() != null) { element = element.getParent(); result = element.getElementName() + "/" + result; } return result; } /** * Gets the root ASTNode for a compilation unit, with bindings on. * @param compUnit * @return the root ASTNode for a compilation unit, with bindings on. */ public static ASTNode getASTNodeFromCompilationUnit(ICompilationUnit compUnit) { ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setResolveBindings(true); parser.setSource(compUnit); return parser.createAST(/* passing in monitor messes up previous monitor state */ null); } /** * Given an IType from the model, this method will return the ast node * associated with that type, or null if it doesn't exist. * @return NONE if the ast node associated with this type could not * be found. */ public static Option<TypeDeclaration> getDeclNodeFromType(final IType type) { return findNodeForModel(type, TypeDeclaration.class, new Lambda<TypeDeclaration, Boolean>() { public Boolean call(TypeDeclaration i) { return i.resolveBinding().getJavaElement().equals(type); } }); } /** * Returns the AST node associated with the given model element, which in this case * is a method. * @param method * @return */ public static Option<MethodDeclaration> getMethodDeclFromModel(final IMethod method) { return findNodeForModel(method, MethodDeclaration.class, new Lambda<MethodDeclaration, Boolean>() { public Boolean call(MethodDeclaration i) { return i.resolveBinding().getMethodDeclaration().getJavaElement().equals(method); } }); } /** * Return the ast node associated with the given model element. * @return */ private static <NODETYPE extends ASTNode> Option<NODETYPE> findNodeForModel(IMember model_element, final Class<? extends NODETYPE> clazz, final Lambda<NODETYPE, Boolean> isCorrectNode) { ICompilationUnit comp_unit = model_element.getCompilationUnit(); ASTNode node = getASTNodeFromCompilationUnit(comp_unit); // Now, find the corresponding type node final Box<NODETYPE> result = new Box<NODETYPE>(null); node.accept(new ASTVisitor() { @Override public void postVisit(ASTNode node) { // If this node is a subtype of the node we are // interested in, then we can ask the client's // function if this is the right instance. if (clazz.isAssignableFrom(node.getClass())) { @SuppressWarnings("unchecked") NODETYPE node2 = (NODETYPE) node; if (isCorrectNode.call(node2)) { result.setValue(node2); } } } }); if (result.getValue() == null) return Option.none(); else return Option.some(result.getValue()); } } class BindingsCollectorVisitor extends ASTVisitor { Map<String, ASTNode> bindings = null; public BindingsCollectorVisitor(Map<String, ASTNode> bindingsIn) { bindings = bindingsIn; } protected void addNewBinding(IBinding binding, ASTNode node) { if (binding == null) return; if (bindings == null) throw new CrystalRuntimeException("BindingsCollectorVisitor::addNewBinding: Unexpected null mapping"); if (bindings.containsKey(binding)) { throw new CrystalRuntimeException( "BindingsCollectorVisitor::addNewBinding: Readding existing binding. This is a framework error."); } bindings.put(binding.getKey(), node); } public static String getSimpleClassName(String className) { String simpleName; try { simpleName = className.substring(className.lastIndexOf('.') + 1); } catch (Exception e) { simpleName = className; } return simpleName; } public boolean visit(AnonymousClassDeclaration node) { addNewBinding(node.resolveBinding(), node); return true; } public boolean visit(EnumConstantDeclaration node) { addNewBinding(node.resolveVariable(), node); return true; } public boolean visit(EnumDeclaration node) { addNewBinding(node.resolveBinding(), node); return true; } // FieldDeclaration - handled by VariableDeclarationFragment public boolean visit(ImportDeclaration node) { addNewBinding(node.resolveBinding(), node); return true; } public boolean visit(MethodDeclaration node) { addNewBinding(node.resolveBinding(), node); return true; } public boolean visit(PackageDeclaration node) { addNewBinding(node.resolveBinding(), node); return true; } public boolean visit(SingleVariableDeclaration node) { addNewBinding(node.resolveBinding(), node); return true; } public boolean visit(TypeDeclarationStatement node) { addNewBinding(node.resolveBinding(), node); return true; } public boolean visit(VariableDeclaration node) { addNewBinding(node.resolveBinding(), node); return true; } public boolean visit(VariableDeclarationFragment node) { addNewBinding(node.resolveBinding(), node); return true; } }