1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.corext.util;
13 import java.util.Arrays;
14 import java.util.HashSet;
15 import java.util.Iterator;
16 import java.util.List;
19 import net.sourceforge.phpdt.core.Flags;
20 import net.sourceforge.phpdt.core.ICompilationUnit;
21 import net.sourceforge.phpdt.core.IField;
22 import net.sourceforge.phpdt.core.IJavaElement;
23 import net.sourceforge.phpdt.core.IJavaProject;
24 import net.sourceforge.phpdt.core.IMember;
25 import net.sourceforge.phpdt.core.IMethod;
26 import net.sourceforge.phpdt.core.IPackageFragment;
27 import net.sourceforge.phpdt.core.IPackageFragmentRoot;
28 import net.sourceforge.phpdt.core.IType;
29 import net.sourceforge.phpdt.core.JavaModelException;
30 import net.sourceforge.phpdt.core.Signature;
31 import net.sourceforge.phpdt.core.compiler.CharOperation;
32 import net.sourceforge.phpeclipse.phpeditor.EditorUtility;
34 import org.eclipse.core.resources.IProject;
35 import org.eclipse.core.resources.IResource;
36 import org.eclipse.core.runtime.CoreException;
37 import org.eclipse.core.runtime.IPath;
38 import org.eclipse.core.runtime.IProgressMonitor;
39 import org.eclipse.core.runtime.Path;
40 import org.eclipse.core.runtime.SubProgressMonitor;
45 * Utility methods for the Java Model.
47 public class JavaModelUtil {
50 * Finds a type by its qualified type name (dot separated).
51 * @param jproject The java project to search in
52 * @param str The fully qualified name (type name with enclosing type names and package (all separated by dots))
53 * @return The type found, or null if not existing
55 // public static IType findType(IJavaProject jproject, String fullyQualifiedName) throws JavaModelException {
56 // //workaround for bug 22883
57 // IType type= jproject.findType(fullyQualifiedName);
60 // IPackageFragmentRoot[] roots= jproject.getPackageFragmentRoots();
61 // for (int i= 0; i < roots.length; i++) {
62 // IPackageFragmentRoot root= roots[i];
63 // type= findType(root, fullyQualifiedName);
64 // if (type != null && type.exists())
71 * Returns <code>true</code> if the given package fragment root is
72 * referenced. This means it is own by a different project but is referenced
73 * by the root's parent. Returns <code>false</code> if the given root
74 * doesn't have an underlying resource.
76 public static boolean isReferenced(IPackageFragmentRoot root) {
77 // IResource resource= root.getResource();
78 // if (resource != null) {
79 // IProject jarProject= resource.getProject();
80 // IProject container= root.getJavaProject().getProject();
81 // return !container.equals(jarProject);
86 // private static IType findType(IPackageFragmentRoot root, String fullyQualifiedName) throws JavaModelException{
87 // IJavaElement[] children= root.getChildren();
88 // for (int i= 0; i < children.length; i++) {
89 // IJavaElement element= children[i];
90 // if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT){
91 // IPackageFragment pack= (IPackageFragment)element;
92 // if (! fullyQualifiedName.startsWith(pack.getElementName()))
94 // IType type= findType(pack, fullyQualifiedName);
95 // if (type != null && type.exists())
102 // private static IType findType(IPackageFragment pack, String fullyQualifiedName) throws JavaModelException{
103 // ICompilationUnit[] cus= pack.getCompilationUnits();
104 // for (int i= 0; i < cus.length; i++) {
105 // ICompilationUnit unit= cus[i];
106 // ICompilationUnit wc= WorkingCopyUtil.getWorkingCopyIfExists(unit);
107 // IType type= findType(wc, fullyQualifiedName);
108 // if (type != null && type.exists())
114 // private static IType findType(ICompilationUnit cu, String fullyQualifiedName) throws JavaModelException{
115 // IType[] types= cu.getAllTypes();
116 // for (int i= 0; i < types.length; i++) {
117 // IType type= types[i];
118 // if (getFullyQualifiedName(type).equals(fullyQualifiedName))
125 * Finds a type by package and type name.
126 * @param jproject the java project to search in
127 * @param pack The package name
128 * @param typeQualifiedName the type qualified name (type name with enclosing type names (separated by dots))
129 * @return the type found, or null if not existing
130 * @deprecated Use IJavaProject.findType(String, String) instead
132 // public static IType findType(IJavaProject jproject, String pack, String typeQualifiedName) throws JavaModelException {
133 // return jproject.findType(pack, typeQualifiedName);
137 * Finds a type container by container name.
138 * The returned element will be of type <code>IType</code> or a <code>IPackageFragment</code>.
139 * <code>null</code> is returned if the type container could not be found.
140 * @param jproject The Java project defining the context to search
141 * @param typeContainerName A dot separarted name of the type container
142 * @see #getTypeContainerName(IType)
144 // public static IJavaElement findTypeContainer(IJavaProject jproject, String typeContainerName) throws JavaModelException {
145 // // try to find it as type
146 // IJavaElement result= jproject.findType(typeContainerName);
147 // if (result == null) {
148 // // find it as package
149 // IPath path= new Path(typeContainerName.replace('.', '/'));
150 // result= jproject.findElement(path);
151 // if (!(result instanceof IPackageFragment)) {
160 * Finds a type in a compilation unit. Typical usage is to find the corresponding
161 * type in a working copy.
162 * @param cu the compilation unit to search in
163 * @param typeQualifiedName the type qualified name (type name with enclosing type names (separated by dots))
164 * @return the type found, or null if not existing
166 public static IType findTypeInCompilationUnit(ICompilationUnit cu, String typeQualifiedName) throws JavaModelException {
167 IType[] types= cu.getAllTypes();
168 for (int i= 0; i < types.length; i++) {
169 String currName= getTypeQualifiedName(types[i]);
170 if (typeQualifiedName.equals(currName)) {
178 * Finds a a member in a compilation unit. Typical usage is to find the corresponding
179 * member in a working copy.
180 * @param cu the compilation unit (eg. working copy) to search in
181 * @param member the member (eg. from the original)
182 * @return the member found, or null if not existing
184 public static IMember findMemberInCompilationUnit(ICompilationUnit cu, IMember member) throws JavaModelException {
185 IJavaElement[] elements= cu.findElements(member);
186 if (elements != null && elements.length > 0) {
187 return (IMember) elements[0];
194 * Returns the element of the given compilation unit which is "equal" to the
195 * given element. Note that the given element usually has a parent different
196 * from the given compilation unit.
198 * @param cu the cu to search in
199 * @param element the element to look for
200 * @return an element of the given cu "equal" to the given element
202 public static IJavaElement findInCompilationUnit(ICompilationUnit cu, IJavaElement element) throws JavaModelException {
203 IJavaElement[] elements= cu.findElements(element);
204 if (elements != null && elements.length > 0) {
211 * Returns the qualified type name of the given type using '.' as separators.
212 * This is a replace for IType.getTypeQualifiedName()
213 * which uses '$' as separators. As '$' is also a valid character in an id
214 * this is ambiguous. JavaCore PR: 1GCFUNT
216 public static String getTypeQualifiedName(IType type) {
217 return type.getTypeQualifiedName('.');
220 private static void getTypeQualifiedName(IType type, StringBuffer buf) {
221 IType outerType= type.getDeclaringType();
222 if (outerType != null) {
223 getTypeQualifiedName(outerType, buf);
226 buf.append(type.getElementName());
230 * Returns the fully qualified name of the given type using '.' as separators.
231 * This is a replace for IType.getFullyQualifiedTypeName
232 * which uses '$' as separators. As '$' is also a valid character in an id
233 * this is ambiguous. JavaCore PR: 1GCFUNT
235 public static String getFullyQualifiedName(IType type) {
236 return type.getFullyQualifiedName('.');
240 * Returns the fully qualified name of a type's container. (package name or enclosing type name)
242 public static String getTypeContainerName(IType type) {
243 IType outerType= type.getDeclaringType();
244 if (outerType != null) {
245 return outerType.getFullyQualifiedName('.');
247 return type.getPackageFragment().getElementName();
253 * Concatenates two names. Uses a dot for separation.
254 * Both strings can be empty or <code>null</code>.
256 public static String concatenateName(String name1, String name2) {
257 StringBuffer buf= new StringBuffer();
258 if (name1 != null && name1.length() > 0) {
261 if (name2 != null && name2.length() > 0) {
262 if (buf.length() > 0) {
267 return buf.toString();
271 * Concatenates two names. Uses a dot for separation.
272 * Both strings can be empty or <code>null</code>.
274 public static String concatenateName(char[] name1, char[] name2) {
275 StringBuffer buf= new StringBuffer();
276 if (name1 != null && name1.length > 0) {
279 if (name2 != null && name2.length > 0) {
280 if (buf.length() > 0) {
285 return buf.toString();
289 * Evaluates if a member (possible from another package) is visible from
290 * elements in a package.
291 * @param member The member to test the visibility for
292 * @param pack The package in focus
294 public static boolean isVisible(IMember member, IPackageFragment pack) throws JavaModelException {
295 int otherflags= member.getFlags();
297 if (Flags.isPublic(otherflags)) {
299 } else if (Flags.isPrivate(otherflags)) {
303 IPackageFragment otherpack= (IPackageFragment) findParentOfKind(member, IJavaElement.PACKAGE_FRAGMENT);
304 return (pack != null && pack.equals(otherpack));
308 * Returns the package fragment root of <code>IJavaElement</code>. If the given
309 * element is already a package fragment root, the element itself is returned.
311 public static IPackageFragmentRoot getPackageFragmentRoot(IJavaElement element) {
312 return (IPackageFragmentRoot) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
316 * Returns the parent of the supplied java element that conforms to the given
317 * parent type or <code>null</code>, if such a parent doesn't exit.
318 * @deprecated Use element.getParent().getAncestor(kind);
320 public static IJavaElement findParentOfKind(IJavaElement element, int kind) {
321 if (element != null && element.getParent() != null) {
322 return element.getParent().getAncestor(kind);
328 * Finds a method in a type.
329 * This searches for a method with the same name and signature. Parameter types are only
330 * compared by the simple name, no resolving for the fully qualified type name is done.
331 * Constructors are only compared by parameters, not the name.
332 * @param name The name of the method to find
333 * @param paramTypes The type signatures of the parameters e.g. <code>{"QString;","I"}</code>
334 * @param isConstructor If the method is a constructor
335 * @return The first found method or <code>null</code>, if nothing found
337 public static IMethod findMethod(String name, String[] paramTypes, boolean isConstructor, IType type) throws JavaModelException {
338 return findMethod(name, paramTypes, isConstructor, type.getMethods());
342 * Finds a method by name.
343 * This searches for a method with a name and signature. Parameter types are only
344 * compared by the simple name, no resolving for the fully qualified type name is done.
345 * Constructors are only compared by parameters, not the name.
346 * @param name The name of the method to find
347 * @param paramTypes The type signatures of the parameters e.g. <code>{"QString;","I"}</code>
348 * @param isConstructor If the method is a constructor
349 * @param methods The methods to search in
350 * @return The found method or <code>null</code>, if nothing found
352 public static IMethod findMethod(String name, String[] paramTypes, boolean isConstructor, IMethod[] methods) throws JavaModelException {
353 for (int i= methods.length - 1; i >= 0; i--) {
354 if (isSameMethodSignature(name, paramTypes, isConstructor, methods[i])) {
363 * Finds a method declararion in a type's hierarchy. The search is top down, so this
364 * returns the first declaration of the method in the hierarchy.
365 * This searches for a method with a name and signature. Parameter types are only
366 * compared by the simple name, no resolving for the fully qualified type name is done.
367 * Constructors are only compared by parameters, not the name.
368 * @param type Searches in this type's supertypes.
369 * @param name The name of the method to find
370 * @param paramTypes The type signatures of the parameters e.g. <code>{"QString;","I"}</code>
371 * @param isConstructor If the method is a constructor
372 * @return The first method found or null, if nothing found
374 // public static IMethod findMethodDeclarationInHierarchy(ITypeHierarchy hierarchy, IType type, String name, String[] paramTypes, boolean isConstructor) throws JavaModelException {
375 // IType[] superTypes= hierarchy.getAllSupertypes(type);
376 // for (int i= superTypes.length - 1; i >= 0; i--) {
377 // IMethod first= findMethod(name, paramTypes, isConstructor, superTypes[i]);
378 // if (first != null && !Flags.isPrivate(first.getFlags())) {
379 // // the order getAllSupertypes does make assumptions of the order of inner elements -> search recursivly
380 // IMethod res= findMethodDeclarationInHierarchy(hierarchy, first.getDeclaringType(), name, paramTypes, isConstructor);
381 // if (res != null) {
391 * Finds a method implementation in a type's classhierarchy. The search is bottom-up, so this
392 * returns the nearest overridden method. Does not find methods in interfaces or abstract methods.
393 * This searches for a method with a name and signature. Parameter types are only
394 * compared by the simple name, no resolving for the fully qualified type name is done.
395 * Constructors are only compared by parameters, not the name.
396 * @param type Type to search the superclasses
397 * @param name The name of the method to find
398 * @param paramTypes The type signatures of the parameters e.g. <code>{"QString;","I"}</code>
399 * @param isConstructor If the method is a constructor
400 * @return The first method found or null, if nothing found
402 // public static IMethod findMethodImplementationInHierarchy(ITypeHierarchy hierarchy, IType type, String name, String[] paramTypes, boolean isConstructor) throws JavaModelException {
403 // IType[] superTypes= hierarchy.getAllSuperclasses(type);
404 // for (int i= 0; i < superTypes.length; i++) {
405 // IMethod found= findMethod(name, paramTypes, isConstructor, superTypes[i]);
406 // if (found != null) {
407 // if (Flags.isAbstract(found.getFlags())) {
417 * Tests if a method equals to the given signature.
418 * Parameter types are only compared by the simple name, no resolving for
419 * the fully qualified type name is done. Constructors are only compared by
420 * parameters, not the name.
421 * @param Name of the method
422 * @param The type signatures of the parameters e.g. <code>{"QString;","I"}</code>
423 * @param Specifies if the method is a constructor
424 * @return Returns <code>true</code> if the method has the given name and parameter types and constructor state.
426 public static boolean isSameMethodSignature(String name, String[] paramTypes, boolean isConstructor, IMethod curr) throws JavaModelException {
427 if (isConstructor || name.equals(curr.getElementName())) {
428 if (isConstructor == curr.isConstructor()) {
429 String[] currParamTypes= curr.getParameterTypes();
430 if (paramTypes.length == currParamTypes.length) {
431 for (int i= 0; i < paramTypes.length; i++) {
432 String t1= Signature.getSimpleName(Signature.toString(paramTypes[i]));
433 String t2= Signature.getSimpleName(Signature.toString(currParamTypes[i]));
434 if (!t1.equals(t2)) {
446 * Checks whether the given type has a valid main method or not.
448 public static boolean hasMainMethod(IType type) throws JavaModelException {
449 IMethod[] methods= type.getMethods();
450 for (int i= 0; i < methods.length; i++) {
451 if (methods[i].isMainMethod()) {
459 * Checks if the field is boolean.
461 public static boolean isBoolean(IField field) throws JavaModelException{
462 return field.getTypeSignature().equals(Signature.SIG_BOOLEAN);
466 * Returns true if the element is on the build path of the given project
467 * @deprecated Use jproject.isOnClasspath(element);
469 // public static boolean isOnBuildPath(IJavaProject jproject, IJavaElement element) throws JavaModelException {
470 // return jproject.isOnClasspath(element);
474 * Tests if the given element is on the class path of its containing project. Handles the case
475 * that the containing project isn't a Java project.
477 // public static boolean isOnClasspath(IJavaElement element) {
478 // IJavaProject project= element.getJavaProject();
479 // if (!project.exists())
481 // return project.isOnClasspath(element);
485 * Resolves a type name in the context of the declaring type.
486 * @param refTypeSig the type name in signature notation (for example 'QVector')
487 * this can also be an array type, but dimensions will be ignored.
488 * @param declaringType the context for resolving (type where the reference was made in)
489 * @return returns the fully qualified type name or build-in-type name.
490 * if a unresoved type couldn't be resolved null is returned
492 public static String getResolvedTypeName(String refTypeSig, IType declaringType) throws JavaModelException {
493 int arrayCount= Signature.getArrayCount(refTypeSig);
494 char type= refTypeSig.charAt(arrayCount);
495 if (type == Signature.C_UNRESOLVED) {
496 int semi= refTypeSig.indexOf(Signature.C_SEMICOLON, arrayCount + 1);
498 throw new IllegalArgumentException();
500 String name= refTypeSig.substring(arrayCount + 1, semi);
502 // String[][] resolvedNames= declaringType.resolveType(name);
503 // if (resolvedNames != null && resolvedNames.length > 0) {
504 // return JavaModelUtil.concatenateName(resolvedNames[0][0], resolvedNames[0][1]);
508 return Signature.toString(refTypeSig.substring(arrayCount));
513 * Returns if a CU can be edited.
515 public static boolean isEditable(ICompilationUnit cu) {
516 if (cu.isWorkingCopy()) {
517 cu= (ICompilationUnit) cu.getOriginalElement();
519 IResource resource= cu.getResource();
520 return (resource.exists() && !resource.isReadOnly());
524 * Finds a qualified import for a type name.
526 // public static IImportDeclaration findImport(ICompilationUnit cu, String simpleName) throws JavaModelException {
527 // IImportDeclaration[] existing= cu.getImports();
528 // for (int i= 0; i < existing.length; i++) {
529 // String curr= existing[i].getElementName();
530 // if (curr.endsWith(simpleName)) {
531 // int dotPos= curr.length() - simpleName.length() - 1;
532 // if ((dotPos == -1) || (dotPos > 0 && curr.charAt(dotPos) == '.')) {
533 // return existing[i];
541 * Returns the original if the given member. If the member is already
542 * an original the input is returned. The returned member must not exist
544 public static IMember toOriginal(IMember member) {
545 if (member instanceof IMethod)
546 return toOriginalMethod((IMethod)member);
547 ICompilationUnit cu= member.getCompilationUnit();
548 if (cu != null && cu.isWorkingCopy())
549 return (IMember)cu.getOriginal(member);
554 * XXX workaround for bug 18568
555 * http://bugs.eclipse.org/bugs/show_bug.cgi?id=18568
556 * to be removed once the bug is fixed
558 private static IMethod toOriginalMethod(IMethod method) {
560 ICompilationUnit cu= method.getCompilationUnit();
561 if (cu == null || ! cu.isWorkingCopy())
563 //use the workaround only if needed
564 if (! method.getElementName().equals(method.getDeclaringType().getElementName()))
565 return (IMethod)cu.getOriginal(method);
567 IType originalType = (IType)toOriginal(method.getDeclaringType());
568 IMethod[] methods = originalType.findMethods(method);
569 boolean isConstructor = method.isConstructor();
570 for (int i=0; i < methods.length; i++) {
571 if (methods[i].isConstructor() == isConstructor)
575 } catch(JavaModelException e){
581 * Returns the original cu if the given cu. If the cu is already
582 * an original the input cu is returned. The returned cu must not exist
584 public static ICompilationUnit toOriginal(ICompilationUnit cu) {
585 if (cu != null && cu.isWorkingCopy())
586 return (ICompilationUnit) cu.getOriginal(cu);
591 * Returns the working copy of the given member. If the member is already in a
592 * working copy or the member does not exist in the working copy the input is returned.
594 public static IMember toWorkingCopy(IMember member) {
595 ICompilationUnit cu= member.getCompilationUnit();
596 if (cu != null && !cu.isWorkingCopy()) {
597 ICompilationUnit workingCopy= EditorUtility.getWorkingCopy(cu);
598 if (workingCopy != null) {
599 IJavaElement[] members= workingCopy.findElements(member);
600 if (members != null && members.length > 0) {
601 return (IMember) members[0];
610 * Returns the working copy CU of the given CU. If the CU is already a
611 * working copy or the CU has no working copy the input CU is returned.
613 public static ICompilationUnit toWorkingCopy(ICompilationUnit cu) {
614 if (!cu.isWorkingCopy()) {
615 ICompilationUnit workingCopy= EditorUtility.getWorkingCopy(cu);
616 if (workingCopy != null) {
624 * http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
626 * Reconciling happens in a separate thread. This can cause a situation where the
627 * Java element gets disposed after an exists test has been done. So we should not
628 * log not present exceptions when they happen in working copies.
630 public static boolean filterNotPresentException(CoreException exception) {
631 if (!(exception instanceof JavaModelException))
633 JavaModelException je= (JavaModelException)exception;
634 if (!je.isDoesNotExist())
636 IJavaElement[] elements= je.getJavaModelStatus().getElements();
637 for (int i= 0; i < elements.length; i++) {
638 IJavaElement element= elements[i];
639 ICompilationUnit unit= (ICompilationUnit)element.getAncestor(IJavaElement.COMPILATION_UNIT);
642 if (!unit.isWorkingCopy())
648 // public static IType[] getAllSuperTypes(IType type, IProgressMonitor pm) throws JavaModelException {
649 // //workaround for bugs 23644 and 23656
651 // pm.beginTask("", 3); //$NON-NLS-1$
652 // ITypeHierarchy hierarchy= type.newSupertypeHierarchy(new SubProgressMonitor(pm, 1));
654 // IProgressMonitor subPm= new SubProgressMonitor(pm, 2);
655 // List typeList= Arrays.asList(hierarchy.getAllSupertypes(type));
656 // subPm.beginTask("", typeList.size()); //$NON-NLS-1$
657 // Set types= new HashSet(typeList);
658 // for (Iterator iter= typeList.iterator(); iter.hasNext();) {
659 // IType superType= (IType)iter.next();
660 // IType[] superTypes= getAllSuperTypes(superType, new SubProgressMonitor(subPm, 1));
661 // types.addAll(Arrays.asList(superTypes));
663 // types.add(type.getJavaProject().findType("java.lang.Object"));//$NON-NLS-1$
665 // return (IType[]) types.toArray(new IType[types.size()]);
672 public static boolean isExcludedPath(IPath resourcePath, IPath[] exclusionPatterns) {
673 char[] path = resourcePath.toString().toCharArray();
674 for (int i = 0, length = exclusionPatterns.length; i < length; i++) {
675 char[] pattern= exclusionPatterns[i].toString().toCharArray();
676 if (CharOperation.pathMatch(pattern, path, true, '/')) {
685 * Returns whether the given resource path matches one of the exclusion
688 * @see IClasspathEntry#getExclusionPatterns
690 public final static boolean isExcluded(IPath resourcePath, char[][] exclusionPatterns) {
691 if (exclusionPatterns == null) return false;
692 char[] path = resourcePath.toString().toCharArray();
693 for (int i = 0, length = exclusionPatterns.length; i < length; i++)
694 if (CharOperation.pathMatch(exclusionPatterns[i], path, true, '/'))