| /******************************************************************************* |
| * Copyright (c) 2005, 2012 IBM Corporation and others. |
| * 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 org.eclipse.jdt.debug.tests.refactoring; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.internal.core.SourceMethod; |
| import org.eclipse.jdt.internal.core.SourceType; |
| |
| /** |
| * Contains methods to find an IMember within a given path subdivided by the '$' character. |
| * Syntax: |
| * Type$InnerType$MethodNameAndSignature$AnonymousTypeDeclarationNumber$FieldName |
| * eg:<code> |
| * public class Foo{ |
| * class Inner |
| * { |
| * public void aMethod() |
| * { |
| * Object anon = new Object(){ |
| * int anIntField; |
| * String anonTypeMethod() {return "an Example";} |
| * } |
| * } |
| * } |
| * }</code> |
| * Syntax to get anIntField would be: Foo$Inner$aMethod()V$1$anIntField |
| * Syntax to get the anonymous toString would be: Foo$Inner$aMethod()V$1$anonTypeMethod()QString |
| * In the case of local types, the listed syntax should be Count and then Name, like: CountName |
| * eg:<code>1MyType</code> |
| */ |
| public class MemberParser{ |
| |
| /** |
| * @param typeQualifiedName |
| * @return |
| */ |
| private static ArrayList<String> createTypeList(String typeQualifiedName) { |
| String newname = typeQualifiedName; |
| newname = newname.replace('$','.');//ensure proper format was used. |
| String parsed[] = newname.split("\\."); //$NON-NLS-1$ |
| //make list of types to find |
| ArrayList<String> typeList = new ArrayList<>(); |
| for (int splitNum = 0; splitNum < parsed.length; splitNum++) { |
| typeList.add(parsed[splitNum]); |
| } |
| return typeList; |
| } |
| /** |
| * @param fragments the scope of which you wish to return compilation units |
| * @return a handle to all compilation units contained by the given fragments |
| * @throws JavaModelException |
| */ |
| private static ICompilationUnit[] getAllCompilationUnits(IPackageFragment[] fragments) throws JavaModelException { |
| if(fragments == null) |
| return null; |
| final Set<ICompilationUnit> results = new HashSet<>(); |
| for (int fragmentNum = 0; fragmentNum < fragments.length; fragmentNum++) { |
| if(fragments[fragmentNum].containsJavaResources()){ |
| ICompilationUnit cunits[] = fragments[fragmentNum].getCompilationUnits(); |
| for (int cunitNum = 0; cunitNum < cunits.length; cunitNum++) { |
| results.add(cunits[cunitNum]); |
| } |
| } |
| } |
| if(results.isEmpty()) |
| return null; |
| return results.toArray(new ICompilationUnit[results.size()]); |
| } |
| |
| /** |
| * @param projects the scope of which you wish to return compilation units |
| * @return a handle to all compilation units contained by the given projects |
| * @throws JavaModelException |
| */ |
| private static ICompilationUnit[] getAllCompilationUnits(IProject[] projects) throws JavaModelException{ |
| return getAllCompilationUnits(getAllPackageFragments(projects)); |
| } |
| |
| private static ICompilationUnit[] getAllCompilationUnits(String packageName, IProject[] projects)throws JavaModelException { |
| return getAllCompilationUnits(getAllPackageFragments(packageName, projects)); |
| } |
| |
| /** |
| * @param types |
| * @return an array of all declared methods for the given types |
| * @throws JavaModelException |
| */ |
| private static IMethod[] getAllMethods(IType[] types) throws JavaModelException{ |
| if(types==null) |
| return null; |
| |
| final Set<IMethod> results = new HashSet<>(); |
| for (int typeNum = 0; typeNum < types.length; typeNum++) { |
| IMethod[] methods = types[typeNum].getMethods(); |
| for (int methodNum = 0; methodNum < methods.length; methodNum++) { |
| results.add(methods[methodNum]); |
| } |
| } |
| if(results.isEmpty()) |
| return null; |
| return results.toArray(new SourceMethod[results.size()]); |
| } |
| |
| /** |
| * @param projects the scope of the return |
| * @return all package fragments in the scope |
| * @throws JavaModelException |
| */ |
| private static IPackageFragment[] getAllPackageFragments(IProject[] projects) throws JavaModelException { |
| final Set<IPackageFragment> results = new HashSet<>(); |
| for (int projectNum = 0; projectNum < projects.length; projectNum++) { |
| IJavaProject javaProj = JavaCore.create(projects[projectNum]); |
| if(javaProj!= null && javaProj.exists() && javaProj.hasChildren()){ |
| IPackageFragment fragments[] = javaProj.getPackageFragments(); |
| for (int fragmentNum = 0; fragmentNum < fragments.length; fragmentNum++) { |
| results.add(fragments[fragmentNum]); |
| } |
| } |
| } |
| if(results.isEmpty()) |
| return null; |
| return results.toArray(new IPackageFragment[results.size()]); |
| } |
| /** |
| * @return all projects in the workspace |
| */ |
| private static IProject[] getAllProjects(){ |
| return ResourcesPlugin.getWorkspace().getRoot().getProjects(); |
| } |
| |
| /** |
| * @param cunits the scope of the search |
| * @return all types within the scope |
| * @throws JavaModelException |
| */ |
| private static IType[] getAllTypes(ICompilationUnit[] cunits) throws JavaModelException { |
| if(cunits == null) |
| return null; |
| |
| final Set<IType> results = new HashSet<>(); |
| for (int cunitNum = 0; cunitNum < cunits.length; cunitNum++) { |
| IType types[] = cunits[cunitNum].getTypes(); //get all topLevel types |
| for (int typeNum = 0; typeNum < types.length; typeNum++) { |
| results.add(types[typeNum]); |
| } |
| } |
| if(results.isEmpty()) |
| return null; |
| return results.toArray(new IType[results.size()]); |
| } |
| |
| /** |
| * @param methods the scope of the search |
| * @return an array of all types declared within the given methods. |
| * @throws JavaModelException |
| */ |
| private static IType[] getAllTypes(IMethod[] methods) throws JavaModelException { |
| if(methods==null) |
| return null; |
| final Set<IJavaElement> results = new HashSet<>(); |
| for (int methodNum = 0; methodNum < methods.length; methodNum++) { |
| IJavaElement[] children = methods[methodNum].getChildren(); |
| for (int childNum = 0; childNum < children.length; childNum++) { |
| if(children[childNum] instanceof IType) |
| results.add(children[childNum]); |
| } |
| } |
| if(results.isEmpty()) |
| return null; |
| return results.toArray(new SourceType[results.size()]); |
| } |
| |
| /**Will search within the given type and all of it's children - including methods |
| * and anonymous types for other types. |
| * @param types the scope of the search |
| * @return all types within the given scope |
| * @throws JavaModelException |
| */ |
| public static IType[] getAllTypes(IType[] types) throws JavaModelException{ |
| if(types == null) |
| return null; |
| IType[] newtypes = types; |
| final Set<IType> results = new HashSet<>(); |
| //get all the obvious type declarations |
| for (int mainTypeNum = 0; mainTypeNum < newtypes.length; mainTypeNum++) { |
| IType declaredTypes[] = newtypes[mainTypeNum].getTypes(); |
| for (int declaredTypeNum = 0; declaredTypeNum < declaredTypes.length; declaredTypeNum++) { |
| results.add(declaredTypes[declaredTypeNum]); |
| } |
| //get all the type's method's type declarations |
| newtypes = getAllTypes(getAllMethods(newtypes)); |
| for (int methodTypes = 0; methodTypes < newtypes.length; methodTypes++) { |
| results.add(newtypes[methodTypes]); |
| } |
| } |
| if(results.isEmpty()) |
| return null; |
| //else |
| return results.toArray(new SourceType[results.size()]);//possibly change to new IType |
| } |
| |
| |
| /** |
| * Returns the Java project with the given name. |
| * |
| * @param name project name |
| * @return the Java project with the given name |
| */ |
| static protected IJavaProject getJavaProject() { |
| IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(); |
| return JavaCore.create(project); |
| } |
| |
| /** |
| * @param packageName name of the package |
| * @param projects where to search |
| * @return the 1st instance of the given packageName |
| * @throws JavaModelException |
| */ |
| private static IPackageFragment[] getAllPackageFragments(String packageName, IProject[] projects) throws JavaModelException{ |
| final Set<IPackageFragment> results = new HashSet<>(); |
| for (int projectNum = 0; projectNum < projects.length; projectNum++) { |
| IJavaProject javaProj = JavaCore.create(projects[projectNum]); |
| if(javaProj!= null && javaProj.exists() && javaProj.hasChildren()){ |
| IPackageFragment fragments[] = javaProj.getPackageFragments(); |
| for (int fragmentNum = 0; fragmentNum < fragments.length; fragmentNum++) { |
| if(fragments[fragmentNum].getElementName().equalsIgnoreCase(packageName)) |
| results.add(fragments[fragmentNum]); |
| } |
| } |
| } |
| if(results.isEmpty()) |
| return null; |
| //else |
| return results.toArray(new IPackageFragment[results.size()]); |
| } |
| |
| private static IProject getProject(String projectName){ |
| return ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); |
| } |
| |
| private static IType getType(ArrayList<String> typeList, ICompilationUnit[] cunits) throws JavaModelException{ |
| IType[] types = getAllTypes(cunits); |
| //if 1st letter is a number, it's anonymous |
| boolean targetIsAnonymous = Character.isDigit(typeList.get(0).toString().charAt(0)); |
| boolean targetFound=false; |
| char separator = '.'; |
| while (true) {//search all types for desired target - will return internally. |
| //check all the types we have |
| int typeNum=0; |
| while(typeNum < types.length) {//search current list of types |
| if(targetIsAnonymous){//must ensure format is same for both. |
| String nameOfCurrentType = types[typeNum].getTypeQualifiedName(separator); |
| nameOfCurrentType = nameOfCurrentType.substring(nameOfCurrentType.lastIndexOf(separator)+1); |
| targetFound = nameOfCurrentType.equalsIgnoreCase(typeList.get(0).toString()); |
| }else{ |
| targetFound = types[typeNum].getElementName().equalsIgnoreCase(typeList.get(0).toString()); |
| } |
| if(targetFound){//yay! |
| typeList.remove(0); |
| if(typeList.isEmpty()){ |
| return types[typeNum];//we're at our destination |
| } |
| //else, get all this type's subtypes |
| types = getAllTypes(new IType[]{types[typeNum]});//get next level |
| // check format of this new type |
| targetIsAnonymous = Character.isDigit(typeList.get(0).toString().charAt(0)); |
| typeNum = 0;//start again. |
| } |
| else |
| typeNum++;//check the next type |
| } |
| |
| //else, it is not in the top-level types - check in methods |
| types = getAllTypes(getAllMethods(types)); |
| if(types==null) |
| return null;//couldn't find it. |
| }//end while |
| } |
| |
| /** |
| * Will search the workspace and return the requested type. The more information given, |
| * the faster the search |
| * @param typeName the name of the type, with or without qualifiers - it cannot be null |
| * e.g. "aType.innerType.1.typeInAnonymousType" or even just "typeInAnonymousType" |
| * or "innerType.1.typeInAnonymousType". |
| * @param packageName the elemental name of the package containing the given type - may be null |
| * @param projectName the elemental name of the project containing the given type - may be null |
| * @return the IType handle to the requested type |
| * @throws JavaModelException |
| */ |
| public static IType getType(String typeName, String packageName, String projectName) throws JavaModelException{ |
| if(typeName == null) |
| return null; |
| //make list of types to find, in order |
| ArrayList<String> typeList = createTypeList(typeName); |
| //get the proper project(s) |
| IProject[] projects=null; |
| if(projectName!=null && projectName.length()>0){ |
| projects = new IProject[] {getProject(projectName)}; |
| } |
| else{ |
| projects = getAllProjects(); |
| } |
| |
| //get the Comp.units for those projects |
| ICompilationUnit cunits[] = null; |
| if(packageName!=null && packageName.length()>0){ |
| cunits = getAllCompilationUnits(packageName, projects); |
| } |
| else{ |
| cunits = getAllCompilationUnits(projects); |
| } |
| |
| return getType(typeList, cunits); |
| } |
| |
| |
| /** |
| * @param cu the CompilationUnit containing the toplevel Type |
| * @param target - the IMember target, listed in full Syntax, as noted in MemberParser |
| * eg: EnclosingType$InnerType |
| * @return the Lowest level inner type specified in input |
| */ |
| public IMember getDeepest(ICompilationUnit cu, String target) |
| { |
| for(int i=0;i<target.length();i++) |
| { |
| if(target.charAt(i)=='$') |
| {//EnclosingType$InnerType$MoreInner |
| String tail = target.substring(i+1); |
| IType enclosure = cu.getType(target.substring(0, i)); |
| if(enclosure.exists()) |
| return getDeepest(enclosure,tail); |
| } |
| } |
| //has no inner type |
| return cu.getType(target); |
| |
| } |
| |
| /** |
| * Helper method for getLowestType (ICompilationUnit cu, String input) |
| * @param top name of enclosing Type |
| * @param tail the typename, possibly including inner type, |
| * separated by $. |
| * eg: EnclosingType$InnerType |
| * @return the designated type, or null if type not found. |
| */ |
| protected IMember getDeepest(IMember top, String tail) { |
| String newtail = tail; |
| if(newtail==null || newtail.length()==0 ) |
| return top; |
| |
| if(!top.exists()) |
| return null; |
| |
| //check if there are more nested elements |
| String head=null; |
| for(int i=0;i<newtail.length();i++) |
| { |
| if(newtail.charAt(i)=='$')//nested Item? |
| {//Enclosing$Inner$MoreInner |
| head = newtail.substring(0,i); |
| newtail = newtail.substring(i+1); |
| break;//found next item |
| } |
| } |
| if(head==null)//we are at last item to parse |
| {//swap Members |
| head = newtail; |
| newtail = null; |
| } |
| |
| if(top instanceof IType) |
| return getNextFromType(top, head, newtail); |
| else |
| if(top instanceof IMethod) |
| return getNextFromMethod(top, head, newtail); |
| else |
| if(top instanceof IField) |
| return getNextFromField(top, head, newtail); |
| //else there is a problem! |
| return getDeepest(top,newtail); |
| } |
| |
| /** |
| * @param head the string to parse for a name |
| * @return the name in the type, given in the format "Occurance#Type" |
| * e.g. head = "1Type"; |
| */ |
| protected String getLocalTypeName(String head) { |
| for(int i=0;i<head.length();i++) |
| { |
| if(!Character.isDigit(head.charAt(i))) |
| { |
| return head.substring(i); |
| } |
| |
| } |
| return IInternalDebugCoreConstants.EMPTY_STRING;//entire thing is a number |
| } |
| |
| /** |
| * @param head the string to parse for an occurrence |
| * @return the name in the type, given in the format "Occurance#Type" |
| * e.g. head = "1Type"; |
| */ |
| protected int getLocalTypeOccurrence(String head) { |
| for(int i=0;i<head.length();i++) |
| { |
| if(!Character.isDigit(head.charAt(i))) |
| return Integer.parseInt(head.substring(0, i)); |
| } |
| return Integer.parseInt(head);//entire thing is a number |
| } |
| |
| /** |
| * @param head name of method w/ signature at the end |
| * @return simply the name of the given method, using format: |
| * methodNameSignature. |
| * e.g. head = "someMethod()V" |
| */ |
| protected String getName(String head) { |
| for(int i=0;i<head.length();i++) |
| { |
| if(head.charAt(i)=='(')//nested Item? |
| return head.substring(0,i); |
| } |
| return null; |
| } |
| |
| /** |
| * @param top the field in which to search |
| * @param head the next member to find |
| * @param tail the remaining members to find |
| * @return the next member down contained by the given Field |
| */ |
| protected IMember getNextFromField(IMember top, String head, String tail) { |
| IField current = (IField)top; |
| |
| IType type = current.getType(getLocalTypeName(head),getLocalTypeOccurrence(head)); |
| if(type.exists()) |
| return getDeepest(type,tail); |
| //else |
| return null;//something failed. |
| } |
| |
| /** |
| * @param top the member in which to search |
| * @param head the next member to find |
| * @param tail the remaining members to find |
| * @return the next member down contained by the given Method |
| */ |
| protected IMember getNextFromMethod(IMember top, String head, String tail) { |
| //must be a local or anonymous type |
| IMethod current = (IMethod)top; |
| |
| //is next part a Type? |
| IType type = current.getType(getLocalTypeName(head), getLocalTypeOccurrence(head)); |
| if(type.exists()) |
| return getDeepest(type,tail); |
| //else |
| return null; |
| } |
| |
| /** |
| * @param top the member in which to search |
| * @param head the next member to find |
| * @param tail the remaining members to find |
| * @return the next member down contained by the given Type |
| */ |
| protected IMember getNextFromType(IMember top, String head, String tail) { |
| IType current = (IType)top; |
| |
| //is next part a Type? |
| IMember next = current.getType(head); |
| if(next.exists()) |
| return getDeepest(next,tail); |
| //else, is next part a Field? |
| next = current.getField(head); |
| if(next.exists()) |
| return getDeepest(next,tail); |
| //else, is next part a Method? |
| next = current.getMethod(getName(head),getSignature(head)); |
| if(next.exists()) |
| return getDeepest(next,tail); |
| //else |
| return null;//something failed. |
| } |
| |
| /** |
| * @param head name of method w/ signature at the end |
| * @return simply the ParameterTypeSignature, using format: |
| * methodNameSignature. |
| * e.g. head = "someMethod()V" |
| */ |
| protected String[] getSignature(String head) { |
| for(int i=0;i<head.length();i++) |
| { |
| if(head.charAt(i)=='(')//nested Item? |
| return Signature.getParameterTypes(head.substring(i)); |
| } |
| return null; |
| } |
| } |