blob: 1edde8cfb86f1abcccbb6c9131394c032149849f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2013 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.api.tools.internal.builder;
import java.util.HashMap;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.Factory;
import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations;
import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers;
import org.eclipse.pde.api.tools.internal.provisional.builder.IReference;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiMember;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiType;
import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem;
import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes;
/**
* Detects when a type illegally implements another type.
*
* @since 1.1
*/
public class IllegalImplementsProblemDetector extends AbstractIllegalTypeReference {
/**
* Map of directly implemented interfaces to implement restricted
* super-interfaces
*/
private HashMap<String, IApiType> fRestrictedInterfaces = new HashMap<>();
@Override
public int getReferenceKinds() {
return IReference.REF_IMPLEMENTS;
}
@Override
protected int getProblemKind() {
return IApiProblem.ILLEGAL_IMPLEMENT;
}
@Override
protected String getSeverityKey() {
return IApiProblemTypes.ILLEGAL_IMPLEMENT;
}
@Override
public boolean considerReference(IReference reference, IProgressMonitor monitor) {
try {
if (super.considerReference(reference, monitor)) {
return true;
}
IApiType type = (IApiType) reference.getMember();
IApiType[] inters = type.getSuperInterfaces();
IApiType inter = null;
for (IApiType interLoop : inters) {
if (interLoop.getName().equals(reference.getReferencedTypeName())) {
inter = interLoop;
break;
}
}
if (inter != null && findRestrictedSuperinterfaces(type.getApiComponent(), reference.getReferencedTypeName(), inter)) {
retainReference(reference);
return true;
}
} catch (CoreException ce) {
if (ApiPlugin.DEBUG_PROBLEM_DETECTOR) {
ApiPlugin.log(ce);
}
checkIfDisposed(reference.getMember().getApiComponent(), monitor);
}
return false;
}
@Override
protected boolean isProblem(IReference reference, IProgressMonitor monitor) {
try {
if (isIllegalType(reference)) {
return super.isProblem(reference, monitor);
}
if (fRestrictedInterfaces.size() > 0) {
IApiMember member = reference.getMember();
if (member.getType() == IApiElement.TYPE) {
IApiType itype = fRestrictedInterfaces.get(reference.getReferencedTypeName());
return itype != null && !isImplemented(((IApiType) member).getSuperclass(), itype.getName());
}
}
return true;
} catch (CoreException ce) {
if (ApiPlugin.DEBUG_PROBLEM_DETECTOR) {
ApiPlugin.log(ce);
}
IApiMember member = reference.getMember();
if (member != null) {
checkIfDisposed(member.getApiComponent(), monitor);
}
}
return super.isProblem(reference, monitor);
}
@Override
protected String[] getMessageArgs(IReference reference) throws CoreException {
String[] args = super.getMessageArgs(reference);
if (!isIllegalType(reference) && fRestrictedInterfaces.size() > 0) {
IApiType type = (IApiType) reference.getResolvedReference();
IApiType inter = fRestrictedInterfaces.get(type.getName());
if (inter != null) {
String[] newargs = new String[args.length + 1];
System.arraycopy(args, 0, newargs, 0, args.length);
newargs[args.length] = getSimpleTypeName(inter);
return newargs;
}
}
return args;
}
@Override
protected String[] getQualifiedMessageArgs(IReference reference) throws CoreException {
String[] args = super.getQualifiedMessageArgs(reference);
if (!isIllegalType(reference) && fRestrictedInterfaces.size() > 0) {
IApiType type = (IApiType) reference.getResolvedReference();
IApiType inter = fRestrictedInterfaces.get(type.getName());
if (inter != null) {
String[] newargs = new String[args.length + 1];
System.arraycopy(args, 0, newargs, 0, args.length);
newargs[args.length] = inter.getName();
return newargs;
}
}
return args;
}
@Override
protected int getProblemFlags(IReference reference) {
if (isIllegalType(reference)) {
return super.getProblemFlags(reference);
}
IApiType type = (IApiType) reference.getMember();
if (type.isLocal()) {
return IApiProblem.INDIRECT_LOCAL_REFERENCE;
}
return IApiProblem.INDIRECT_REFERENCE;
}
/**
* Returns if the given type implements any of the given interfaces anywhere
* in its lineage
*
* @param type
* @param iname
* @return true if all of the interfaces are implemented, false otherwise
* @throws CoreException
*/
private boolean isImplemented(IApiType type, final String iname) throws CoreException {
if (type == null) {
return false;
}
if (isImplemented(iname, type.getSuperInterfaces())) {
return true;
}
return isImplemented(type.getSuperclass(), iname);
}
/**
* Inspects the hierarchy of super-interfaces to determine if an interface
* with the given name is implemented or not
*
* @param iname the name of the interface to find
* @param interfaces the collection of interfaces to inspect
* @return true if the interface is implemented, false otherwise
* @throws CoreException
*/
private boolean isImplemented(final String iname, IApiType[] interfaces) throws CoreException {
if (interfaces.length == 0) {
return false;
}
for (IApiType interfaceLoop : interfaces) {
if (interfaceLoop.getName().equals(iname)) {
return true;
}
if (isImplemented(iname, interfaceLoop.getSuperInterfaces())) {
return true;
}
}
return false;
}
/**
* Finds all of the implements restricted interfaces in the hierarchy of
* this given type
*
* @param originalcomponent the original {@link IApiComponent}
* @param entryinterface the name of the interface we originally entered the
* recursion with
* @param type the {@link IApiType} to inspect the interfaces of
* @throws CoreException
*/
private boolean findRestrictedSuperinterfaces(final IApiComponent originalcomponent, final String entryinterface, IApiType type) throws CoreException {
IApiType[] inters = type.getSuperInterfaces();
if (inters.length == 0) {
return false;
}
IApiAnnotations annot = null;
IApiComponent comp = null;
for (IApiType inter : inters) {
comp = inter.getApiComponent();
if (comp == null) {
continue;
}
if (!comp.equals(originalcomponent)) {
annot = comp.getApiDescription().resolveAnnotations(Factory.typeDescriptor(inter.getName()));
if (annot != null && RestrictionModifiers.isImplementRestriction(annot.getRestrictions())) {
fRestrictedInterfaces.put(entryinterface, inter);
return true;
}
}
return findRestrictedSuperinterfaces(originalcomponent, entryinterface, inter);
}
return false;
}
}