/******************************************************************************* * Copyright (c) 2002 - 2006 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package com.ibm.wala.ide.util; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaModel; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeParameter; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; 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.NodeFinder; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; import org.eclipse.jface.viewers.StructuredSelection; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.collections.HashSetFactory; /** * Convenience methods to get information from JDT {@link IJavaElement} model. */ public class JdtUtil { public static String getFilePath(IJavaElement javaElt) { if (javaElt == null) { throw new IllegalArgumentException("javaElt is null"); } try { IPath p = javaElt.getPath(); if (p == null) { throw new IllegalArgumentException("javaElt has null path"); } String filePath = p.toString(); return filePath; } catch (Exception e) { throw new IllegalArgumentException("malformed javaElt: " + javaElt, e); } } public static String getPackageName(ICompilationUnit cu) { if (cu == null) { throw new IllegalArgumentException("cu is null"); } try { IPackageDeclaration[] pkgDecl = cu.getPackageDeclarations(); // TODO: handle default package? if (pkgDecl != null && pkgDecl.length > 0) { String packageName = pkgDecl[0].getElementName(); return packageName; } } catch (JavaModelException e) { } return ""; } public static String getFullyQualifiedClassName(IType type) { if (type == null) { throw new IllegalArgumentException("type is null"); } ICompilationUnit cu = (ICompilationUnit) type.getParent(); String packageName = getPackageName(cu); String className = type.getElementName(); String fullyQName = packageName + "." + className; return fullyQName; } public static String getClassName(IType type) { if (type == null) { throw new IllegalArgumentException("type is null"); } String className = type.getElementName(); return className; } /** * Return a unique string representing the specified Java element across projects in the workspace. The returned string can be * used as a handle to create JavaElement by 'JavaCore.create(String)' * * For example, suppose we have the method 'fooPackage.barPackage.FooClass.fooMethod(int)' which is in the 'FooProject' and source * folder 'src' the handle would be '=FooProject/src<fooPackage.barPackage{FooClass.java[FooClass~fooMethod~I' * * @param javaElt * @throws IllegalArgumentException if javaElt is null */ public static String getJdtHandleString(IJavaElement javaElt) { if (javaElt == null) { throw new IllegalArgumentException("javaElt is null"); } return javaElt.getHandleIdentifier(); } @Deprecated public static IJavaElement createJavaElementFromJdtHandle(String jdtHandle) { return JavaCore.create(jdtHandle); } public static IType[] getClasses(ICompilationUnit cu) { if (cu == null) { throw new IllegalArgumentException("cu is null"); } try { return cu.getAllTypes(); } catch (JavaModelException e) { } return null; } public static IJavaProject getProject(IJavaElement javaElt) { if (javaElt == null) { throw new IllegalArgumentException("javaElt is null"); } IJavaProject javaProject = javaElt.getJavaProject(); return javaProject; } public static String getProjectName(IJavaProject javaProject) { if (javaProject == null) { throw new IllegalArgumentException("javaProject is null"); } return javaProject.getElementName(); } /** * @param typeSignature Some of the type signatures examples are "QString;" (String) and "I" (int) The type signatures may be * either unresolved (for source types) or resolved (for binary types), and either basic (for basic types) or rich (for * parameterized types). See {@link Signature} for details. */ public static String getHumanReadableType(String typeSignature) { String simpleName = Signature.getSignatureSimpleName(typeSignature); return simpleName; } public static IJavaProject getJavaProject(IFile appJar) { if (appJar == null) { throw new IllegalArgumentException("appJar is null"); } String projectName = appJar.getProject().getName(); return getJavaProject(projectName); } public static IJavaProject getJavaProject(String projectName) { if (projectName == null) { throw new IllegalArgumentException("null projectName"); } IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); IJavaModel javaModel = JavaCore.create(workspaceRoot); IJavaProject javaProject = javaModel.getJavaProject(projectName); return javaProject; } /** * compute the java projects in the active workspace */ public static Collection<IJavaProject> getWorkspaceJavaProjects() { Collection<IJavaProject> result = HashSetFactory.make(); IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); for (IProject p : workspaceRoot.getProjects()) { try { if (p.hasNature(JavaCore.NATURE_ID)) { IJavaProject jp = JavaCore.create(p); if (jp != null) { result.add(jp); } } } catch (CoreException e) { // do nothing } } return result; } /** * Find the {@link IType} in the workspace corresponding to a class name. * * @return null if not found * @throws IllegalArgumentException if projects == null */ public static IType findJavaClassInProjects(String fullyQualifiedName, Collection<IJavaProject> projects) throws IllegalArgumentException { if (projects == null) { throw new IllegalArgumentException("projects == null"); } for (IJavaProject project : projects) { try { IType t = project.findType(fullyQualifiedName); if (t != null) { return t; } } catch (JavaModelException e) { e.printStackTrace(); } } System.err.println("failed to find " + fullyQualifiedName); return null; // IJavaElement[] arr = new IJavaElement[projects.size()]; // projects.toArray(arr); // IJavaSearchScope scope = SearchEngine.createJavaSearchScope(arr, false); // // return searchForJavaClass(className, scope); } public static IType findJavaClassInResources(String className, Collection<IResource> resources) { if (resources == null) { throw new IllegalArgumentException("null resources"); } Collection<IJavaProject> projects = HashSetFactory.make(); for (IResource r : resources) { projects.add(JavaCore.create(r).getJavaProject()); } return findJavaClassInProjects(className, projects); } // private static IType searchForJavaClass(String className, IJavaSearchScope scope) { // SearchPattern p = SearchPattern.createPattern(className, IJavaSearchConstants.CLASS_AND_INTERFACE, // IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH); // SearchEngine engine = new SearchEngine(); // final Collection<IJavaElement> kludge = HashSetFactory.make(); // SearchRequestor requestor = new SearchRequestor() { // // @Override // public void acceptSearchMatch(SearchMatch match) throws CoreException { // kludge.add((IJavaElement) match.getElement()); // } // // }; // try { // engine.search(p, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, scope, requestor, null); // } catch (CoreException e) { // e.printStackTrace(); // } // if (kludge.size() == 1) { // System.err.println("Found " + className); // return (IType) kludge.iterator().next(); // } else { // System.err.println("Failed to find " + className + " " + kludge.size()); // return null; // } // } /** * Find the IMethod in the workspace corresponding to a method selector. * * TODO: this is way too slow. figure out something better. * * @return null if not found */ public static IMethod findJavaMethodInProjects(String klass, String selector, Collection<IJavaProject> projects) { if (klass == null) { throw new IllegalArgumentException("null klass"); } if (projects == null) { throw new IllegalArgumentException("null projects"); } IType type = null; try { type = findJavaClassInProjects(klass, projects); } catch (Throwable t) { return null; } if (type == null) { return null; } String name = parseForName(selector, type); String[] paramTypes = parseForParameterTypes(selector); IMethod m = type.getMethod(name, paramTypes); IMethod[] methods = type.findMethods(m); if (methods != null && methods.length == 1) { return methods[0]; } else { // methods is null. probably got screwed by generics. // i spent 5 hours trying to figure out how to fix this correctly // and failed. implementing a miserable hack instead. // Need to consult a guru to figure out how to do this. try { List<IMethod> matches = new ArrayList<IMethod>(); Collection<String> typeParameterNames = getTypeParameterNames(type); METHODS: for (IMethod x : type.getMethods()) { if (x.getElementName().equals(name)) { if (x.getParameterTypes().length == paramTypes.length) { for (int i = 0; i < x.getParameterTypes().length; i++) { String s1 = Signature.getTypeErasure(Signature.getSignatureSimpleName(x.getParameterTypes()[i])); String s2 = Signature.getTypeErasure(Signature.getSignatureSimpleName(paramTypes[i])); if (typeParameterNames.contains(s1)) { // s1 is a type parameter to the class. optimistically assume // the types match. } else { if (!s1.equals(s2)) { // no match continue METHODS; } } } matches.add(x); } } } if (matches.size() == 1) { return matches.get(0); } else { System.err.println("findJavaMethodInWorkspace FAILED TO MATCH " + m); return null; } } catch (JavaModelException e) { e.printStackTrace(); return null; } } } public static Collection<String> getTypeParameterNames(IType type) throws IllegalArgumentException, JavaModelException { if (type == null) { throw new IllegalArgumentException("type == null"); } ITypeParameter[] tp = type.getTypeParameters(); Collection<String> typeParameterNames = HashSetFactory.make(tp.length); for (ITypeParameter p : tp) { typeParameterNames.add(p.getElementName()); } return typeParameterNames; } public static String parseForName(String selector, IType type) { if (selector == null) { throw new IllegalArgumentException("selector is null"); } try { String result = selector.substring(0, selector.indexOf('(')); if (result.equals("<init>")) { return type.getElementName(); } else { return result; } } catch (StringIndexOutOfBoundsException e) { throw new IllegalArgumentException("invalid selector: " + selector); } } public static final String[] parseForParameterTypes(String selector) throws IllegalArgumentException { try { if (selector == null) { throw new IllegalArgumentException("selector is null"); } String d = selector.substring(selector.indexOf('(')); if (d.length() <= 2) { throw new IllegalArgumentException("invalid descriptor: " + d); } if (d.charAt(0) != '(') { throw new IllegalArgumentException("invalid descriptor: " + d); } ArrayList<String> sigs = new ArrayList<String>(10); int i = 1; while (true) { switch (d.charAt(i++)) { case TypeReference.VoidTypeCode: sigs.add(TypeReference.VoidName.toString()); continue; case TypeReference.BooleanTypeCode: sigs.add(TypeReference.BooleanName.toString()); continue; case TypeReference.ByteTypeCode: sigs.add(TypeReference.ByteName.toString()); continue; case TypeReference.ShortTypeCode: sigs.add(TypeReference.ShortName.toString()); continue; case TypeReference.IntTypeCode: sigs.add(TypeReference.IntName.toString()); continue; case TypeReference.LongTypeCode: sigs.add(TypeReference.LongName.toString()); continue; case TypeReference.FloatTypeCode: sigs.add(TypeReference.FloatName.toString()); continue; case TypeReference.DoubleTypeCode: sigs.add(TypeReference.DoubleName.toString()); continue; case TypeReference.CharTypeCode: sigs.add(TypeReference.CharName.toString()); continue; case TypeReference.ArrayTypeCode: { int off = i - 1; while (d.charAt(i) == TypeReference.ArrayTypeCode) { ++i; } if (d.charAt(i++) == TypeReference.ClassTypeCode) { while (d.charAt(i++) != ';') ; sigs.add(d.substring(off, i).replaceAll("/", ".")); } else { sigs.add(d.substring(off, i)); } continue; } case (byte) ')': // end of parameter list return toArray(sigs); default: { // a class int off = i - 1; char c; do { c = d.charAt(i++); } while (c != ',' && c != ')'); sigs.add("L" + d.substring(off, i - 1) + ";"); if (c == ')') { return toArray(sigs); } continue; } } } } catch (StringIndexOutOfBoundsException e) { throw new IllegalArgumentException("error parsing selector " + selector); } } private static String[] toArray(ArrayList<String> sigs) { int size = sigs.size(); if (size == 0) { return new String[0]; } Iterator<String> it = sigs.iterator(); String[] result = new String[size]; for (int j = 0; j < size; j++) { result[j] = it.next(); } return result; } /** * Find the IMethod in the workspace corresponding to a method signature. * * This doesn't work for elements declared in inner classes. It's possible this is a 3.2 bug fixed in 3.3 * * @return null if not found */ @Deprecated public static IMethod findJavaMethodInWorkspaceBrokenForInnerClasses(String methodSig) { // dammit ... this doesn't work for inner classes. System.err.println("Search for " + methodSig); SearchPattern p = SearchPattern.createPattern(methodSig, IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH); IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); SearchEngine engine = new SearchEngine(); final Collection<IJavaElement> kludge = HashSetFactory.make(); SearchRequestor requestor = new SearchRequestor() { @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { kludge.add((IJavaElement) match.getElement()); } }; try { engine.search(p, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, scope, requestor, null); } catch (CoreException e) { e.printStackTrace(); } if (kludge.size() == 1) { return (IMethod) kludge.iterator().next(); } else { System.err.println("RETURNED " + kludge.size() + " " + kludge); return null; } } /** * Use the search engine to find all methods in a java element */ public static Collection<IMethod> findMethods(IJavaElement elt) { if (elt instanceof ICompilationUnit) { Collection<IMethod> result = HashSetFactory.make(); for (IType type : getClasses((ICompilationUnit) elt)) { try { for (IMethod m : type.getMethods()) { result.add(m); } } catch (JavaModelException e) { e.printStackTrace(); } } return result; } else { final Collection<IMethod> result = HashSetFactory.make(); SearchPattern p = SearchPattern.createPattern("*", IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_PATTERN_MATCH); SearchPattern p2 = SearchPattern.createPattern("*", IJavaSearchConstants.CONSTRUCTOR, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_PATTERN_MATCH); IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] { elt }, IJavaSearchScope.SOURCES); SearchEngine engine = new SearchEngine(); SearchRequestor requestor = new SearchRequestor() { @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { if (match.getElement() instanceof IMethod) { result.add((IMethod) match.getElement()); } } }; try { engine.search(p, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, scope, requestor, null); engine.search(p2, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, scope, requestor, null); } catch (CoreException e) { e.printStackTrace(); } return result; } } /** * get a {@link StructuredSelection} corresponding to the named projects */ public static StructuredSelection getStructuredSelectionForProjectNames(Collection<String> projectNames) { if (projectNames == null) { throw new IllegalArgumentException("null projectNames"); } Object[] projects = new Object[projectNames.size()]; int i = 0; for (String projectName : projectNames) { projects[i++] = getJavaProject(projectName); } StructuredSelection selection = new StructuredSelection(projects); return selection; } public static IJavaProject getNamedProject(String projectName) { IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); IJavaModel javaModel = JavaCore.create(workspaceRoot); IJavaProject helloWorldProject = javaModel.getJavaProject(projectName); return helloWorldProject; } public static ASTNode getAST(IFile javaSourceFile) { ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setSource(JavaCore.createCompilationUnitFrom(javaSourceFile)); parser.setProject(JavaCore.create(javaSourceFile.getProject())); parser.setResolveBindings(true); return parser.createAST(new NullProgressMonitor()); } public static ASTNode getOriginalNode(JdtPosition pos) { ASTNode root = getAST(pos.getEclipseFile()); return getOriginalNode(root, pos); } public static ASTNode getOriginalNode(ASTNode root, JdtPosition pos) { return NodeFinder.perform(root, pos.getFirstOffset(), pos.getLastOffset()-pos.getFirstOffset()); } }