| /******************************************************************************* |
| * Copyright (c) 2010 BSI Business Systems Integration AG. |
| * 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: |
| * BSI Business Systems Integration AG - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.scout.sdk.util.type; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.IClassFile; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IImportDeclaration; |
| 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.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.IRegion; |
| import org.eclipse.jdt.core.ISourceRange; |
| 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.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTParser; |
| import org.eclipse.jdt.core.dom.ASTVisitor; |
| import org.eclipse.jdt.core.dom.Block; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.scout.commons.CompareUtility; |
| import org.eclipse.scout.commons.StringUtility; |
| import org.eclipse.scout.commons.holders.Holder; |
| import org.eclipse.scout.sdk.util.ast.AstUtility; |
| import org.eclipse.scout.sdk.util.internal.SdkUtilActivator; |
| import org.eclipse.scout.sdk.util.internal.typecache.HierarchyCache; |
| import org.eclipse.scout.sdk.util.internal.typecache.TypeCache; |
| import org.eclipse.scout.sdk.util.jdt.SourceRange; |
| import org.eclipse.scout.sdk.util.signature.SignatureUtility; |
| import org.eclipse.scout.sdk.util.typecache.IPrimaryTypeTypeHierarchy; |
| import org.eclipse.scout.sdk.util.typecache.ITypeCache; |
| import org.eclipse.scout.sdk.util.typecache.ITypeHierarchy; |
| |
| public class TypeUtility { |
| |
| public static final String DEFAULT_SOURCE_FOLDER_NAME = "src"; |
| public static final Pattern BEAN_METHOD_NAME = Pattern.compile("(get|set|is)([A-Z].*)"); |
| |
| protected TypeUtility() { |
| } |
| |
| /** |
| * @see ITypeCache#getType(String) |
| */ |
| public static IType getType(String typeName) { |
| return TypeCache.getInstance().getType(typeName); |
| } |
| |
| /** |
| * @see ITypeCache#getTypes(String) |
| */ |
| public static IType[] getTypes(String typeName) { |
| return TypeCache.getInstance().getTypes(typeName); |
| } |
| |
| /** |
| * @see ITypeCache#getType(String, IJavaProject) |
| */ |
| public static IType getType(String typeName, IJavaProject classpath) { |
| return TypeCache.getInstance().getType(typeName, classpath); |
| } |
| |
| /** |
| * @see ITypeCache#getTypes(String, IJavaProject) |
| */ |
| public static IType[] getTypes(String typeName, IJavaProject classpath) { |
| return TypeCache.getInstance().getTypes(typeName, classpath); |
| } |
| |
| public static boolean existsType(String typeName) { |
| return exists(getType(typeName)); |
| } |
| |
| public static IPackageFragmentRoot getSrcPackageFragmentRoot(IJavaProject project) throws JavaModelException { |
| return project.findPackageFragmentRoot(new Path(IPath.SEPARATOR + project.getElementName() + IPath.SEPARATOR + DEFAULT_SOURCE_FOLDER_NAME)); |
| } |
| |
| public static IPackageFragment getPackage(IJavaElement element) { |
| IPackageFragment packageFragment = (IPackageFragment) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT); |
| return packageFragment; |
| } |
| |
| public static IPackageFragment getPackage(IJavaProject project, String packageName) throws JavaModelException { |
| return getSrcPackageFragmentRoot(project).getPackageFragment(packageName); |
| } |
| |
| public static IPackageFragment[] getSubPackages(IPackageFragment packageFragment) { |
| ArrayList<IPackageFragment> subPackages = new ArrayList<IPackageFragment>(); |
| String prefix = packageFragment.getElementName() + "."; |
| IPackageFragmentRoot packageFragmentRoot = (IPackageFragmentRoot) packageFragment.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); |
| if (exists(packageFragmentRoot)) { |
| try { |
| for (IJavaElement candidate : packageFragmentRoot.getChildren()) { |
| if (exists(candidate) && candidate.getElementType() == IJavaElement.PACKAGE_FRAGMENT && candidate.getElementName().startsWith(prefix)) { |
| subPackages.add((IPackageFragment) candidate); |
| } |
| } |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logError("could not get subpackages of '" + packageFragment.getElementName() + "'.", e); |
| } |
| } |
| |
| return subPackages.toArray(new IPackageFragment[subPackages.size()]); |
| } |
| |
| public static boolean isSubtype(IType type, IType potentialSubtype, org.eclipse.jdt.core.ITypeHierarchy hierarchy) { |
| HashSet<IType> allSubtypes = new HashSet<IType>(Arrays.asList(hierarchy.getAllSubtypes(type))); |
| return allSubtypes.contains(potentialSubtype); |
| } |
| |
| public static IType[] toArray(IType... types) { |
| if (types == null) { |
| return new IType[0]; |
| } |
| return types; |
| } |
| |
| /** |
| * Gets the first type in the compilation unit surrounding the given java element |
| * |
| * @param e |
| * the java element. |
| * @return the primary type or null. |
| * @throws JavaModelException |
| */ |
| public static IType getPrimaryType(IJavaElement e) throws JavaModelException { |
| if (exists(e)) { |
| // source |
| ICompilationUnit icu = (ICompilationUnit) e.getAncestor(IJavaElement.COMPILATION_UNIT); |
| if (exists(icu)) { |
| IType[] candidates = icu.getTypes(); |
| if (candidates != null && candidates.length > 0) { |
| IType result = candidates[0]; |
| if (exists(result)) { |
| return result; |
| } |
| } |
| } |
| else { |
| // binary |
| IClassFile cf = (IClassFile) e.getAncestor(IJavaElement.CLASS_FILE); |
| if (exists(cf)) { |
| IType candidate = cf.getType(); |
| if (exists(candidate)) { |
| return candidate; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static IType[] getInnerTypes(IType type) { |
| return getInnerTypes(type, null); |
| } |
| |
| public static IType[] getInnerTypes(IType type, ITypeFilter filter) { |
| return getInnerTypes(type, filter, null); |
| } |
| |
| /** |
| * Returns the immediate member types declared by the given type. The results is filtered using the given filter and |
| * sorted using the given comparator. |
| * |
| * @param type |
| * The type whose immediate inner types should be returned. |
| * @param filter |
| * the filter to apply or null |
| * @param comparator |
| * the comparator to sort the result or null |
| * @return the immediate inner types declared in the given type. |
| */ |
| public static IType[] getInnerTypes(IType type, ITypeFilter filter, Comparator<IType> comparator) { |
| Set<IType> result = null; |
| if (comparator == null) { |
| result = new HashSet<IType>(); |
| } |
| else { |
| result = new TreeSet<IType>(comparator); |
| } |
| |
| try { |
| for (IType subType : type.getTypes()) { |
| if (filter == null || filter.accept(subType)) { |
| result.add(subType); |
| } |
| } |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logWarning("could not get inner types of '" + type.getFullyQualifiedName() + "'", e); |
| } |
| return result.toArray(new IType[result.size()]); |
| } |
| |
| /** |
| * Returns the immediate member types declared by the given type which are sub-types of the given super-type. The |
| * results is sorted using the given comparator. |
| * |
| * @param declaringType |
| * The type whose immediate inner types should be returned. |
| * @param superType |
| * The super-type for which all returned types must be a sub-type. |
| * @param comparator |
| * the comparator to sort the result. |
| * @return the immediate member types declared by the given type which are sub-types of the given super-type. |
| */ |
| public static IType[] getInnerTypesOrdered(IType declaringType, IType superType, Comparator<IType> comparator) { |
| ITypeHierarchy combinedTypeHierarchy = getLocalTypeHierarchy(declaringType); |
| IType[] allSubtypes = getInnerTypes(declaringType, TypeFilters.getSubtypeFilter(superType, combinedTypeHierarchy), comparator); |
| return allSubtypes; |
| } |
| |
| public static IType getTypeBySignature(String signature) { |
| if (signature == null) { |
| return null; |
| } |
| return getType(SignatureUtility.getFullyQualifiedName(signature)); |
| } |
| |
| /** |
| * To get a type hierarchy with the given elements as scope. |
| * |
| * @param elements |
| * @return |
| * @throws JavaModelException |
| */ |
| public static ITypeHierarchy getLocalTypeHierarchy(IJavaElement... elements) { |
| IRegion region = JavaCore.newRegion(); |
| if (elements != null) { |
| for (IJavaElement e : elements) { |
| if (e.getElementType() == IJavaElement.TYPE) { |
| IType t = (IType) e; |
| if (t.isBinary()) { |
| // binary types do not include their inner types because these inner types belong to their own class file |
| // solution: add them manually |
| addBinaryInnerTypesToRegionRec(t, region); |
| } |
| } |
| region.add(e); |
| } |
| } |
| return HierarchyCache.getInstance().getLocalHierarchy(region); |
| } |
| |
| private static void addBinaryInnerTypesToRegionRec(IType declaringType, IRegion region) { |
| try { |
| for (IType child : declaringType.getTypes()) { |
| region.add(child); |
| addBinaryInnerTypesToRegionRec(child, region); |
| } |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logError("Unable to get inner types of type '" + declaringType.getFullyQualifiedName() + "'.", e); |
| } |
| } |
| |
| public static ITypeHierarchy getLocalTypeHierarchy(IRegion region) { |
| return HierarchyCache.getInstance().getLocalHierarchy(region); |
| } |
| |
| public static ITypeHierarchy getSuperTypeHierarchy(IType type) { |
| return HierarchyCache.getInstance().getSuperHierarchy(type); |
| } |
| |
| public static IPrimaryTypeTypeHierarchy[] getAllCachedPrimaryTypeHierarchies() { |
| return HierarchyCache.getInstance().getAllCachedHierarchies(); |
| } |
| |
| /** |
| * To get a type hierarchy containing no inner types and tracking changes of all primary types in the hierarchy. <br> |
| * <br> |
| * <b> Note: </b> the listener reference is a weak reference. If users do not keep the reference the type hierarchy |
| * will be removed from cache and released. |
| * |
| * @param type |
| * the type to get the primary type hierarchy for must be a primary type |
| * @return the cached type hierarchy or null if type does not exist or hierarchy could not be created. |
| * @throws IllegalArgumentException |
| * if the given type is not a primary type. |
| */ |
| public static IPrimaryTypeTypeHierarchy getPrimaryTypeHierarchy(IType type) throws IllegalArgumentException { |
| return HierarchyCache.getInstance().getPrimaryTypeHierarchy(type); |
| } |
| |
| public static boolean hasInnerType(IType declaringType, String typeName) { |
| IType candidate = declaringType.getType(typeName); |
| return candidate != null && candidate.exists(); |
| } |
| |
| public static IMethod getMethod(IType declaringType, String methodName, String[] resolvedParameterSignatures) throws CoreException { |
| for (IMethod m : declaringType.getMethods()) { |
| if (CompareUtility.equals(m.getElementName(), methodName)) { |
| // signature compare |
| String[] parameterSignatures = SignatureUtility.getMethodParameterSignatureResolved(m); |
| if (parameterSignatures.length == resolvedParameterSignatures.length) { |
| boolean signatureEquals = true; |
| for (int i = 0; i < parameterSignatures.length; i++) { |
| if (!CompareUtility.equals(resolvedParameterSignatures[i], parameterSignatures[i])) { |
| signatureEquals = false; |
| break; |
| } |
| } |
| if (signatureEquals) { |
| return m; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static IMethod findMethodInSuperClassHierarchy(IType type, IMethodFilter filter) { |
| try { |
| return findMethodInSuperClassHierarchy(type, type.newSupertypeHierarchy(null), filter); |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logWarning("could not create super type hierarchy of '" + type.getFullyQualifiedName() + "'.", e); |
| return null; |
| } |
| } |
| |
| public static IMethod findMethodInSuperClassHierarchy(IType type, org.eclipse.jdt.core.ITypeHierarchy hierarchy, IMethodFilter filter) { |
| if (exists(type)) { |
| IMethod method = getFirstMethod(type, filter); |
| if (exists(method)) { |
| return method; |
| } |
| else { |
| return findMethodInSuperClassHierarchy(hierarchy.getSuperclass(type), hierarchy, filter); |
| } |
| } |
| return null; |
| } |
| |
| public static IMethod getFirstMethod(IType type, IMethodFilter filter) { |
| try { |
| for (IMethod method : type.getMethods()) { |
| |
| if (filter == null || filter.accept(method)) { |
| return method; |
| } |
| } |
| } |
| catch (CoreException e) { |
| SdkUtilActivator.logWarning("could not get methods of '" + type.getFullyQualifiedName() + "' with filter '" + filter + "'.", e); |
| } |
| return null; |
| } |
| |
| /** |
| * Searches and returns the first method with the given name in the given type.<br> |
| * If multiple methods with the same name exist (overloads), the first is returned as they appear in the source or |
| * class file. |
| * |
| * @param type |
| * The type in which the method should be searched. |
| * @param methodName |
| * The name of the method. |
| * @return The first method found or null. |
| */ |
| public static IMethod getMethod(IType type, final String methodName) { |
| IMethod[] methods = getMethods(type, new IMethodFilter() { |
| @Override |
| public boolean accept(IMethod method) { |
| return method.getElementName().equals(methodName); |
| } |
| }); |
| if (methods.length > 0) { |
| return methods[0]; |
| } |
| return null; |
| } |
| |
| /** |
| * Gets all methods in the given type.<br> |
| * The methods are in the order in which they appear in the source or class file. |
| * |
| * @param type |
| * The type to get all methods of. |
| * @return an array of all methods of the given type. never returns null. |
| */ |
| public static IMethod[] getMethods(IType type) { |
| return getMethods(type, null); |
| } |
| |
| /** |
| * Gets all methods in the given type that match the given filter.<br> |
| * The methods are in the order in which they appear in the source or class file. |
| * |
| * @param type |
| * The type to get all methods of. |
| * @param filter |
| * The filter. |
| * @return an array of all methods of the given type matching the given filter. never returns null. |
| */ |
| public static IMethod[] getMethods(IType type, IMethodFilter filter) { |
| return getMethods(type, filter, null); |
| } |
| |
| /** |
| * Gets all methods in the given type that match the given filter ordered by the given comparator.<br> |
| * If the given comparator is null, the methods are in the order in which they appear in the source or class file. |
| * |
| * @param type |
| * The type to get all methods of. |
| * @param filter |
| * The filter to use or null for no filtering. |
| * @param comparator |
| * The comparator to use or null to get the methods in the order in which they appear in the source or class |
| * file. |
| * @return an array of all methods of the given type matching the given filter. never returns null. |
| */ |
| public static IMethod[] getMethods(IType type, IMethodFilter filter, Comparator<IMethod> comparator) { |
| try { |
| IMethod[] methods = type.getMethods(); |
| if (filter == null && comparator == null) { |
| return methods; |
| } |
| |
| Collection<IMethod> collector = null; |
| if (comparator == null) { |
| collector = new ArrayList<IMethod>(methods.length); |
| } |
| else { |
| collector = new TreeSet<IMethod>(comparator); |
| } |
| |
| for (IMethod method : methods) { |
| if (filter == null || filter.accept(method)) { |
| collector.add(method); |
| } |
| } |
| return collector.toArray(new IMethod[collector.size()]); |
| } |
| catch (CoreException e) { |
| SdkUtilActivator.logWarning("could not get methods of '" + type.getFullyQualifiedName() + "' with filter '" + filter + "'.", e); |
| return new IMethod[]{}; |
| } |
| } |
| |
| public static ISourceRange getContentSourceRange(IMethod method) throws JavaModelException { |
| ASTParser parser = AstUtility.newParser(); |
| parser.setCompilerOptions(method.getJavaProject().getOptions(true)); |
| parser.setKind(ASTParser.K_CLASS_BODY_DECLARATIONS); |
| parser.setSource(method.getSource().toCharArray()); |
| parser.setResolveBindings(false); |
| parser.setBindingsRecovery(false); |
| ASTNode rootNode = parser.createAST(null); |
| final Holder<ISourceRange> rangeHolder = new Holder<ISourceRange>(ISourceRange.class); |
| rootNode.accept(new ASTVisitor() { |
| boolean a = false; |
| |
| @Override |
| public boolean visit(MethodDeclaration node) { |
| a = true; |
| return true; |
| } |
| |
| @Override |
| public boolean visit(Block node) { |
| if (a) { |
| rangeHolder.setValue(new SourceRange(node.getStartPosition(), node.getLength())); |
| return false; |
| } |
| return true; |
| } |
| |
| }); |
| ISourceRange methodRelativeRange = rangeHolder.getValue(); |
| if (methodRelativeRange != null) { |
| return new SourceRange(methodRelativeRange.getOffset() + method.getSourceRange().getOffset() + 1, methodRelativeRange.getLength() - 2); |
| } |
| return null; |
| } |
| |
| public static MethodParameter[] getMethodParameters(IMethod method, IType contextType) throws CoreException { |
| String[] paramNames = method.getParameterNames(); |
| String[] resolvedParamSignatures = SignatureUtility.getMethodParameterSignatureResolved(method, contextType); |
| if (paramNames.length != resolvedParamSignatures.length) { |
| throw new IllegalArgumentException("Could not resolve method parameters of '" + method.getElementName() + "' in '" + method.getDeclaringType().getFullyQualifiedName() + "'."); |
| } |
| List<MethodParameter> params = new ArrayList<MethodParameter>(); |
| for (int i = 0; i < paramNames.length; i++) { |
| params.add(new MethodParameter(paramNames[i], resolvedParamSignatures[i])); |
| } |
| return params.toArray(new MethodParameter[params.size()]); |
| } |
| |
| public static MethodParameter[] getMethodParameters(IMethod method, Map<String, String> generics) throws CoreException { |
| String[] paramNames = method.getParameterNames(); |
| String[] resolvedParamSignatures = SignatureUtility.getMethodParameterSignatureResolved(method, generics); |
| if (paramNames.length != resolvedParamSignatures.length) { |
| throw new IllegalArgumentException("Could not resolve method parameters of '" + method.getElementName() + "' in '" + method.getDeclaringType().getFullyQualifiedName() + "'."); |
| } |
| List<MethodParameter> params = new ArrayList<MethodParameter>(); |
| for (int i = 0; i < paramNames.length; i++) { |
| params.add(new MethodParameter(paramNames[i], resolvedParamSignatures[i])); |
| } |
| return params.toArray(new MethodParameter[params.size()]); |
| } |
| |
| public static IType getAncestor(IType type, ITypeFilter filter) { |
| IType ancestorType = type; |
| while (exists(ancestorType)) { |
| if (filter.accept(ancestorType)) { |
| return ancestorType; |
| } |
| ancestorType = ancestorType.getDeclaringType(); |
| } |
| return null; |
| } |
| |
| public static IField[] getFields(IType declaringType) { |
| return getFields(declaringType, null); |
| } |
| |
| public static IField[] getFields(IType declaringType, IFieldFilter filter) { |
| return getFields(declaringType, filter, null); |
| } |
| |
| public static IField[] getFields(IType declaringType, IFieldFilter filter, Comparator<IField> comparator) { |
| try { |
| IField[] fields = declaringType.getFields(); |
| if (filter == null && comparator == null) { |
| return fields; |
| } |
| |
| Collection<IField> collector = null; |
| if (comparator == null) { |
| collector = new ArrayList<IField>(fields.length); |
| } |
| else { |
| collector = new TreeSet<IField>(comparator); |
| } |
| |
| for (IField field : fields) { |
| if (filter == null || filter.accept(field)) { |
| collector.add(field); |
| } |
| } |
| return collector.toArray(new IField[collector.size()]); |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logWarning("could not get fields of '" + declaringType.getElementName() + "'.", e); |
| return new IField[]{}; |
| } |
| } |
| |
| public static IField getFirstField(IType type, IFieldFilter filter) { |
| try { |
| for (IField method : type.getFields()) { |
| if (filter == null || filter.accept(method)) { |
| return method; |
| } |
| } |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logWarning("could not get methods of '" + type.getFullyQualifiedName() + "'.", e); |
| } |
| return null; |
| } |
| |
| public static Object getFieldConstant(IField field) throws JavaModelException { |
| Object val = field.getConstant(); |
| if (val instanceof String) { |
| String ret = (String) val; |
| ret = ret.trim(); |
| if (ret.length() > 2 && ret.charAt(0) == '"' && ret.charAt(ret.length() - 1) == '"') { |
| // when scout runtime sources are present in the workspace -> field value is returned with double quotes |
| ret = ret.substring(1, ret.length() - 1); |
| } |
| return ret; |
| } |
| else { |
| return val; |
| } |
| } |
| |
| public static boolean equalTypes(IType[] arr1, IType[] arr2) { |
| if (arr1 == null && arr2 == null) { |
| return true; |
| } |
| else if (arr1 == null) { |
| return false; |
| } |
| else if (arr2 == null) { |
| return false; |
| } |
| else if (arr1.length != arr2.length) { |
| return false; |
| } |
| else { |
| Arrays.sort(arr1, TypeComparators.getHashCodeComparator()); |
| Arrays.sort(arr2, TypeComparators.getHashCodeComparator()); |
| return Arrays.equals(arr1, arr2); |
| } |
| } |
| |
| public static IMethod getOverwrittenMethod(IMethod method, org.eclipse.jdt.core.ITypeHierarchy superTypeHierarchy) { |
| IType superType = superTypeHierarchy.getSuperclass(method.getDeclaringType()); |
| IMethodFilter overrideFilter = MethodFilters.getSuperMethodFilter(method); |
| while (superType != null) { |
| IMethod superMethod = getFirstMethod(superType, overrideFilter); |
| if (superMethod != null) { |
| return superMethod; |
| } |
| superType = superTypeHierarchy.getSuperclass(superType); |
| } |
| return null; |
| } |
| |
| /** |
| * <xmp> |
| * class A{ |
| * class B{ |
| * class C{ |
| * } |
| * class D{ |
| * } |
| * } |
| * } |
| * // A.getTopLevelType() returns A |
| * // D.getTopLevelType() returns A |
| * </xmp> |
| * |
| * @return the primary type of the compilation unit this type is declared in. |
| */ |
| public static IType getToplevelType(IType type) { |
| if (type == null) { |
| return null; |
| } |
| if (type.getParent().getElementType() != IJavaElement.TYPE) { |
| return type; |
| } |
| return getToplevelType(type.getDeclaringType()); |
| } |
| |
| public static IType[] getTypesInPackage(IPackageFragment pck) { |
| return getTypesInPackage(pck, null); |
| } |
| |
| public static IType[] getTypesInPackage(IPackageFragment pck, ITypeFilter filter) { |
| return getTypesInPackage(pck, filter, null); |
| } |
| |
| public static IType[] getTypesInPackage(IPackageFragment pck, ITypeFilter filter, Comparator<IType> comparator) { |
| return getTypesInPackage(pck, filter, comparator, false); |
| } |
| |
| public static IType[] getTypesInPackage(IPackageFragment pck, ITypeFilter filter, Comparator<IType> comparator, boolean includeSubpackages) { |
| |
| Collection<IType> unsortedTypes = new ArrayList<IType>(); |
| collectTypesInPackage(pck, filter, includeSubpackages, unsortedTypes); |
| |
| if (comparator == null) { |
| return unsortedTypes.toArray(new IType[unsortedTypes.size()]); |
| } |
| else { |
| TreeSet<IType> sortedTypes = new TreeSet<IType>(comparator); |
| sortedTypes.addAll(unsortedTypes); |
| return sortedTypes.toArray(new IType[sortedTypes.size()]); |
| } |
| } |
| |
| private static void collectTypesInPackage(IPackageFragment pck, ITypeFilter filter, boolean includeSubPackages, Collection<IType> collector) { |
| try { |
| if (pck != null && pck.exists()) { |
| for (IJavaElement element : pck.getChildren()) { |
| if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) { |
| if (includeSubPackages) { |
| collectTypesInPackage((IPackageFragment) element, filter, includeSubPackages, collector); |
| } |
| } |
| else if (element.getElementType() == IJavaElement.COMPILATION_UNIT) { |
| ICompilationUnit icu = (ICompilationUnit) element; |
| IType[] types = icu.getTypes(); |
| if (types != null && types.length > 0) { |
| collector.add(types[0]); |
| } |
| } |
| } |
| } |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logWarning("could not get types of package '" + pck.getElementName() + "'", e); |
| } |
| |
| } |
| |
| public static boolean exists(IJavaElement element) { |
| return element != null && element.exists(); |
| } |
| |
| public static IType findInnerType(IType type, String innerTypeName) { |
| if (type == null) { |
| return null; |
| } |
| else if (StringUtility.equalsIgnoreCase(type.getElementName(), innerTypeName)) { |
| return type; |
| } |
| else { |
| try { |
| for (IType innerType : type.getTypes()) { |
| IType found = findInnerType(innerType, innerTypeName); |
| if (found != null) { |
| return found; |
| } |
| } |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logError("could not find inner type named '" + innerTypeName + "' in type '" + type.getFullyQualifiedName() + "'.", e); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Tries to find the fully qualified class name of the given simple name based on the context of the given type. |
| * |
| * @param declaringType |
| * The context type for which the simple name should be resolved. |
| * @param typeName |
| * The type name to resolve. |
| * @param searchOnClassPath |
| * specifies if the classpath of the {@link IJavaProject} of the declaringType should be searched as well. |
| * @return The fully qualified name of the given simple name for the given context or null. |
| * @throws JavaModelException |
| */ |
| public static String getReferencedTypeFqn(IType declaringType, String typeName, boolean searchOnClassPath) throws JavaModelException { |
| // 1. check the imports (performance improvement) |
| ICompilationUnit compilationUnit = declaringType.getCompilationUnit(); |
| if (compilationUnit != null) { |
| String searchString = '.' + typeName; |
| IImportDeclaration[] imports = compilationUnit.getImports(); |
| for (IImportDeclaration imp : imports) { |
| if (imp.getElementName().endsWith(searchString)) { |
| return imp.getElementName(); |
| } |
| } |
| } |
| |
| // 2. try to resolve |
| String[][] resolvedTypeName = declaringType.resolveType(typeName); |
| if (resolvedTypeName != null && resolvedTypeName.length == 1) { |
| String pck = resolvedTypeName[0][0]; |
| StringBuilder fqName = new StringBuilder(); |
| if (pck != null && pck.length() > 0) { |
| fqName.append(pck); |
| fqName.append('.'); |
| } |
| fqName.append(resolvedTypeName[0][1]); // class simple name |
| return fqName.toString(); |
| } |
| |
| if (searchOnClassPath) { |
| // 3. try to find a matching type on the classpath |
| // some types may not be part of the compilation unit (e.g. declaringType is binary, then there is no compilation unit) and cannot be resolved in the class file. |
| // this can happen when e.g. only a reference to a final static field is in the class file and there is no other reference to the class. |
| // then the compiler removes this reference and directly puts the value of the field in the class file even though the reference remains in the source of the class. |
| // the originating class can then not be found anymore. This happens e.g. with the AbstractIcons reference in AbstractSmartField. |
| // to solve this, try to find a unique type in the workspace with the simple name. If there is only one match, we are happy. |
| if (exists(declaringType.getJavaProject())) { |
| IType[] candidates = getTypes(typeName, declaringType.getJavaProject()); |
| if (candidates.length == 1) { |
| return candidates[0].getFullyQualifiedName(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @return The {@link IType} of the fully qualified name found or null. |
| * @see TypeUtility#getReferencedTypeFqn(IType, String, boolean) |
| */ |
| public static IType getReferencedType(IType declaringType, String typeName, boolean searchOnClassPath) throws JavaModelException { |
| String referencedTypeFqn = getReferencedTypeFqn(declaringType, typeName, searchOnClassPath); |
| if (referencedTypeFqn != null) { |
| return getType(referencedTypeFqn); |
| } |
| return null; |
| } |
| |
| public static boolean isGenericType(IType type) { |
| try { |
| return exists(type) && (type.getTypeParameters().length > 0); |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logWarning("could not get generic information of type: " + type.getFullyQualifiedName(), e); |
| return false; |
| } |
| } |
| |
| /** |
| * checks whether element is on the classpath of the given project |
| * |
| * @param element |
| * the element to search |
| * @param project |
| * the project classpath to search in |
| * @return true if element was found in the classpath of project |
| */ |
| public static boolean isOnClasspath(IJavaElement element, IJavaProject project) { |
| if (!exists(element)) { |
| return false; |
| } |
| if (!exists(project)) { |
| return false; |
| } |
| |
| if (element instanceof IMember) { |
| IMember member = (IMember) element; |
| if (member.isBinary()) { |
| return project.isOnClasspath(member); |
| } |
| } |
| |
| IJavaProject elemenProject = element.getJavaProject(); |
| if (elemenProject != null) { |
| if (project.equals(elemenProject)) { |
| return true; |
| } |
| else { |
| return project.isOnClasspath(elemenProject); |
| } |
| } |
| return project.isOnClasspath(element); |
| } |
| |
| /** |
| * Collects all property beans declared directly in the given type by search methods with the following naming |
| * convention: |
| * |
| * <pre> |
| * public <em><PropertyType></em> get<em><PropertyName></em>(); |
| * public void set<em><PropertyName></em>(<em><PropertyType></em> a); |
| * </pre> |
| * |
| * If <code>PropertyType</code> is a boolean property, the following getter is expected |
| * |
| * <pre> |
| * public boolean is<em><PropertyName></em>(); |
| * </pre> |
| * <p> |
| * This implementation tries to determine the field by using the JDT code style settings stored in the Eclipse |
| * preferences. Prefixes and suffixes used for fields must be declared. The default prefix Scout uses for fields ( |
| * <code>m_</code>) is added by default. |
| * |
| * @param type |
| * the type within properties are searched |
| * @param propertyFilter |
| * optional property bean filter used to filter the result |
| * @param comparator |
| * optional property bean comparator used to sort the result |
| * @return Returns an array of property bean descriptions. The array is empty if the given class does not contain any |
| * bean properties. |
| * @see <a href="http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html">JavaBeans Spec</a> |
| */ |
| public static IPropertyBean[] getPropertyBeans(IType type, IPropertyBeanFilter propertyFilter, Comparator<IPropertyBean> comparator) { |
| HashMap<String, PropertyBean> beans = new HashMap<String, PropertyBean>(); |
| IMethodFilter filter = MethodFilters.getMultiMethodFilter(MethodFilters.getFlagsFilter(Flags.AccPublic), MethodFilters.getNameRegexFilter(BEAN_METHOD_NAME)); |
| IMethod[] methods = getMethods(type, filter); |
| for (IMethod m : methods) { |
| Matcher matcher = BEAN_METHOD_NAME.matcher(m.getElementName()); |
| if (matcher.matches()) { |
| try { |
| String kind = matcher.group(1); |
| String name = matcher.group(2); |
| // |
| String[] parameterTypes = m.getParameterTypes(); |
| String returnType = m.getReturnType(); |
| if (kind.equals("get") && parameterTypes.length == 0 && !returnType.equals(Signature.SIG_VOID)) { |
| PropertyBean desc = beans.get(name); |
| if (desc == null) { |
| desc = new PropertyBean(type, name); |
| beans.put(name, desc); |
| } |
| if (desc.getReadMethod() == null) { |
| desc.setReadMethod(m); |
| } |
| } |
| else if (kind.equals("is") && parameterTypes.length == 0 && returnType.equals(Signature.SIG_BOOLEAN)) { |
| PropertyBean desc = beans.get(name); |
| if (desc == null) { |
| desc = new PropertyBean(type, name); |
| beans.put(name, desc); |
| } |
| if (desc.getReadMethod() == null) { |
| desc.setReadMethod(m); |
| } |
| } |
| else if (kind.equals("set") && parameterTypes.length == 1 && returnType.equals(Signature.SIG_VOID)) { |
| PropertyBean desc = beans.get(name); |
| if (desc == null) { |
| desc = new PropertyBean(type, name); |
| beans.put(name, desc); |
| } |
| if (desc.getWriteMethod() == null) { |
| desc.setWriteMethod(m); |
| } |
| } |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logError("Error while collectiong property beans of type [" + type + "]", e); |
| } |
| } |
| } |
| |
| // filter |
| ArrayList<PropertyBean> filteredBeans = new ArrayList<PropertyBean>(beans.size()); |
| for (PropertyBean bean : beans.values()) { |
| if (propertyFilter == null || propertyFilter.accept(bean)) { |
| filteredBeans.add(bean); |
| } |
| } |
| |
| // fields |
| IField[] fieldCandidates = getFields(type, FieldFilters.getPrivateNotStaticNotFinalNotAbstract(), null); |
| HashMap<String, IField> fields = new HashMap<String, IField>(); |
| for (IField field : fieldCandidates) { |
| fields.put(field.getElementName(), field); |
| } |
| |
| // get field pre- and suffixes |
| @SuppressWarnings("restriction") |
| org.eclipse.jdt.internal.codeassist.impl.AssistOptions assistOptions = new org.eclipse.jdt.internal.codeassist.impl.AssistOptions(type.getJavaProject().getOptions(true)); |
| @SuppressWarnings("restriction") |
| String[] fieldPrefixes = toStringArray(assistOptions.fieldPrefixes, "m_", ""); |
| @SuppressWarnings("restriction") |
| String[] fieldSuffixes = toStringArray(assistOptions.fieldSuffixes, ""); |
| |
| for (PropertyBean bean : filteredBeans) { |
| IField field = findFieldForPropertyBean(bean.getBeanName(), fields, fieldPrefixes, fieldSuffixes); |
| if (field != null) { |
| bean.setField(field); |
| } |
| else { |
| SdkUtilActivator.logWarning("Unable to find field for property bean [" + bean + "]"); |
| } |
| } |
| |
| // sort |
| if (comparator != null) { |
| Collections.sort(filteredBeans, comparator); |
| } |
| |
| return filteredBeans.toArray(new IPropertyBean[filteredBeans.size()]); |
| } |
| |
| /** |
| * To find out if the given child element is a ancestor of the given parent element. If parent and child is the same |
| * element true is returned. |
| * |
| * @param parent |
| * @param chlid |
| * @return |
| */ |
| public static boolean isAncestor(IJavaElement parent, IJavaElement chlid) { |
| if (parent == null || chlid == null) { |
| return false; |
| } |
| if (parent.equals(chlid)) { |
| return true; |
| } |
| else if (chlid.getParent() != null && chlid.getParent().getElementType() >= parent.getElementType()) { |
| return isAncestor(parent, chlid.getParent().getAncestor(parent.getElementType())); |
| } |
| else { |
| return false; |
| } |
| } |
| |
| public static int getIndent(IJavaElement element) { |
| int indent = 0; |
| IJavaElement visitedElement = element; |
| while (visitedElement.getElementType() != IJavaElement.COMPILATION_UNIT) { |
| indent++; |
| visitedElement = element.getParent(); |
| } |
| return indent; |
| } |
| |
| /** |
| * Tries to find a method in the given type and all super types and super interfaces.<br> |
| * If multiple methods with the same name exist in a type (overloads), the first is returned as they appear in the |
| * source or class file. |
| * |
| * @param methodName |
| * the name of the method |
| * @param type |
| * The start type in which (together with its super types and super interfaces) the given method should be |
| * searched. |
| * @param superTypeHierarchy |
| * The super type hierarchy of the given type. |
| * @return The first method found in the type itself, its super types or super interfaces (searched in this order). If |
| * multiple methods with the same name exist in a type (overloads), the first is returned as they appear in |
| * the source or class file. |
| */ |
| public static IMethod findMethodInSuperHierarchy(String methodName, IType type, ITypeHierarchy superTypeHierarchy) { |
| return findMethodInSuperTypeHierarchy(type, superTypeHierarchy, MethodFilters.getNameFilter(methodName)); |
| } |
| |
| public static IMethod findMethodInSuperTypeHierarchy(IType type, ITypeHierarchy superTypeHierarchy, IMethodFilter filter) { |
| IMethod[] methods = getMethods(type, filter); |
| IMethod method = null; |
| if (methods.length == 1) { |
| return methods[0]; |
| } |
| else if (methods.length > 1) { |
| StringBuilder sb = new StringBuilder(" [\n"); |
| sb.append("\t\t").append(methods[0].toString()); |
| for (int i = 1; i < methods.length; i++) { |
| sb.append(", \n\t\t").append(methods[i].toString()); |
| } |
| sb.append("\n\t]"); |
| SdkUtilActivator.logWarning("found more than one method in hierarchy" + sb.toString()); |
| return methods[0]; |
| } |
| else { |
| // super types |
| IType superType = superTypeHierarchy.getSuperclass(type); |
| if (exists(superType) && !superType.getElementName().equals(Object.class.getName())) { |
| method = findMethodInSuperTypeHierarchy(superType, superTypeHierarchy, filter); |
| } |
| if (exists(method)) { |
| return method; |
| } |
| // interfaces |
| for (IType intType : superTypeHierarchy.getSuperInterfaces(type)) { |
| if (exists(intType) && !intType.getElementName().equals(Object.class.getName())) { |
| method = findMethodInSuperTypeHierarchy(intType, superTypeHierarchy, filter); |
| } |
| if (exists(method)) { |
| return method; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Tries to determine the field the given property is based on. All combinations of the given pre- and suffixes are |
| * used to find the field. If none of them matches with a method <code>null</code> is returned. |
| * |
| * @param beanName |
| * @param fields |
| * @param fieldPrefixes |
| * @param fieldSuffixes |
| * @return Returns the field the property is based on or <code>null</code>. |
| */ |
| private static IField findFieldForPropertyBean(String beanName, HashMap<String, IField> fields, String[] fieldPrefixes, String[] fieldSuffixes) { |
| for (String prefix : fieldPrefixes) { |
| for (String suffix : fieldSuffixes) { |
| IField field = fields.get(prefix + decapitalize(beanName) + suffix); |
| if (field != null) { |
| return field; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Converts the given two-dimensional array of chars into an array of strings and adds the given additional values. |
| * |
| * @param arrayOfChars |
| * @param additionalValues |
| * @return |
| */ |
| private static String[] toStringArray(char[][] arrayOfChars, String... additionalValues) { |
| HashSet<String> result = new HashSet<String>(); |
| if (additionalValues != null) { |
| for (String s : additionalValues) { |
| result.add(s); |
| } |
| } |
| if (arrayOfChars != null) { |
| for (char[] cs : arrayOfChars) { |
| if (cs != null && cs.length > 0) { |
| result.add(String.valueOf(cs)); |
| } |
| } |
| } |
| return result.toArray(new String[result.size()]); |
| } |
| |
| /** |
| * Converts the given property name to its field name (according to JavaBeans spec). |
| * |
| * @param s |
| * @return |
| */ |
| private static String decapitalize(String s) { |
| if (s == null || s.length() == 0) return ""; |
| if (s.length() >= 2 && Character.isUpperCase(s.charAt(0)) && Character.isUpperCase(s.charAt(1))) { |
| return s; |
| } |
| return Character.toLowerCase(s.charAt(0)) + s.substring(1); |
| } |
| |
| } |