| 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.Iterator; | 
| 14 | import java.util.LinkedList; | 
| 15 | import java.util.List; | 
| 16 |   | 
| 17 | import org.eclipse.core.resources.IProject; | 
| 18 | import org.eclipse.core.resources.IResource; | 
| 19 | import org.eclipse.core.runtime.CoreException; | 
| 20 | import org.eclipse.core.runtime.NullProgressMonitor; | 
| 21 | import org.eclipse.jdt.core.Flags; | 
| 22 | import org.eclipse.jdt.core.ICompilationUnit; | 
| 23 | import org.eclipse.jdt.core.IField; | 
| 24 | import org.eclipse.jdt.core.IJavaElement; | 
| 25 | import org.eclipse.jdt.core.IJavaProject; | 
| 26 | import org.eclipse.jdt.core.IMethod; | 
| 27 | import org.eclipse.jdt.core.ISourceRange; | 
| 28 | import org.eclipse.jdt.core.IType; | 
| 29 | import org.eclipse.jdt.core.JavaModelException; | 
| 30 | import org.eclipse.jdt.core.Signature; | 
| 31 | import org.eclipse.jface.text.BadLocationException; | 
| 32 | import org.eclipse.jface.text.IDocument; | 
| 33 | import org.eclipse.jface.text.Position; | 
| 34 | import org.eclipse.pde.api.tools.internal.model.PluginProjectApiComponent; | 
| 35 | import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory; | 
| 36 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; | 
| 37 | import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants; | 
| 38 | import org.eclipse.pde.api.tools.internal.provisional.builder.IApiProblemDetector; | 
| 39 | import org.eclipse.pde.api.tools.internal.provisional.builder.IReference; | 
| 40 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; | 
| 41 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement; | 
| 42 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiField; | 
| 43 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiMember; | 
| 44 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiMethod; | 
| 45 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiType; | 
| 46 | import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; | 
| 47 | import org.eclipse.pde.api.tools.internal.util.Signatures; | 
| 48 | import org.eclipse.pde.api.tools.internal.util.Util; | 
| 49 |   | 
| 50 | /** | 
| 51 |  * @since 1.1 | 
| 52 |  */ | 
| 53 | public abstract class AbstractProblemDetector implements IApiProblemDetector { | 
| 54 |   | 
| 55 |         /** | 
| 56 |          * Constant used for controlling tracing in the problem detectors | 
| 57 |          */ | 
| 58 |         protected static boolean DEBUG = Util.DEBUG; | 
| 59 |          | 
| 60 |         /** | 
| 61 |          * Method used for initializing tracing in the problem detectors | 
| 62 |          */ | 
| 63 |         public static void setDebug(boolean debugValue) { | 
| 64 |                 DEBUG = debugValue || Util.DEBUG; | 
| 65 |         } | 
| 66 |          | 
| 67 |         /** | 
| 68 |          * List of potential {@link IReference} problems | 
| 69 |          */ | 
| 70 |         private List fPotentialProblems = new LinkedList(); | 
| 71 |          | 
| 72 |         /** | 
| 73 |          * Retains the reference for further analysis. | 
| 74 |          *   | 
| 75 |          * @param reference reference | 
| 76 |          */ | 
| 77 |         protected void retainReference(IReference reference) { | 
| 78 |                 fPotentialProblems.add(reference); | 
| 79 |         } | 
| 80 |   | 
| 81 |         /** | 
| 82 |          * Return the list of retained references. | 
| 83 |          *  | 
| 84 |          * @return references | 
| 85 |          */ | 
| 86 |         protected List getRetainedReferences() { | 
| 87 |                 return fPotentialProblems; | 
| 88 |         } | 
| 89 |          | 
| 90 |         /** | 
| 91 |          * Creates a problem for a specific reference in the workspace | 
| 92 |          *  | 
| 93 |          * @param reference reference | 
| 94 |          * @param associated java project (with reference source location) | 
| 95 |          * @return problem or <code>null</code> if none | 
| 96 |          * @exception CoreException if something goes wrong | 
| 97 |          */ | 
| 98 |         protected IApiProblem createProblem(IReference reference, IJavaProject javaProject) { | 
| 99 |                 IProject project = javaProject.getProject(); | 
| 100 |                 if (ApiPlugin.getDefault().getSeverityLevel(getSeverityKey(), project) == ApiPlugin.SEVERITY_IGNORE) { | 
| 101 |                         return null; | 
| 102 |                 }                 | 
| 103 |                 try { | 
| 104 |                         String lookupName = getTypeName(reference.getMember()).replace('$', '.'); | 
| 105 |                         IType type = javaProject.findType(lookupName, new NullProgressMonitor()); | 
| 106 |                         if (type == null) { | 
| 107 |                                 return null; | 
| 108 |                         } | 
| 109 |                         ICompilationUnit compilationUnit = type.getCompilationUnit(); | 
| 110 |                         if (compilationUnit == null) { | 
| 111 |                                 return null; | 
| 112 |                         } | 
| 113 |                         IResource resource = Util.getResource(project, type); | 
| 114 |                         if (resource == null) { | 
| 115 |                                 return null; | 
| 116 |                         } | 
| 117 |                         int charStart = -1; | 
| 118 |                         int charEnd = -1; | 
| 119 |                         int lineNumber = reference.getLineNumber(); | 
| 120 |                         IJavaElement element = compilationUnit; | 
| 121 |                         if (!Util.isManifest(resource.getProjectRelativePath()) && !type.isBinary()) { | 
| 122 |                                 IDocument document = Util.getDocument(compilationUnit); | 
| 123 |                                 // retrieve line number, char start and char end | 
| 124 |                                 if (lineNumber > 0) { | 
| 125 |                                         lineNumber--; | 
| 126 |                                 } | 
| 127 |                                 // get the source range for the problem | 
| 128 |                                 try { | 
| 129 |                                         Position pos = getSourceRange(type, document, reference); | 
| 130 |                                         if (pos != null) { | 
| 131 |                                                 charStart = pos.getOffset(); | 
| 132 |                                                 if (charStart != -1) { | 
| 133 |                                                         charEnd = charStart + pos.getLength(); | 
| 134 |                                                         lineNumber = document.getLineOfOffset(charStart); | 
| 135 |                                                 } | 
| 136 |                                         } | 
| 137 |                                 } catch (CoreException e) { | 
| 138 |                                         ApiPlugin.log(e); | 
| 139 |                                         return null; | 
| 140 |                                 } | 
| 141 |                                 catch (BadLocationException e) { | 
| 142 |                                         ApiPlugin.log(e); | 
| 143 |                                         return null; | 
| 144 |                                 } | 
| 145 |                                 if(charStart > -1) { | 
| 146 |                                         element = compilationUnit.getElementAt(charStart); | 
| 147 |                                 } | 
| 148 |                         } | 
| 149 |                         return ApiProblemFactory.newApiUsageProblem(resource.getProjectRelativePath().toPortableString(), | 
| 150 |                                         type.getFullyQualifiedName(), | 
| 151 |                                         getMessageArgs(reference),  | 
| 152 |                                         new String[] {IApiMarkerConstants.MARKER_ATTR_HANDLE_ID, IApiMarkerConstants.API_MARKER_ATTR_ID},  | 
| 153 |                                         new Object[] {(element == null ? compilationUnit.getHandleIdentifier() : element.getHandleIdentifier()), | 
| 154 |                                                                    new Integer(IApiMarkerConstants.API_USAGE_MARKER_ID)},  | 
| 155 |                                         lineNumber,  | 
| 156 |                                         charStart,  | 
| 157 |                                         charEnd,  | 
| 158 |                                         getElementType(reference),  | 
| 159 |                                         getProblemKind(), | 
| 160 |                                         getProblemFlags(reference)); | 
| 161 |                 } catch (CoreException e) { | 
| 162 |                         ApiPlugin.log(e); | 
| 163 |                 } | 
| 164 |                 return null; | 
| 165 |         } | 
| 166 |          | 
| 167 |         /** | 
| 168 |          * Returns the source range to include in the associated problem or <code>null</code> | 
| 169 |          * if a valid source range could not be computed. | 
| 170 |          *  | 
| 171 |          * @param type resolved type where the reference occurs | 
| 172 |          * @param doc source document of the type | 
| 173 |          * @param reference associated reference | 
| 174 |          * @return source range as a position | 
| 175 |          */ | 
| 176 |         protected abstract Position getSourceRange(IType type, IDocument doc, IReference reference) throws CoreException, BadLocationException; | 
| 177 |   | 
| 178 |         /** | 
| 179 |          * Returns the element type the problem is reported on. | 
| 180 |          *  | 
| 181 |          * @return | 
| 182 |          */ | 
| 183 |         protected abstract int getElementType(IReference reference); | 
| 184 |          | 
| 185 |         /** | 
| 186 |          * Returns problem flags, if any. | 
| 187 |          *  | 
| 188 |          * @param reference | 
| 189 |          * @return problem flags | 
| 190 |          */ | 
| 191 |         protected abstract int getProblemFlags(IReference reference); | 
| 192 |          | 
| 193 |         /** | 
| 194 |          * Returns problem message arguments | 
| 195 |          *  | 
| 196 |          * @return message arguments | 
| 197 |          */ | 
| 198 |         protected abstract String[] getMessageArgs(IReference reference) throws CoreException; | 
| 199 |          | 
| 200 |         /** | 
| 201 |          * Returns problem message arguments to be used in headless build | 
| 202 |          *  | 
| 203 |          * @return message arguments | 
| 204 |          */         | 
| 205 |         protected abstract String[] getQualifiedMessageArgs(IReference reference) throws CoreException; | 
| 206 |          | 
| 207 |         /** | 
| 208 |          * Returns the kind of problem to create | 
| 209 |          *  | 
| 210 |          * @return problem kind | 
| 211 |          */ | 
| 212 |         protected abstract int getProblemKind(); | 
| 213 |          | 
| 214 |         /** | 
| 215 |          * Returns the key used to lookup problem severity. | 
| 216 |          *  | 
| 217 |          * @return problem severity key | 
| 218 |          */ | 
| 219 |         protected abstract String getSeverityKey(); | 
| 220 |          | 
| 221 |         /** | 
| 222 |          * Returns the fully qualified type name associated with the given member. | 
| 223 |          *  | 
| 224 |          * @param member | 
| 225 |          * @return fully qualified type name | 
| 226 |          */ | 
| 227 |         protected String getTypeName(IApiMember member) throws CoreException { | 
| 228 |                 switch (member.getType()) { | 
| 229 |                         case IApiElement.TYPE: { | 
| 230 |                                 IApiType type = (IApiType) member; | 
| 231 |                                 if(type.isAnonymous()) { | 
| 232 |                                         return getTypeName(member.getEnclosingType()); | 
| 233 |                                 } | 
| 234 |                                 else if(type.isLocal()) { | 
| 235 |                                         return getTypeName(member.getEnclosingType()); | 
| 236 |                                 } | 
| 237 |                                 return member.getName(); | 
| 238 |                         } | 
| 239 |                         default: { | 
| 240 |                                 return getTypeName(member.getEnclosingType()); | 
| 241 |                         } | 
| 242 |                 } | 
| 243 |         } | 
| 244 |          | 
| 245 |         /** | 
| 246 |          * Returns the qualified type name to display. This method delegates to the  | 
| 247 |          * {@link Signatures} class to build the display signatures | 
| 248 |          * @param member | 
| 249 |          * @return fully qualified display signature for the given {@link IApiType} or enclosing | 
| 250 |          * type if the member is not a type itself | 
| 251 |          * @throws CoreException | 
| 252 |          */ | 
| 253 |         protected String getQualifiedTypeName(IApiMember member) throws CoreException { | 
| 254 |                 switch (member.getType()) { | 
| 255 |                         case IApiElement.TYPE: { | 
| 256 |                                 IApiType type = (IApiType) member; | 
| 257 |                                 if(type.isAnonymous()) { | 
| 258 |                                         return getQualifiedTypeName(member.getEnclosingType()); | 
| 259 |                                 } | 
| 260 |                                 else if(type.isLocal()) { | 
| 261 |                                         String name = getTypeName(member.getEnclosingType()); | 
| 262 |                                         int idx = name.indexOf('$'); | 
| 263 |                                         if(idx > -1) { | 
| 264 |                                                 return name.substring(0, idx); | 
| 265 |                                         } | 
| 266 |                                         return name; | 
| 267 |                                 } | 
| 268 |                                 return Signatures.getQualifiedTypeSignature((IApiType) member); | 
| 269 |                         } | 
| 270 |                         default: { | 
| 271 |                                 return getQualifiedTypeName(member.getEnclosingType()); | 
| 272 |                         } | 
| 273 |                 } | 
| 274 |         } | 
| 275 |          | 
| 276 |         /** | 
| 277 |          * Returns the unqualified type name associated with the given member. | 
| 278 |          *  | 
| 279 |          * @param member | 
| 280 |          * @return unqualified type name | 
| 281 |          */ | 
| 282 |         protected String getSimpleTypeName(IApiMember member) throws CoreException { | 
| 283 |                 switch (member.getType()) { | 
| 284 |                         case IApiElement.TYPE: { | 
| 285 |                                 IApiType type = (IApiType) member; | 
| 286 |                                 if(type.isAnonymous()) { | 
| 287 |                                         return getSimpleTypeName(type.getEnclosingType()); | 
| 288 |                                 } | 
| 289 |                                 else if(type.isLocal()) { | 
| 290 |                                         String name = getSimpleTypeName(member.getEnclosingType()); | 
| 291 |                                         int idx = name.indexOf('$'); | 
| 292 |                                         if(idx > -1) { | 
| 293 |                                                 return name.substring(0, idx); | 
| 294 |                                         } | 
| 295 |                                         return name; | 
| 296 |                                 } | 
| 297 |                                 return Signatures.getTypeName(Signatures.getTypeSignature(type)); | 
| 298 |                         } | 
| 299 |                         default: | 
| 300 |                                 return getSimpleTypeName(member.getEnclosingType()); | 
| 301 |                 } | 
| 302 |         }         | 
| 303 |          | 
| 304 |         /** | 
| 305 |          * Default strategy for when no source position can be computed: creates | 
| 306 |          * a {@link Position} for the name of the given {@link IType}. Returns <code>null</code> in the event | 
| 307 |          * the given {@link IType} is <code>null</code> or the name range cannot be computed for the type. | 
| 308 |          *  | 
| 309 |          * @param type the type | 
| 310 |          * @param reference the reference | 
| 311 |          * @throws CoreException | 
| 312 |          * @return returns a default {@link Position} for the name range of the given {@link IType} | 
| 313 |          */ | 
| 314 |         protected Position defaultSourcePosition(IType type, IReference reference) throws CoreException { | 
| 315 |                 if(type != null) { | 
| 316 |                         ISourceRange range = type.getNameRange(); | 
| 317 |                         if(range != null) { | 
| 318 |                                 return new Position(range.getOffset(), range.getLength()); | 
| 319 |                         } | 
| 320 |                 } | 
| 321 |                 return null; | 
| 322 |         } | 
| 323 |          | 
| 324 |         /** | 
| 325 |          * Finds the method name to select on the given line of code starting from the given index. | 
| 326 |          * This method will recurse to find a method name in the even there is a name clash with the type. | 
| 327 |          * For example: | 
| 328 |          * <pre> | 
| 329 |          *                 MyType type = new MyType(); | 
| 330 |          * </pre> | 
| 331 |          * If we are trying to find the constructor method call we have a name collision (and the first occurrence of MyType would be selected).  | 
| 332 |          * <br> | 
| 333 |          * A name is determined to be a method name if it is followed by a '(' character (excluding spaces) | 
| 334 |          * @param namepart | 
| 335 |          * @param line | 
| 336 |          * @param index | 
| 337 |          * @return the index of the method name on the given line or -1 if not found | 
| 338 |          */ | 
| 339 |         protected int findMethodNameStart(String namepart, String line, int index) { | 
| 340 |                 int start = line.indexOf(namepart, index); | 
| 341 |                 if(start < 0) { | 
| 342 |                         return -1; | 
| 343 |                 } | 
| 344 |                 int offset = start+namepart.length(); | 
| 345 |                 while(line.charAt(offset) == ' ') { | 
| 346 |                         offset++; | 
| 347 |                 } | 
| 348 |                 if(line.charAt(offset) == '(' || | 
| 349 |                                 line.charAt(offset) == '<') { | 
| 350 |                         return start; | 
| 351 |                 } | 
| 352 |                 return findMethodNameStart(namepart, line, offset); | 
| 353 |         }         | 
| 354 |          | 
| 355 |         /* (non-Javadoc) | 
| 356 |          * @see org.eclipse.pde.api.tools.internal.provisional.search.IApiProblemDetector#createProblems() | 
| 357 |          */ | 
| 358 |         public List createProblems() { | 
| 359 |                 List references = getRetainedReferences(); | 
| 360 |                 List problems = new LinkedList(); | 
| 361 |                 Iterator iterator = references.iterator(); | 
| 362 |                 while (iterator.hasNext()) { | 
| 363 |                         IReference reference = (IReference) iterator.next(); | 
| 364 |                         if (reference.getResolvedReference() == null) { | 
| 365 |                                 // TODO: unresolved reference | 
| 366 |                         } else { | 
| 367 |                                 if (isProblem(reference)) { | 
| 368 |                                         try { | 
| 369 |                                                 IApiProblem problem = null; | 
| 370 |                                                 IApiComponent component = reference.getMember().getApiComponent(); | 
| 371 |                                                 if (component instanceof PluginProjectApiComponent) { | 
| 372 |                                                         PluginProjectApiComponent ppac = (PluginProjectApiComponent) component; | 
| 373 |                                                         IJavaProject project = ppac.getJavaProject(); | 
| 374 |                                                         problem = createProblem(reference, project); | 
| 375 |                                                 } else { | 
| 376 |                                                         problem = createProblem(reference); | 
| 377 |                                                 } | 
| 378 |                                                 if (problem != null) { | 
| 379 |                                                         problems.add(problem); | 
| 380 |                                                 } | 
| 381 |                                         } catch (CoreException e) { | 
| 382 |                                                 ApiPlugin.log(e.getStatus()); | 
| 383 |                                         } | 
| 384 |                                 } | 
| 385 |                         } | 
| 386 |                 } | 
| 387 |                 return problems; | 
| 388 |         } | 
| 389 |   | 
| 390 |         /** | 
| 391 |          * Returns whether the resolved reference is a real problem. | 
| 392 |          *  | 
| 393 |          * @param reference | 
| 394 |          * @return whether a problem | 
| 395 |          */ | 
| 396 |         protected boolean isProblem(IReference reference) { | 
| 397 |                 //by default fragment -> host references are not problems  | 
| 398 |                 //https://bugs.eclipse.org/bugs/show_bug.cgi?id=255659 | 
| 399 |                 IApiMember member = reference.getResolvedReference(); | 
| 400 |                 if(member != null) { | 
| 401 |                         IApiMember local = reference.getMember(); | 
| 402 |                         try { | 
| 403 |                                 IApiComponent lcomp = local.getApiComponent(); | 
| 404 |                                 if(lcomp != null && lcomp.isFragment()) { | 
| 405 |                                         return !lcomp.getHost().equals(member.getApiComponent()); | 
| 406 |                                 } | 
| 407 |                         } | 
| 408 |                         catch(CoreException ce) { | 
| 409 |                                 ApiPlugin.log(ce); | 
| 410 |                         } | 
| 411 |                 } | 
| 412 |                 return true; | 
| 413 |         } | 
| 414 |          | 
| 415 |         /** | 
| 416 |          * Tries to find the given {@link IApiMethod} in the given {@link IType}. If a matching method is not | 
| 417 |          * found <code>null</code> is returned  | 
| 418 |          * @param type the type top look in for the given {@link IApiMethod} | 
| 419 |          * @param method the {@link IApiMethod} to look for | 
| 420 |          * @return the {@link IMethod} from the given {@link IType} that matches the given {@link IApiMethod} or <code>null</code> if no | 
| 421 |          * matching method is found | 
| 422 |          * @throws JavaModelException | 
| 423 |          * @throws CoreException | 
| 424 |          */ | 
| 425 |         protected IMethod findMethodInType(IType type, IApiMethod method) throws JavaModelException, CoreException { | 
| 426 |                 String[] parameterTypes = Signature.getParameterTypes(method.getSignature()); | 
| 427 |                 for (int i = 0; i < parameterTypes.length; i++) { | 
| 428 |                         parameterTypes[i] = parameterTypes[i].replace('/', '.'); | 
| 429 |                 } | 
| 430 |                 String methodname = method.getName(); | 
| 431 |                 if(method.isConstructor()) { | 
| 432 |                         IApiType enclosingType = method.getEnclosingType(); | 
| 433 |                         if (enclosingType.isMemberType() && !Flags.isStatic(enclosingType.getModifiers())) { | 
| 434 |                                 // remove the synthetic argument that corresponds to the enclosing type | 
| 435 |                                 int length = parameterTypes.length - 1; | 
| 436 |                                 System.arraycopy(parameterTypes, 1, (parameterTypes = new String[length]), 0, length); | 
| 437 |                         } | 
| 438 |                         methodname = enclosingType.getSimpleName(); | 
| 439 |                 } | 
| 440 |                 IMethod Qmethod = type.getMethod(methodname, parameterTypes); | 
| 441 |                 IMethod[] methods = type.getMethods(); | 
| 442 |                 IMethod match = null; | 
| 443 |                 for (int i = 0; i < methods.length; i++) { | 
| 444 |                         IMethod m = methods[i]; | 
| 445 |                         if (m.isSimilar(Qmethod)) { | 
| 446 |                                 match = m; | 
| 447 |                                 break; | 
| 448 |                         } | 
| 449 |                 } | 
| 450 |                 return match; | 
| 451 |         } | 
| 452 |          | 
| 453 |         /** | 
| 454 |          * Returns the source range for the given {@link IApiMethod} within the given {@link IType} | 
| 455 |          * @param type the type to look for the method within | 
| 456 |          * @param reference the reference the method comes from | 
| 457 |          * @param method the {@link IApiMethod} to look for the source range for | 
| 458 |          * @return the {@link ISourceRange} in the {@link IType} enclosing the given {@link IApiMethod} | 
| 459 |          * @throws CoreException | 
| 460 |          * @throws JavaModelException | 
| 461 |          */ | 
| 462 |         protected Position getSourceRangeForMethod(IType type, IReference reference, IApiMethod method) throws CoreException, JavaModelException { | 
| 463 |                 IMethod match = findMethodInType(type, method); | 
| 464 |                 Position pos = null; | 
| 465 |                 if (match != null) { | 
| 466 |                         ISourceRange range = match.getNameRange(); | 
| 467 |                         if(range != null) { | 
| 468 |                                 pos = new Position(range.getOffset(), range.getLength()); | 
| 469 |                         } | 
| 470 |                 } | 
| 471 |                 if(pos == null) { | 
| 472 |                         return defaultSourcePosition(type, reference); | 
| 473 |                 } | 
| 474 |                 return pos; | 
| 475 |         } | 
| 476 |          | 
| 477 |         /** | 
| 478 |          * Returns the source range to use for the given field within the given {@link IType} | 
| 479 |          * @param type the type to look in for the given {@link IApiField} | 
| 480 |          * @param reference the reference the field is involved in | 
| 481 |          * @param field the field to find the range for | 
| 482 |          * @return the {@link ISourceRange} in the given {@link IType} that encloses the given {@link IApiField}  | 
| 483 |          * @throws JavaModelException | 
| 484 |          * @throws CoreException | 
| 485 |          */ | 
| 486 |         protected Position getSourceRangeForField(IType type, IReference reference, IApiField field) throws JavaModelException, CoreException { | 
| 487 |                 IField javaField = type.getField(field.getName()); | 
| 488 |                 Position pos = null; | 
| 489 |                 if (javaField.exists()) { | 
| 490 |                         ISourceRange range = javaField.getNameRange(); | 
| 491 |                         if(range != null) { | 
| 492 |                                 pos = new Position(range.getOffset(), range.getLength());  | 
| 493 |                         } | 
| 494 |                 } | 
| 495 |                 if(pos == null) { | 
| 496 |                         return defaultSourcePosition(type, reference); | 
| 497 |                 } | 
| 498 |                 return pos; | 
| 499 |         } | 
| 500 |          | 
| 501 |         /** | 
| 502 |          * Returns the range of the name of the given {@link IApiField} to select when creating {@link IApiProblem}s. | 
| 503 |          * Source ranges are computed and tried in the following order: | 
| 504 |          * <ol> | 
| 505 |          * <li>Try the type-qualified name of the variable</li> | 
| 506 |          * <li>Try looking for 'super.variable'</li> | 
| 507 |          * <li>Try looking for 'this.variable'</li> | 
| 508 |          * <li>Try looking for pattern '*.variable'</li> | 
| 509 |          * <li>Else select the entire line optimistically</li> | 
| 510 |          * </ol> | 
| 511 |          * @param field the field to find the name range for | 
| 512 |          * @param document the document to look within | 
| 513 |          * @param reference the reference the field is from | 
| 514 |          * @return the range of text to select, or <code>null</code> if one could not be computed | 
| 515 |          * @throws BadLocationException | 
| 516 |          * @throws CoreException | 
| 517 |          */ | 
| 518 |         protected Position getFieldNameRange(IApiField field, IDocument document, IReference reference) throws BadLocationException, CoreException { | 
| 519 |                 return getFieldNameRange(field.getEnclosingType().getName(), field.getName(), document, reference); | 
| 520 |         } | 
| 521 |         protected Position getFieldNameRange(String typeName, String fieldName, IDocument document, IReference reference) throws BadLocationException, CoreException { | 
| 522 |                 int linenumber = reference.getLineNumber(); | 
| 523 |                 if (linenumber > 0) { | 
| 524 |                         linenumber--; | 
| 525 |                 } | 
| 526 |                 if (linenumber > 0) { | 
| 527 |                         int offset = document.getLineOffset(linenumber); | 
| 528 |                         String line = document.get(offset, document.getLineLength(linenumber)); | 
| 529 |                         String qname = typeName +"."+fieldName; //$NON-NLS-1$ | 
| 530 |                         int first = line.indexOf(qname); | 
| 531 |                         if(first < 0) { | 
| 532 |                                 qname = "super."+fieldName; //$NON-NLS-1$ | 
| 533 |                                 first = line.indexOf(qname); | 
| 534 |                         } | 
| 535 |                         if(first < 0) { | 
| 536 |                                 qname = "this."+fieldName; //$NON-NLS-1$ | 
| 537 |                                 first = line.indexOf(qname); | 
| 538 |                         } | 
| 539 |                         if(first < 0) { | 
| 540 |                                 //try a pattern [.*fieldname]  | 
| 541 |                                 //the field might be ref'd via a constant, e.g. enum constant | 
| 542 |                                 int idx = line.indexOf(fieldName); | 
| 543 |                                 while(idx > -1) { | 
| 544 |                                         if(line.charAt(idx-1) == '.') { | 
| 545 |                                                 first = idx; | 
| 546 |                                                 qname = fieldName; | 
| 547 |                                                 break; | 
| 548 |                                         } | 
| 549 |                                         idx = line.indexOf(fieldName, idx+1); | 
| 550 |                                 } | 
| 551 |                         } | 
| 552 |                         Position pos = null; | 
| 553 |                         if(first > -1) { | 
| 554 |                                 pos = new Position(offset + first, qname.length()); | 
| 555 |                         } | 
| 556 |                         else { | 
| 557 |                                 //optimistically select the whole line since we can't find the correct variable name and we can't just select | 
| 558 |                                 //the first occurrence | 
| 559 |                                 pos = new Position(offset, line.length()); | 
| 560 |                         } | 
| 561 |                         return pos; | 
| 562 |                 } | 
| 563 |                 return null; | 
| 564 |         } | 
| 565 |          | 
| 566 |         /** | 
| 567 |          * Searches for the name of a method at the line number specified in the given | 
| 568 |          * reference. | 
| 569 |          *  | 
| 570 |          * @param name method name | 
| 571 |          * @param document document to search in | 
| 572 |          * @param reference provides line number | 
| 573 |          * @return method name range | 
| 574 |          * @throws CoreException | 
| 575 |          */ | 
| 576 |         protected Position getMethodNameRange(boolean isContructor, String name, IDocument document, IReference reference) throws CoreException, BadLocationException { | 
| 577 |                 int linenumber = reference.getLineNumber(); | 
| 578 |                 if (linenumber > 0) { | 
| 579 |                         linenumber--; | 
| 580 |                 } | 
| 581 |                 String methodname = name; | 
| 582 |                 int idx = methodname.indexOf('$'); | 
| 583 |                 if(idx > -1) { | 
| 584 |                         methodname = methodname.substring(0, idx); | 
| 585 |                 } | 
| 586 |                 idx = methodname.indexOf(Signatures.getLT()); | 
| 587 |                 if(idx > -1) { | 
| 588 |                         methodname = methodname.substring(0, idx); | 
| 589 |                 } | 
| 590 |                 int offset = document.getLineOffset(linenumber); | 
| 591 |                 String line = document.get(offset, document.getLineLength(linenumber)); | 
| 592 |                 int start = line.indexOf('='); | 
| 593 |                 if(start < 0) { | 
| 594 |                         if (isContructor) { | 
| 595 |                                 // new keyword should only be checked if the method is a constructor | 
| 596 |                                 start = line.indexOf("new"); //$NON-NLS-1$ | 
| 597 |                                 if(start < 0) { | 
| 598 |                                         start = 0; | 
| 599 |                                 } | 
| 600 |                         } else { | 
| 601 |                                 start = 0; | 
| 602 |                         } | 
| 603 |                 } | 
| 604 |                 else { | 
| 605 |                         char charat = line.charAt(start-1); | 
| 606 |                         //make sure its not '==' | '!=' | '<=' | '>=' | 
| 607 |                         if(line.charAt(start+1) == '=' || | 
| 608 |                                         charat == '!' || charat == '<' || charat == '>') { | 
| 609 |                                 start = 0; | 
| 610 |                         } | 
| 611 |                 } | 
| 612 |                 int first = findMethodNameStart(methodname, line, start); | 
| 613 |                 if(first < 0) { | 
| 614 |                         methodname = "super"; //$NON-NLS-1$ | 
| 615 |                         first = findMethodNameStart(methodname, line, start); | 
| 616 |                 } | 
| 617 |                 if(first > -1) { | 
| 618 |                         return new Position(offset + first, methodname.length()); | 
| 619 |                 } | 
| 620 |                 return null; | 
| 621 |         } | 
| 622 |         /* (non-Javadoc) | 
| 623 |          * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#createProblem(org.eclipse.pde.api.tools.internal.provisional.model.IReference) | 
| 624 |          */ | 
| 625 |         protected IApiProblem createProblem(IReference reference) throws CoreException { | 
| 626 |                 int lineNumber = reference.getLineNumber(); | 
| 627 |                 if (lineNumber > 0) { | 
| 628 |                         lineNumber--; | 
| 629 |                 } | 
| 630 |                 String ltypename = getTypeName(reference.getMember()); | 
| 631 |                 return ApiProblemFactory.newApiUsageProblem( | 
| 632 |                                 null, | 
| 633 |                                 ltypename, | 
| 634 |                                 getQualifiedMessageArgs(reference), | 
| 635 |                                 new String[] {IApiMarkerConstants.API_MARKER_ATTR_ID},  | 
| 636 |                                 new Object[] {new Integer(IApiMarkerConstants.API_USAGE_MARKER_ID)},  | 
| 637 |                                 lineNumber,  | 
| 638 |                                 IApiProblem.NO_CHARRANGE,  | 
| 639 |                                 IApiProblem.NO_CHARRANGE, | 
| 640 |                                 getElementType(reference),  | 
| 641 |                                 getProblemKind(), | 
| 642 |                                 getProblemFlags(reference)); | 
| 643 |         }         | 
| 644 |          | 
| 645 | } |