/** * Tries to find the given {@link IApiMethod} in the given {@link IType}. If a matching method is * not found <code>null</code> is returned * * @param type the type top look in for the given {@link IApiMethod} * @param method the {@link IApiMethod} to look for * @return the {@link IMethod} from the given {@link IType} that matches the given {@link * IApiMethod} or <code>null</code> if no matching method is found * @throws JavaModelException * @throws CoreException */ protected IMethod findMethodInType(IType type, IApiMethod method) throws JavaModelException, CoreException { String[] parameterTypes = Signature.getParameterTypes(method.getSignature()); for (int i = 0; i < parameterTypes.length; i++) { parameterTypes[i] = parameterTypes[i].replace('/', '.'); } String methodname = method.getName(); if (method.isConstructor()) { IApiType enclosingType = method.getEnclosingType(); if (enclosingType.isMemberType() && !Flags.isStatic(enclosingType.getModifiers())) { // remove the synthetic argument that corresponds to the enclosing type int length = parameterTypes.length - 1; System.arraycopy(parameterTypes, 1, (parameterTypes = new String[length]), 0, length); } methodname = enclosingType.getSimpleName(); } IMethod Qmethod = type.getMethod(methodname, parameterTypes); IMethod[] methods = type.getMethods(); IMethod match = null; for (int i = 0; i < methods.length; i++) { IMethod m = methods[i]; if (m.isSimilar(Qmethod)) { match = m; break; } } return match; }
/** * Returns whether the given type has API visibility. * * @param type type * @return whether the given type has API visibility */ private boolean isAPIType(IApiType type) throws CoreException { IApiDescription description = type.getApiComponent().getApiDescription(); IApiAnnotations annotations = description.resolveAnnotations(type.getHandle()); if (annotations == null) { // top level non-public top can have no annotations - they are not // API return false; } return VisibilityModifiers.isAPI(annotations.getVisibility()); }
/** * Adds all visible methods to the given set in the specified type. * * @param type type to analyze * @param members set to add methods to * @param modifiers visibilities to consider */ private void gatherVisibleMethods(IApiType type, Set<MethodKey> members, int modifiers) { IApiMethod[] methods = type.getMethods(); for (int i = 0; i < methods.length; i++) { IApiMethod method = methods[i]; if ((method.getModifiers() & modifiers) > 0 && !method.isConstructor() && !method.isSynthetic()) { members.add(new MethodKey(type.getName(), method.getName(), method.getSignature(), false)); } } }
/** * Returns whether all enclosing types of the given member are visible. * * @param member member * @return whether all enclosing types of the given member are visible * @throws CoreException */ protected boolean isEnclosingTypeVisible(IApiMember member) throws CoreException { IApiType type = null; if (member.getType() == IApiElement.TYPE) { type = (IApiType) member; } else { type = member.getEnclosingType(); } while (type != null) { if (((Flags.AccPublic | Flags.AccProtected) & type.getModifiers()) == 0) { // the type is private or default protection, do not retain the reference return false; } type = type.getEnclosingType(); } return true; }
/** * Adds all API super types of the given type to the given list in top down order. * * @param superTypes list to add to * @param type type being processed */ private void gatherAPISuperTypes(List<IApiType> superTypes, IApiType type) throws CoreException { if (type != null) { if (isAPIType(type)) { superTypes.add(0, type); } gatherAPISuperTypes(superTypes, type.getSuperclass()); IApiType[] interfaces = type.getSuperInterfaces(); if (interfaces != null) { for (int i = 0; i < interfaces.length; i++) { if (isAPIType(interfaces[i])) { superTypes.add(interfaces[i]); gatherAPISuperTypes(superTypes, interfaces[i]); } } } } }
/** * Returns the fully qualified type name associated with the given member. * * @param member * @return fully qualified type name */ protected String getTypeName(IApiMember member) throws CoreException { switch (member.getType()) { case IApiElement.TYPE: { IApiType type = (IApiType) member; if (type.isAnonymous()) { return getTypeName(member.getEnclosingType()); } else if (type.isLocal()) { return getTypeName(member.getEnclosingType()); } return member.getName(); } default: { return getTypeName(member.getEnclosingType()); } } }
/** * Returns whether the given type has any visible fields base on the given visibility flags to * consider. A field is visible signals a definite leak. * * @param type type to analyze * @param modifiers visibilities to consider * @return whether there are any visible fields */ private boolean hasVisibleField(IApiType type, int modifiers) { IApiField[] fields = type.getFields(); for (int i = 0; i < fields.length; i++) { IApiField field = fields[i]; if ((field.getModifiers() & modifiers) > 0) { return true; } } return false; }
/** * Returns whether the referenced type name matches a non-API package. * * @param reference * @return whether the referenced type name matches a non-API package */ protected boolean isNonAPIReference(IReference reference) { String packageName = Signatures.getPackageName(reference.getReferencedTypeName()); if (fNonApiPackageNames.contains(packageName)) { return true; } // could be a reference to a package visible type IApiMember member = reference.getMember(); IApiType type = null; if (member.getType() == IApiElement.TYPE) { type = (IApiType) member; } else { type = (IApiType) member.getAncestor(IApiElement.TYPE); } String origin = Signatures.getPackageName(type.getName()); if (packageName.equals(origin)) { return true; // possible package visible reference } return false; }
/** * Returns the unqualified type name associated with the given member. * * @param member * @return unqualified type name */ protected String getSimpleTypeName(IApiMember member) throws CoreException { switch (member.getType()) { case IApiElement.TYPE: { IApiType type = (IApiType) member; if (type.isAnonymous()) { return getSimpleTypeName(type.getEnclosingType()); } else if (type.isLocal()) { String name = getSimpleTypeName(member.getEnclosingType()); int idx = name.indexOf('$'); if (idx > -1) { return name.substring(0, idx); } return name; } return Signatures.getTypeName(Signatures.getTypeSignature(type)); } default: return getSimpleTypeName(member.getEnclosingType()); } }
/** * Returns a method descriptor with a resolved signature for the given method descriptor with an * unresolved signature. * * @param descriptor method to resolve * @return resolved method descriptor or the same method descriptor if unable to resolve * @exception CoreException if unable to resolve the method and a class file container was * provided for this purpose */ private IMethodDescriptor resolveMethod(IMethodDescriptor descriptor) throws CoreException { if (fContainer != null) { IReferenceTypeDescriptor type = descriptor.getEnclosingType(); IApiTypeRoot classFile = fContainer.findTypeRoot(type.getQualifiedName()); if (classFile != null) { IApiType structure = classFile.getStructure(); if (structure != null) { IApiMethod[] methods = structure.getMethods(); for (int i = 0; i < methods.length; i++) { IApiMethod method = methods[i]; if (descriptor.getName().equals(method.getName())) { String signature = method.getSignature(); String descriptorSignature = descriptor.getSignature().replace('/', '.'); if (Signatures.matchesSignatures( descriptorSignature, signature.replace('/', '.'))) { return descriptor.getEnclosingType().getMethod(method.getName(), signature); } String genericSignature = method.getGenericSignature(); if (genericSignature != null) { if (Signatures.matchesSignatures( descriptorSignature, genericSignature.replace('/', '.'))) { return descriptor.getEnclosingType().getMethod(method.getName(), signature); } } } } } } throw new CoreException( new Status( IStatus.ERROR, ApiPlugin.PLUGIN_ID, MessageFormat.format( "Unable to resolve method signature: {0}", new String[] {descriptor.toString()}), null)); //$NON-NLS-1$ } return descriptor; }
/* * (non-Javadoc) * @see * org.eclipse.pde.api.tools.internal.search.AbstractTypeLeakDetector#isProblem * (org.eclipse.pde.api.tools.internal.provisional.model.IReference) */ @Override public boolean isProblem(IReference reference) { if (super.isProblem(reference)) { // check the use restrictions on the API type (can be extended or // not) IApiType type = (IApiType) reference.getMember(); IApiComponent component = type.getApiComponent(); try { if (type.isClass()) { int modifiers = 0; IApiAnnotations annotations = component.getApiDescription().resolveAnnotations(type.getHandle()); if (annotations != null) { // if annotations are null, the reference should not // have been retained // as it indicates a reference from a top level non // public type if (RestrictionModifiers.isExtendRestriction(annotations.getRestrictions())) { // The no extend restriction means only public // members can be seen modifiers = Flags.AccPublic; } else { if (Flags.isFinal(type.getModifiers())) { // if final then only public members can be seen modifiers = Flags.AccPublic; } else { // public and protected members can be seen modifiers = Flags.AccPublic | Flags.AccProtected; } } IApiType nonApiSuper = type.getSuperclass(); // collect all visible methods in non-API types Set<MethodKey> methods = new HashSet<MethodKey>(); while (!isAPIType(nonApiSuper)) { if (hasVisibleField(nonApiSuper, modifiers)) { // a visible field in a non-API type is a // definite leak return true; } gatherVisibleMethods(nonApiSuper, methods, modifiers); nonApiSuper = nonApiSuper.getSuperclass(); } if (methods.size() > 0) { // check if the visible members are part of an API // interface/class List<IApiType> apiTypes = new LinkedList<IApiType>(); apiTypes.add(type); gatherAPISuperTypes(apiTypes, type); for (IApiType t2 : apiTypes) { Set<MethodKey> apiMembers = new HashSet<MethodKey>(); gatherVisibleMethods(t2, apiMembers, modifiers); methods.removeAll(apiMembers); if (methods.size() == 0) { // there are no visible methods left that // are not part of an API type/interface return false; } } if (methods.size() > 0) { // there are visible members that are not part // of an API type/interface return true; } } } } else { // don't process interfaces, enums, annotations return true; } } catch (CoreException ce) { if (ApiPlugin.DEBUG_PROBLEM_DETECTOR) { ApiPlugin.log(ce); } return true; } } return false; }