blob: 5bbe2753fb7d38363d8b453717f75c173cf9b648 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 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.pde.api.tools.internal.search;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.Flags;
import org.eclipse.pde.api.tools.internal.model.cache.MethodKey;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations;
import org.eclipse.pde.api.tools.internal.provisional.IApiComponent;
import org.eclipse.pde.api.tools.internal.provisional.IApiDescription;
import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers;
import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiField;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiMethod;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiType;
import org.eclipse.pde.api.tools.internal.provisional.model.IReference;
import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem;
import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes;
import org.eclipse.pde.api.tools.internal.provisional.search.ReferenceModifiers;
/**
* Detects leaked super types.
*
* @since 1.1
*/
public class LeakExtendsProblemDetector extends AbstractTypeLeakDetector {
/**
* @param nonApiPackageNames
*/
public LeakExtendsProblemDetector(Set nonApiPackageNames) {
super(nonApiPackageNames);
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.internal.provisional.search.IApiProblemDetector#getReferenceKinds()
*/
public int getReferenceKinds() {
return ReferenceModifiers.REF_EXTENDS;
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#getSeverityKey()
*/
protected String getSeverityKey() {
return IApiProblemTypes.LEAK_EXTEND;
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#getProblemFlags(org.eclipse.pde.api.tools.internal.provisional.model.IReference)
*/
protected int getProblemFlags(IReference reference) {
return IApiProblem.LEAK_EXTENDS;
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.internal.search.AbstractTypeLeakDetector#isProblem(org.eclipse.pde.api.tools.internal.provisional.model.IReference)
*/
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 (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 methoods = new HashSet();
while (!isAPIType(nonApiSuper)) {
if (hasVisibleField(nonApiSuper, modifiers)) {
// a visible field in a non-API type is a definite leak
return true;
}
gatherVisibleMethods(nonApiSuper, methoods, modifiers);
nonApiSuper = nonApiSuper.getSuperclass();
}
if (methoods.size() > 0) {
// check if the visible members are part of an API interface/class
List apiTypes = new LinkedList();
apiTypes.add(type);
gatherAPISuperTypes(apiTypes, type);
Iterator iterator2 = apiTypes.iterator();
while (iterator2.hasNext()) {
IApiType t2 = (IApiType) iterator2.next();
Set apiMembers = new HashSet();
gatherVisibleMethods(t2, apiMembers, modifiers);
methoods.removeAll(apiMembers);
if (methoods.size() == 0) {
// there are no visible methods left that are not part of an API type/interface
return false;
}
}
if (methoods.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 e) {
ApiPlugin.log(e.getStatus());
return true;
}
}
return false;
}
/**
* 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 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(method.getName(), method.getSignature()));
}
}
}
/**
* 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;
}
/**
* 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 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 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());
return VisibilityModifiers.isAPI(annotations.getVisibility());
}
}