| 1 | /******************************************************************************* | 
| 2 |  * Copyright (c) 2008, 2009 IBM Corporation and others. | 
| 3 |  * All rights reserved. This program and the accompanying materials | 
| 4 |  * are made available under the terms of the Eclipse Public License v1.0 | 
| 5 |  * which accompanies this distribution, and is available at | 
| 6 |  * http://www.eclipse.org/legal/epl-v10.html | 
| 7 |  * | 
| 8 |  * Contributors: | 
| 9 |  *     IBM Corporation - initial API and implementation | 
| 10 |  *******************************************************************************/ | 
| 11 | package org.eclipse.pde.api.tools.internal.builder; | 
| 12 |   | 
| 13 | import java.util.Set; | 
| 14 |   | 
| 15 | import org.eclipse.core.runtime.CoreException; | 
| 16 | import org.eclipse.core.runtime.IStatus; | 
| 17 | import org.eclipse.core.runtime.Status; | 
| 18 | import org.eclipse.jdt.core.Flags; | 
| 19 | import org.eclipse.jdt.core.IType; | 
| 20 | import org.eclipse.jface.text.BadLocationException; | 
| 21 | import org.eclipse.jface.text.IDocument; | 
| 22 | import org.eclipse.jface.text.Position; | 
| 23 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; | 
| 24 | import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations; | 
| 25 | import org.eclipse.pde.api.tools.internal.provisional.IApiDescription; | 
| 26 | import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers; | 
| 27 | import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers; | 
| 28 | import org.eclipse.pde.api.tools.internal.provisional.builder.IReference; | 
| 29 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; | 
| 30 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; | 
| 31 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiMember; | 
| 32 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiMethod; | 
| 33 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiType; | 
| 34 | import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; | 
| 35 | import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes; | 
| 36 | import org.eclipse.pde.api.tools.internal.util.Signatures; | 
| 37 |   | 
| 38 | import com.ibm.icu.text.MessageFormat; | 
| 39 |   | 
| 40 | /** | 
| 41 |  * Detects leaks in method return types and parameters | 
| 42 |  *  | 
| 43 |  * @since 1.1 | 
| 44 |  * @noextend This class is not intended to be subclassed by clients. | 
| 45 |  */ | 
| 46 | public abstract class MethodLeakDetector extends AbstractLeakProblemDetector { | 
| 47 |   | 
| 48 |         /** | 
| 49 |          * @param nonApiPackageNames | 
| 50 |          */ | 
| 51 |         public MethodLeakDetector(Set nonApiPackageNames) { | 
| 52 |                 super(nonApiPackageNames); | 
| 53 |         } | 
| 54 |   | 
| 55 |         /* (non-Javadoc) | 
| 56 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#getElementType(org.eclipse.pde.api.tools.internal.provisional.model.IReference) | 
| 57 |          */ | 
| 58 |         protected int getElementType(IReference reference) { | 
| 59 |                 return IElementDescriptor.METHOD; | 
| 60 |         } | 
| 61 |   | 
| 62 |         /* (non-Javadoc) | 
| 63 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#getProblemKind() | 
| 64 |          */ | 
| 65 |         protected int getProblemKind() { | 
| 66 |                 return IApiProblem.API_LEAK; | 
| 67 |         } | 
| 68 |   | 
| 69 |         /* (non-Javadoc) | 
| 70 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#getSeverityKey() | 
| 71 |          */ | 
| 72 |         protected String getSeverityKey() { | 
| 73 |                 return IApiProblemTypes.LEAK_METHOD_RETURN_TYPE ; | 
| 74 |         } | 
| 75 |   | 
| 76 |         /* (non-Javadoc) | 
| 77 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#isProblem(org.eclipse.pde.api.tools.internal.provisional.model.IReference) | 
| 78 |          */ | 
| 79 |         protected boolean isProblem(IReference reference) { | 
| 80 |                 IApiMethod method = (IApiMethod) reference.getMember(); | 
| 81 |                 IApiType type = (IApiType) reference.getResolvedReference(); | 
| 82 |                 try { | 
| 83 |                         // referenced type is non-API | 
| 84 |                         IApiAnnotations annotations = type.getApiComponent().getApiDescription().resolveAnnotations(type.getHandle()); | 
| 85 |                         if (annotations != null) { | 
| 86 |                                 if (VisibilityModifiers.isPrivate(annotations.getVisibility())) { | 
| 87 |                                         if ((Flags.AccProtected & method.getModifiers()) > 0) { | 
| 88 |                                                 // ignore protected members if contained in a @noextend type | 
| 89 |                                                 // TODO: we could perform this check before resolution - it's on the source location | 
| 90 |                                                 IApiDescription description = method.getApiComponent().getApiDescription(); | 
| 91 |                                                 annotations = description.resolveAnnotations(method.getHandle().getEnclosingType()); | 
| 92 |                                                 if (annotations == null || RestrictionModifiers.isExtendRestriction(annotations.getRestrictions())) { | 
| 93 |                                                         // ignore | 
| 94 |                                                         return false; | 
| 95 |                                                 } | 
| 96 |                                         } | 
| 97 |                                         return true; | 
| 98 |                                 } | 
| 99 |                         } else { | 
| 100 |                                 // could be a reference to a top level secondary/non-public type | 
| 101 |                                 if (isEnclosingTypeVisible(type)) { | 
| 102 |                                         // this is an unexpected condition - the enclosing type is visible, but it has no annotations - log an error | 
| 103 |                                         ApiPlugin.log( | 
| 104 |                                                 new Status( | 
| 105 |                                                         IStatus.INFO, ApiPlugin.PLUGIN_ID, | 
| 106 |                                                         MessageFormat.format(BuilderMessages.AbstractTypeLeakDetector_vis_type_has_no_api_description, new String[]{type.getName()}))); | 
| 107 |                                 } else { | 
| 108 |                                         // enclosing type is not visible - this is a problem | 
| 109 |                                         return true; | 
| 110 |                                 } | 
| 111 |                         } | 
| 112 |                 } catch (CoreException e) { | 
| 113 |                         ApiPlugin.log(e); | 
| 114 |                 } | 
| 115 |                 return false; | 
| 116 |         } | 
| 117 |          | 
| 118 |         /* (non-Javadoc) | 
| 119 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#getMessageArgs(org.eclipse.pde.api.tools.internal.provisional.model.IReference) | 
| 120 |          */ | 
| 121 |         protected String[] getMessageArgs(IReference reference) throws CoreException { | 
| 122 |                 IApiMethod method = (IApiMethod) reference.getMember(); | 
| 123 |                 IApiType type = (IApiType) reference.getResolvedReference(); | 
| 124 |                 return new String[] { | 
| 125 |                                 getSimpleTypeName(type),  | 
| 126 |                                 getSimpleTypeName(method),  | 
| 127 |                                 Signatures.getMethodSignature(method)}; | 
| 128 |         } | 
| 129 |          | 
| 130 |         /* (non-Javadoc) | 
| 131 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#getQualifiedMessageArgs(org.eclipse.pde.api.tools.internal.provisional.model.IReference) | 
| 132 |          */ | 
| 133 |         protected String[] getQualifiedMessageArgs(IReference reference) throws CoreException { | 
| 134 |                 IApiMethod method = (IApiMethod) reference.getMember(); | 
| 135 |                 IApiType type = (IApiType) reference.getResolvedReference(); | 
| 136 |                 return new String[] { | 
| 137 |                                 getQualifiedTypeName(type),  | 
| 138 |                                 getQualifiedTypeName(method),  | 
| 139 |                                 Signatures.getMethodSignature(method)}; | 
| 140 |         } | 
| 141 |          | 
| 142 |         /* (non-Javadoc) | 
| 143 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#getSourceRange(org.eclipse.jdt.core.IType, org.eclipse.jface.text.IDocument, org.eclipse.pde.api.tools.internal.provisional.model.IReference) | 
| 144 |          */ | 
| 145 |         protected Position getSourceRange(IType type, IDocument doc, IReference reference) throws CoreException, BadLocationException { | 
| 146 |                 return getSourceRangeForMethod(type, reference, (IApiMethod) reference.getMember()); | 
| 147 |         } | 
| 148 |   | 
| 149 |         /* (non-Javadoc) | 
| 150 |          * @see org.eclipse.pde.api.tools.internal.provisional.search.IApiProblemDetector#considerReference(org.eclipse.pde.api.tools.internal.provisional.model.IReference) | 
| 151 |          */ | 
| 152 |         public boolean considerReference(IReference reference) { | 
| 153 |                 if (isNonAPIReference(reference)) { | 
| 154 |                         IApiMember member = reference.getMember(); | 
| 155 |                         if (member != null && matchesSourceModifiers(member) && matchesSourceApiRestrictions(member)) { | 
| 156 |                                 retainReference(reference); | 
| 157 |                                 return true; | 
| 158 |                         } | 
| 159 |                 } | 
| 160 |                 return false; | 
| 161 |         } | 
| 162 |          | 
| 163 |         /** | 
| 164 |          * Returns if the source API restrictions for the given member matches the restrictions in the parent API description | 
| 165 |          * @param member | 
| 166 |          * @return true if it matches, false otherwise | 
| 167 |          */ | 
| 168 |         protected boolean matchesSourceApiRestrictions(IApiMember member) { | 
| 169 |                 IApiComponent apiComponent = member.getApiComponent(); | 
| 170 |                 try { | 
| 171 |                         IApiMethod method = (IApiMethod) member; | 
| 172 |                         IApiAnnotations annotations = apiComponent.getApiDescription().resolveAnnotations(method.getHandle()); | 
| 173 |                         if (annotations != null) { | 
| 174 |                                 if (VisibilityModifiers.isAPI(annotations.getVisibility())) { | 
| 175 |                                         int ares = annotations.getRestrictions(); | 
| 176 |                                         if(ares != 0) { | 
| 177 |                                                 if(method.isConstructor()) { | 
| 178 |                                                         return (ares & RestrictionModifiers.NO_REFERENCE) == 0; | 
| 179 |                                                 } | 
| 180 |                                                 if((ares & RestrictionModifiers.NO_OVERRIDE) == 0) { | 
| 181 |                                                         IApiAnnotations annot = apiComponent.getApiDescription().resolveAnnotations(method.getEnclosingType().getHandle()); | 
| 182 |                                                         int pres = 0; | 
| 183 |                                                         if(annot != null) { | 
| 184 |                                                                 pres = annot.getRestrictions(); | 
| 185 |                                                         } | 
| 186 |                                                         return (ares & RestrictionModifiers.NO_REFERENCE) != 0 && (!Flags.isFinal(method.getModifiers()) | 
| 187 |                                                                         && !Flags.isStatic(method.getModifiers()) | 
| 188 |                                                                         && !Flags.isFinal(method.getEnclosingType().getModifiers()) | 
| 189 |                                                                         && ((pres & RestrictionModifiers.NO_EXTEND) == 0)); | 
| 190 |                                                 } | 
| 191 |                                                 return  (ares & RestrictionModifiers.NO_REFERENCE) == 0;  | 
| 192 |                                         } | 
| 193 |                                         else { | 
| 194 |                                                 return !(Flags.isProtected(method.getModifiers()) && Flags.isFinal(method.getEnclosingType().getModifiers())); | 
| 195 |                                         } | 
| 196 |                                 } | 
| 197 |                         } else { | 
| 198 |                                 return true; | 
| 199 |                         } | 
| 200 |                 } catch (CoreException e) { | 
| 201 |                         ApiPlugin.log(e); | 
| 202 |                 } | 
| 203 |                 return false; | 
| 204 |         }         | 
| 205 |          | 
| 206 |         /** | 
| 207 |          * Returns if the source modifiers for the given member match the ones specified in the detector | 
| 208 |          * @param member | 
| 209 |          * @return true if the modifiers match, false otherwise | 
| 210 |          */ | 
| 211 |         protected boolean matchesSourceModifiers(IApiMember member) { | 
| 212 |                 IApiMember lmember = member; | 
| 213 |                 while (lmember != null) { | 
| 214 |                         int modifiers = lmember.getModifiers(); | 
| 215 |                         if (Flags.isPublic(modifiers) || Flags.isProtected(modifiers)) { | 
| 216 |                                 try { | 
| 217 |                                         lmember = lmember.getEnclosingType(); | 
| 218 |                                 } catch (CoreException e) { | 
| 219 |                                         ApiPlugin.log(e.getStatus()); | 
| 220 |                                         return false; | 
| 221 |                                 } | 
| 222 |                         } else { | 
| 223 |                                 return false; | 
| 224 |                         } | 
| 225 |                 } | 
| 226 |                 return true; | 
| 227 |         }         | 
| 228 |   | 
| 229 | } |