| 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.HashSet; | 
| 14 | import java.util.Iterator; | 
| 15 | import java.util.LinkedList; | 
| 16 | import java.util.List; | 
| 17 | import java.util.Set; | 
| 18 |   | 
| 19 | import org.eclipse.core.runtime.CoreException; | 
| 20 | import org.eclipse.jdt.core.Flags; | 
| 21 | import org.eclipse.pde.api.tools.internal.model.MethodKey; | 
| 22 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; | 
| 23 | import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations; | 
| 24 | import org.eclipse.pde.api.tools.internal.provisional.IApiDescription; | 
| 25 | import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers; | 
| 26 | import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers; | 
| 27 | import org.eclipse.pde.api.tools.internal.provisional.builder.IReference; | 
| 28 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; | 
| 29 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiField; | 
| 30 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiMethod; | 
| 31 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiType; | 
| 32 | import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; | 
| 33 | import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes; | 
| 34 |   | 
| 35 | /** | 
| 36 |  * Detects leaked super types. | 
| 37 |  *  | 
| 38 |  * @since 1.1 | 
| 39 |  */ | 
| 40 | public class LeakExtendsProblemDetector extends AbstractTypeLeakDetector { | 
| 41 |   | 
| 42 |         /** | 
| 43 |          * @param nonApiPackageNames | 
| 44 |          */ | 
| 45 |         public LeakExtendsProblemDetector(Set nonApiPackageNames) { | 
| 46 |                 super(nonApiPackageNames); | 
| 47 |         } | 
| 48 |   | 
| 49 |         /* (non-Javadoc) | 
| 50 |          * @see org.eclipse.pde.api.tools.internal.provisional.search.IApiProblemDetector#getReferenceKinds() | 
| 51 |          */ | 
| 52 |         public int getReferenceKinds() { | 
| 53 |                 return IReference.REF_EXTENDS; | 
| 54 |         } | 
| 55 |   | 
| 56 |         /* (non-Javadoc) | 
| 57 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#getSeverityKey() | 
| 58 |          */ | 
| 59 |         protected String getSeverityKey() { | 
| 60 |                 return IApiProblemTypes.LEAK_EXTEND; | 
| 61 |         } | 
| 62 |          | 
| 63 |         /* (non-Javadoc) | 
| 64 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#getProblemFlags(org.eclipse.pde.api.tools.internal.provisional.model.IReference) | 
| 65 |          */ | 
| 66 |         protected int getProblemFlags(IReference reference) { | 
| 67 |                 return IApiProblem.LEAK_EXTENDS; | 
| 68 |         } | 
| 69 |          | 
| 70 |         /* (non-Javadoc) | 
| 71 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractTypeLeakDetector#isProblem(org.eclipse.pde.api.tools.internal.provisional.model.IReference) | 
| 72 |          */ | 
| 73 |         public boolean isProblem(IReference reference) { | 
| 74 |                 if (super.isProblem(reference)) { | 
| 75 |                         // check the use restrictions on the API type (can be extended or not) | 
| 76 |                         IApiType type = (IApiType) reference.getMember(); | 
| 77 |                         IApiComponent component = type.getApiComponent(); | 
| 78 |                         try { | 
| 79 |                                 if (type.isClass()) { | 
| 80 |                                         int modifiers = 0; | 
| 81 |                                         IApiAnnotations annotations = component.getApiDescription().resolveAnnotations(type.getHandle()); | 
| 82 |                                         if (annotations != null) { | 
| 83 |                                                 // if annotations are null, the reference should not have been retained | 
| 84 |                                                 // as it indicates a reference from a top level non public type | 
| 85 |                                                 if (RestrictionModifiers.isExtendRestriction(annotations.getRestrictions())) { | 
| 86 |                                                         // The no extend restriction means only public members can be seen | 
| 87 |                                                         modifiers = Flags.AccPublic; | 
| 88 |                                                 } else { | 
| 89 |                                                         if (Flags.isFinal(type.getModifiers())) { | 
| 90 |                                                                 // if final then only public members can be seen | 
| 91 |                                                                 modifiers = Flags.AccPublic; | 
| 92 |                                                         } else { | 
| 93 |                                                                 // public and protected members can be seen | 
| 94 |                                                                 modifiers = Flags.AccPublic | Flags.AccProtected; | 
| 95 |                                                         } | 
| 96 |                                                 } | 
| 97 |                                                 IApiType nonApiSuper = type.getSuperclass(); | 
| 98 |                                                 // collect all visible methods in non-API types | 
| 99 |                                                 Set methods = new HashSet(); | 
| 100 |                                                 while (!isAPIType(nonApiSuper)) { | 
| 101 |                                                         if (hasVisibleField(nonApiSuper, modifiers)) { | 
| 102 |                                                                 // a visible field in a non-API type is a definite leak | 
| 103 |                                                                 return true; | 
| 104 |                                                         } | 
| 105 |                                                         gatherVisibleMethods(nonApiSuper, methods, modifiers); | 
| 106 |                                                         nonApiSuper = nonApiSuper.getSuperclass(); | 
| 107 |                                                 } | 
| 108 |                                                 if (methods.size() > 0) { | 
| 109 |                                                         // check if the visible members are part of an API interface/class | 
| 110 |                                                         List apiTypes = new LinkedList(); | 
| 111 |                                                         apiTypes.add(type); | 
| 112 |                                                         gatherAPISuperTypes(apiTypes, type); | 
| 113 |                                                         Iterator iterator2 = apiTypes.iterator(); | 
| 114 |                                                         while (iterator2.hasNext()) { | 
| 115 |                                                                 IApiType t2 = (IApiType) iterator2.next(); | 
| 116 |                                                                 Set apiMembers = new HashSet(); | 
| 117 |                                                                 gatherVisibleMethods(t2, apiMembers, modifiers); | 
| 118 |                                                                 methods.removeAll(apiMembers); | 
| 119 |                                                                 if (methods.size() == 0) { | 
| 120 |                                                                         // there are no visible methods left that are not part of an API type/interface | 
| 121 |                                                                         return false; | 
| 122 |                                                                 }         | 
| 123 |                                                         } | 
| 124 |                                                         if (methods.size() > 0) { | 
| 125 |                                                                 // there are visible members that are not part of an API type/interface | 
| 126 |                                                                 return true; | 
| 127 |                                                         } | 
| 128 |                                                 } | 
| 129 |                                         } | 
| 130 |                                 } else { | 
| 131 |                                         // don't process interfaces, enums, annotations | 
| 132 |                                         return true; | 
| 133 |                                 } | 
| 134 |                         } catch (CoreException ce) { | 
| 135 |                                 if(DEBUG) { | 
| 136 |                                         ApiPlugin.log(ce); | 
| 137 |                                 } | 
| 138 |                                 return true; | 
| 139 |                         } | 
| 140 |                 } | 
| 141 |                 return false; | 
| 142 |         } | 
| 143 |   | 
| 144 |         /** | 
| 145 |          * Adds all visible methods to the given set in the specified type. | 
| 146 |          *  | 
| 147 |          * @param type type to analyze | 
| 148 |          * @param members set to add methods to | 
| 149 |          * @param modifiers visibilities to consider | 
| 150 |          */ | 
| 151 |         private void gatherVisibleMethods(IApiType type, Set members, int modifiers) { | 
| 152 |                 IApiMethod[] methods = type.getMethods(); | 
| 153 |                 for (int i = 0; i < methods.length; i++) { | 
| 154 |                         IApiMethod method = methods[i]; | 
| 155 |                         if ((method.getModifiers() & modifiers) > 0 && !method.isConstructor() && !method.isSynthetic()) { | 
| 156 |                                 members.add(new MethodKey(method.getName(), method.getSignature())); | 
| 157 |                         } | 
| 158 |                 } | 
| 159 |         } | 
| 160 |          | 
| 161 |         /** | 
| 162 |          * Returns whether the given type has any visible fields base on the given | 
| 163 |          * visibility flags to consider. A field is visible signals a definite leak. | 
| 164 |          *  | 
| 165 |          * @param type type to analyze | 
| 166 |          * @param modifiers visibilities to consider | 
| 167 |          * @return whether there are any visible fields | 
| 168 |          */ | 
| 169 |         private boolean hasVisibleField(IApiType type, int modifiers) { | 
| 170 |                 IApiField[] fields = type.getFields(); | 
| 171 |                 for (int i = 0; i < fields.length; i++) { | 
| 172 |                         IApiField field = fields[i]; | 
| 173 |                         if ((field.getModifiers() & modifiers) > 0) { | 
| 174 |                                 return true; | 
| 175 |                         } | 
| 176 |                 } | 
| 177 |                 return false; | 
| 178 |         } | 
| 179 |          | 
| 180 |         /** | 
| 181 |          * Adds all API super types of the given type to the given list in | 
| 182 |          * top down order. | 
| 183 |          *  | 
| 184 |          * @param superTypes list to add to | 
| 185 |          * @param type type being processed | 
| 186 |          */ | 
| 187 |         private void gatherAPISuperTypes(List superTypes, IApiType type) throws CoreException { | 
| 188 |                 if (type != null) { | 
| 189 |                         if (isAPIType(type)) { | 
| 190 |                                 superTypes.add(0, type); | 
| 191 |                         } | 
| 192 |                         gatherAPISuperTypes(superTypes, type.getSuperclass()); | 
| 193 |                         IApiType[] interfaces = type.getSuperInterfaces(); | 
| 194 |                         if (interfaces != null) { | 
| 195 |                                 for (int i = 0; i < interfaces.length; i++) { | 
| 196 |                                         if (isAPIType(interfaces[i])) { | 
| 197 |                                                 superTypes.add(interfaces[i]); | 
| 198 |                                                 gatherAPISuperTypes(superTypes, interfaces[i]); | 
| 199 |                                         } | 
| 200 |                                 } | 
| 201 |                         } | 
| 202 |                 } | 
| 203 |         }         | 
| 204 |   | 
| 205 |         /** | 
| 206 |          * Returns whether the given type has API visibility. | 
| 207 |          *  | 
| 208 |          * @param type type | 
| 209 |          * @return whether the given type has API visibility | 
| 210 |          */ | 
| 211 |         private boolean isAPIType(IApiType type) throws CoreException { | 
| 212 |                 IApiDescription description = type.getApiComponent().getApiDescription(); | 
| 213 |                 IApiAnnotations annotations = description.resolveAnnotations(type.getHandle()); | 
| 214 |                 if (annotations == null) { | 
| 215 |                         // top level non-public top can have no annotations - they are not API | 
| 216 |                         return false; | 
| 217 |                 } | 
| 218 |                 return VisibilityModifiers.isAPI(annotations.getVisibility()); | 
| 219 |         }         | 
| 220 |          | 
| 221 | } |