1 /******************************************************************************* 2 * Copyright (c) 2000, 2019 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Stephan Herrmann - contribution for bug 337868 - [compiler][model] incomplete support for package-info.java when using SearchableEnvironment 14 *******************************************************************************/ 15 package org.eclipse.jdt.internal.core; 16 17 import java.io.File; 18 import java.util.*; 19 import java.util.function.Function; 20 21 import org.eclipse.core.resources.*; 22 import org.eclipse.core.runtime.IPath; 23 import org.eclipse.core.runtime.IProgressMonitor; 24 import org.eclipse.jdt.core.IClasspathEntry; 25 import org.eclipse.jdt.core.ICompilationUnit; 26 import org.eclipse.jdt.core.IField; 27 import org.eclipse.jdt.core.IInitializer; 28 import org.eclipse.jdt.core.IJavaElement; 29 import org.eclipse.jdt.core.IJavaProject; 30 import org.eclipse.jdt.core.IMethod; 31 import org.eclipse.jdt.core.IModuleDescription; 32 import org.eclipse.jdt.core.IOrdinaryClassFile; 33 import org.eclipse.jdt.core.IPackageFragment; 34 import org.eclipse.jdt.core.IPackageFragmentRoot; 35 import org.eclipse.jdt.core.IType; 36 import org.eclipse.jdt.core.JavaCore; 37 import org.eclipse.jdt.core.JavaModelException; 38 import org.eclipse.jdt.core.compiler.CharOperation; 39 import org.eclipse.jdt.internal.compiler.ast.ASTNode; 40 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; 41 import org.eclipse.jdt.internal.compiler.env.AccessRestriction; 42 import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; 43 import org.eclipse.jdt.internal.compiler.env.IBinaryType; 44 import org.eclipse.jdt.internal.compiler.env.IModule; 45 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; 46 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; 47 import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt; 48 import org.eclipse.jdt.internal.compiler.util.SuffixConstants; 49 import org.eclipse.jdt.internal.core.AbstractModule.AutoModule; 50 import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject; 51 import org.eclipse.jdt.internal.core.util.Messages; 52 import org.eclipse.jdt.internal.core.util.Util; 53 54 /** 55 * A <code>NameLookup</code> provides name resolution within a Java project. 56 * The name lookup facility uses the project's classpath to prioritize the 57 * order in which package fragments are searched when resolving a name. 58 * 59 * <p>Name lookup only returns a handle when the named element actually 60 * exists in the model; otherwise <code>null</code> is returned. 61 * 62 * <p>There are two logical sets of methods within this interface. Methods 63 * which start with <code>find*</code> are intended to be convenience methods for quickly 64 * finding an element within another element; for instance, for finding a class within a 65 * package. The other set of methods all begin with <code>seek*</code>. These methods 66 * do comprehensive searches of the <code>IJavaProject</code> returning hits 67 * in real time through an <code>IJavaElementRequestor</code>. 68 * 69 */ 70 @SuppressWarnings({"rawtypes", "unchecked"}) 71 public class NameLookup implements SuffixConstants { 72 73 private static IModuleDescription NO_MODULE = new SourceModule(null, "Not a module") { /* empty */ }; //$NON-NLS-1$ 74 75 public static class Answer { 76 public IType type; 77 public IModuleDescription module; 78 AccessRestriction restriction; 79 IClasspathEntry entry; Answer(IType type, AccessRestriction restriction, IClasspathEntry entry)80 Answer(IType type, AccessRestriction restriction, IClasspathEntry entry) { 81 this(type, restriction, entry, null); 82 } Answer(IType type, AccessRestriction restriction, IClasspathEntry entry, IModuleDescription module)83 Answer(IType type, AccessRestriction restriction, IClasspathEntry entry, IModuleDescription module) { 84 this.type = type; 85 this.restriction = restriction; 86 this.entry = entry; 87 this.module = module; 88 } Answer(IModuleDescription module)89 Answer(IModuleDescription module) { 90 this.module = module; 91 this.restriction = null; 92 } ignoreIfBetter()93 public boolean ignoreIfBetter() { 94 return this.restriction != null && this.restriction.ignoreIfBetter(); 95 } 96 /* 97 * Returns whether this answer is better than the other awswer. 98 * (accessible is better than discouraged, which is better than 99 * non-accessible) 100 */ isBetter(Answer otherAnswer)101 public boolean isBetter(Answer otherAnswer) { 102 if (otherAnswer == null) return true; 103 if (this.restriction == null) return true; 104 return otherAnswer.restriction != null 105 && this.restriction.getProblemId() < otherAnswer.restriction.getProblemId(); 106 } 107 @Override toString()108 public String toString() { 109 StringBuilder builder = new StringBuilder(this.type.toString()); 110 builder.append("from ") //$NON-NLS-1$ 111 .append(this.module); 112 return builder.toString(); 113 } 114 } 115 116 private class Selector implements IJavaElementRequestor { 117 public List<IPackageFragment> pkgFragments; 118 Selector(String moduleName)119 public Selector(String moduleName) { 120 this.pkgFragments = new ArrayList<>(); 121 } 122 123 @Override acceptField(IField field)124 public void acceptField(IField field) { 125 // do nothing 126 } 127 128 @Override acceptInitializer(IInitializer initializer)129 public void acceptInitializer(IInitializer initializer) { 130 // do nothing 131 } 132 133 @Override acceptMemberType(IType type)134 public void acceptMemberType(IType type) { 135 // do nothing 136 } 137 138 @Override acceptMethod(IMethod method)139 public void acceptMethod(IMethod method) { 140 // do nothing 141 } 142 143 @Override acceptPackageFragment(IPackageFragment packageFragment)144 public void acceptPackageFragment(IPackageFragment packageFragment) { 145 this.pkgFragments.add(packageFragment); 146 } 147 148 @Override acceptType(IType type)149 public void acceptType(IType type) { 150 // do nothing 151 } 152 153 @Override acceptModule(IModuleDescription module)154 public void acceptModule(IModuleDescription module) { 155 // do nothing 156 } 157 158 @Override isCanceled()159 public boolean isCanceled() { 160 // TODO Auto-generated method stub 161 return false; 162 } 163 } 164 165 // TODO (jerome) suppress the accept flags (qualified name is sufficient to find a type) 166 /** 167 * Accept flag for specifying classes. 168 */ 169 public static final int ACCEPT_CLASSES = ASTNode.Bit2; 170 171 /** 172 * Accept flag for specifying interfaces. 173 */ 174 public static final int ACCEPT_INTERFACES = ASTNode.Bit3; 175 176 /** 177 * Accept flag for specifying enums. 178 */ 179 public static final int ACCEPT_ENUMS = ASTNode.Bit4; 180 181 /** 182 * Accept flag for specifying records. 183 * @noreference This field is not intended to be referenced by clients. 184 * TODO Clients should add code if any special treatment is needed. 185 */ 186 public static final int ACCEPT_RECORDS = ASTNode.Bit25; 187 188 /** 189 * Accept flag for specifying annotations. 190 */ 191 public static final int ACCEPT_ANNOTATIONS = ASTNode.Bit5; 192 193 /* 194 * Accept flag for all kinds of types 195 */ 196 public static final int ACCEPT_ALL = ACCEPT_CLASSES | ACCEPT_INTERFACES | ACCEPT_ENUMS | ACCEPT_ANNOTATIONS | ACCEPT_RECORDS; 197 198 public static boolean VERBOSE = false; 199 200 private static final IType[] NO_TYPES = {}; 201 202 /** 203 * The <code>IPackageFragmentRoot</code>'s associated 204 * with the classpath of this NameLookup facility's 205 * project. 206 */ 207 protected IPackageFragmentRoot[] packageFragmentRoots; 208 209 /** 210 * Table that maps package names to lists of package fragment roots 211 * that contain such a package known by this name lookup facility. 212 * To allow > 1 package fragment with the same name, values are 213 * arrays of package fragment roots ordered as they appear on the 214 * classpath. 215 * Note if the list is of size 1, then the IPackageFragmentRoot object 216 * replaces the array. 217 */ 218 protected HashtableOfArrayToObject packageFragments; 219 220 /** 221 * Reverse map from root path to corresponding resolved CP entry 222 * (so as to be able to figure inclusion/exclusion rules) 223 */ 224 protected Map<IPackageFragmentRoot,IClasspathEntry> rootToResolvedEntries; 225 226 protected Map<IPackageFragmentRoot,IModuleDescription> rootToModule; 227 228 /** 229 * A map from package handles to a map from type name to an IType or an IType[]. 230 * Allows working copies to take precedence over compilation units. 231 */ 232 protected HashMap typesInWorkingCopies; 233 234 public long timeSpentInSeekTypesInSourcePackage = 0; 235 public long timeSpentInSeekTypesInBinaryPackage = 0; 236 237 private JavaProject rootProject; 238 NameLookup( JavaProject rootProject, IPackageFragmentRoot[] packageFragmentRoots, HashtableOfArrayToObject packageFragments, ICompilationUnit[] workingCopies, Map rootToResolvedEntries)239 public NameLookup( 240 JavaProject rootProject, IPackageFragmentRoot[] packageFragmentRoots, 241 HashtableOfArrayToObject packageFragments, 242 ICompilationUnit[] workingCopies, 243 Map rootToResolvedEntries) { 244 this.rootProject = rootProject; 245 long start = -1; 246 if (VERBOSE) { 247 Util.verbose(" BUILDING NameLoopkup"); //$NON-NLS-1$ 248 Util.verbose(" -> pkg roots size: " + (packageFragmentRoots == null ? 0 : packageFragmentRoots.length)); //$NON-NLS-1$ 249 Util.verbose(" -> pkgs size: " + (packageFragments == null ? 0 : packageFragments.size())); //$NON-NLS-1$ 250 Util.verbose(" -> working copy size: " + (workingCopies == null ? 0 : workingCopies.length)); //$NON-NLS-1$ 251 start = System.currentTimeMillis(); 252 } 253 this.rootToModule = new HashMap<>(); 254 this.packageFragmentRoots = packageFragmentRoots; 255 if (workingCopies == null) { 256 this.packageFragments = packageFragments; 257 } else { 258 // clone tables as we're adding packages from working copies 259 try { 260 this.packageFragments = (HashtableOfArrayToObject) packageFragments.clone(); 261 } catch (CloneNotSupportedException e1) { 262 // ignore (implementation of HashtableOfArrayToObject supports cloning) 263 } 264 this.typesInWorkingCopies = new HashMap(); 265 HashtableOfObjectToInt rootPositions = new HashtableOfObjectToInt(); 266 for (int i = 0, length = packageFragmentRoots.length; i < length; i++) { 267 rootPositions.put(packageFragmentRoots[i], i); 268 } 269 for (int i = 0, length = workingCopies.length; i < length; i++) { 270 ICompilationUnit workingCopy = workingCopies[i]; 271 PackageFragment pkg = (PackageFragment) workingCopy.getParent(); 272 IPackageFragmentRoot root = (IPackageFragmentRoot) pkg.getParent(); 273 int rootPosition = rootPositions.get(root); 274 if (rootPosition == -1) 275 continue; // working copy is not visible from this project (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=169970) 276 HashMap typeMap = (HashMap) this.typesInWorkingCopies.get(pkg); 277 if (typeMap == null) { 278 typeMap = new HashMap(); 279 this.typesInWorkingCopies.put(pkg, typeMap); 280 } 281 try { 282 IType[] types = workingCopy.getTypes(); 283 int typeLength = types.length; 284 if (typeLength == 0) { 285 String typeName = Util.getNameWithoutJavaLikeExtension(workingCopy.getElementName()); 286 typeMap.put(typeName, NO_TYPES); 287 } else { 288 for (int j = 0; j < typeLength; j++) { 289 IType type = types[j]; 290 String typeName = type.getElementName(); 291 Object existing = typeMap.get(typeName); 292 if (existing == null) { 293 typeMap.put(typeName, type); 294 } else if (existing instanceof IType) { 295 typeMap.put(typeName, new IType[] {(IType) existing, type}); 296 } else { 297 IType[] existingTypes = (IType[]) existing; 298 int existingTypeLength = existingTypes.length; 299 System.arraycopy(existingTypes, 0, existingTypes = new IType[existingTypeLength+1], 0, existingTypeLength); 300 existingTypes[existingTypeLength] = type; 301 typeMap.put(typeName, existingTypes); 302 } 303 } 304 } 305 } catch (JavaModelException e) { 306 // working copy doesn't exist -> ignore 307 } 308 309 // add root of package fragment to cache 310 String[] pkgName = pkg.names; 311 Object existing = this.packageFragments.get(pkgName); 312 if (existing == null || existing == JavaProjectElementInfo.NO_ROOTS) { 313 this.packageFragments.put(pkgName, root); 314 // ensure super packages (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=119161) 315 // are also in the map 316 JavaProjectElementInfo.addSuperPackageNames(pkgName, this.packageFragments); 317 } else { 318 if (existing instanceof PackageFragmentRoot) { 319 int exisitingPosition = rootPositions.get(existing); 320 if (rootPosition != exisitingPosition) { // if not equal 321 this.packageFragments.put( 322 pkgName, 323 exisitingPosition < rootPosition ? 324 new IPackageFragmentRoot[] {(PackageFragmentRoot) existing, root} : 325 new IPackageFragmentRoot[] {root, (PackageFragmentRoot) existing}); 326 } 327 } else { 328 // insert root in the existing list 329 IPackageFragmentRoot[] roots = (IPackageFragmentRoot[]) existing; 330 int rootLength = roots.length; 331 int insertionIndex = 0; 332 for (int j = 0; j < rootLength; j++) { 333 int existingPosition = rootPositions.get(roots[j]); 334 if (rootPosition > existingPosition) { 335 // root is after this index 336 insertionIndex = j; 337 } else if (rootPosition == existingPosition) { 338 // root already in the existing list 339 insertionIndex = -1; 340 break; 341 } else if (rootPosition < existingPosition) { 342 // root is before this index (thus it is at the insertion index) 343 break; 344 } 345 } 346 if (insertionIndex != -1) { 347 IPackageFragmentRoot[] newRoots = new IPackageFragmentRoot[rootLength+1]; 348 System.arraycopy(roots, 0, newRoots, 0, insertionIndex); 349 newRoots[insertionIndex] = root; 350 System.arraycopy(roots, insertionIndex, newRoots, insertionIndex+1, rootLength-insertionIndex); 351 this.packageFragments.put(pkgName, newRoots); 352 } 353 } 354 } 355 } 356 } 357 358 this.rootToResolvedEntries = rootToResolvedEntries; 359 if (VERBOSE) { 360 Util.verbose(" -> spent: " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ 361 } 362 } 363 364 /** 365 * Returns true if:<ul> 366 * <li>the given type is an existing class and the flag's <code>ACCEPT_CLASSES</code> 367 * bit is on 368 * <li>the given type is an existing interface and the <code>ACCEPT_INTERFACES</code> 369 * bit is on 370 * <li>neither the <code>ACCEPT_CLASSES</code> or <code>ACCEPT_INTERFACES</code> 371 * bit is on 372 * </ul> 373 * Otherwise, false is returned. 374 */ acceptType(IType type, int acceptFlags, boolean isSourceType)375 protected boolean acceptType(IType type, int acceptFlags, boolean isSourceType) { 376 if (acceptFlags == 0 || acceptFlags == ACCEPT_ALL) 377 return true; // no flags or all flags, always accepted 378 try { 379 int kind = isSourceType 380 ? TypeDeclaration.kind(((SourceTypeElementInfo) ((SourceType) type).getElementInfo()).getModifiers()) 381 : TypeDeclaration.kind(((IBinaryType) ((BinaryType) type).getElementInfo()).getModifiers()); 382 switch (kind) { 383 case TypeDeclaration.CLASS_DECL : 384 return (acceptFlags & (ACCEPT_CLASSES | ACCEPT_RECORDS)) != 0; 385 case TypeDeclaration.INTERFACE_DECL : 386 return (acceptFlags & ACCEPT_INTERFACES) != 0; 387 case TypeDeclaration.ENUM_DECL : 388 return (acceptFlags & ACCEPT_ENUMS) != 0; 389 case TypeDeclaration.RECORD_DECL : 390 return (acceptFlags & ACCEPT_RECORDS) != 0; 391 default: 392 //case IGenericType.ANNOTATION_TYPE : 393 return (acceptFlags & ACCEPT_ANNOTATIONS) != 0; 394 } 395 } catch (JavaModelException npe) { 396 return false; // the class is not present, do not accept. 397 } 398 } 399 400 /** 401 * Finds every type in the project whose simple name matches 402 * the prefix, informing the requestor of each hit. The requestor 403 * is polled for cancellation at regular intervals. 404 * 405 * <p>The <code>partialMatch</code> argument indicates partial matches 406 * should be considered. 407 */ findAllTypes(String prefix, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor)408 private void findAllTypes(String prefix, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor) { 409 int count= this.packageFragmentRoots.length; 410 for (int i= 0; i < count; i++) { 411 if (requestor.isCanceled()) 412 return; 413 IPackageFragmentRoot root= this.packageFragmentRoots[i]; 414 IJavaElement[] packages= null; 415 try { 416 packages= root.getChildren(); 417 } catch (JavaModelException npe) { 418 continue; // the root is not present, continue; 419 } 420 if (packages != null) { 421 for (int j= 0, packageCount= packages.length; j < packageCount; j++) { 422 if (requestor.isCanceled()) 423 return; 424 seekTypes(prefix, (IPackageFragment) packages[j], partialMatch, acceptFlags, requestor); 425 } 426 } 427 } 428 } 429 430 /** 431 * Returns the <code>ICompilationUnit</code> which defines the type 432 * named <code>qualifiedTypeName</code>, or <code>null</code> if 433 * none exists. The domain of the search is bounded by the classpath 434 * of the <code>IJavaProject</code> this <code>NameLookup</code> was 435 * obtained from. 436 * <p> 437 * The name must be fully qualified (eg "java.lang.Object", "java.util.Hashtable$Entry") 438 */ findCompilationUnit(String qualifiedTypeName)439 public ICompilationUnit findCompilationUnit(String qualifiedTypeName) { 440 String[] pkgName = CharOperation.NO_STRINGS; 441 String cuName = qualifiedTypeName; 442 443 int index= qualifiedTypeName.lastIndexOf('.'); 444 if (index != -1) { 445 pkgName= Util.splitOn('.', qualifiedTypeName, 0, index); 446 cuName= qualifiedTypeName.substring(index + 1); 447 } 448 index= cuName.indexOf('$'); 449 if (index != -1) { 450 cuName= cuName.substring(0, index); 451 } 452 int pkgIndex = this.packageFragments.getIndex(pkgName); 453 if (pkgIndex != -1) { 454 Object value = this.packageFragments.valueTable[pkgIndex]; 455 // reuse existing String[] 456 pkgName = (String[]) this.packageFragments.keyTable[pkgIndex]; 457 if (value instanceof PackageFragmentRoot) { 458 return findCompilationUnit(pkgName, cuName, (PackageFragmentRoot) value); 459 } else { 460 IPackageFragmentRoot[] roots = (IPackageFragmentRoot[]) value; 461 for (int i= 0; i < roots.length; i++) { 462 PackageFragmentRoot root= (PackageFragmentRoot) roots[i]; 463 ICompilationUnit cu = findCompilationUnit(pkgName, cuName, root); 464 if (cu != null) 465 return cu; 466 } 467 } 468 } 469 return null; 470 } 471 findCompilationUnit(String[] pkgName, String cuName, PackageFragmentRoot root)472 private ICompilationUnit findCompilationUnit(String[] pkgName, String cuName, PackageFragmentRoot root) { 473 if (!root.isArchive()) { 474 IPackageFragment pkg = root.getPackageFragment(pkgName); 475 try { 476 ICompilationUnit[] cus = pkg.getCompilationUnits(); 477 for (int j = 0, length = cus.length; j < length; j++) { 478 ICompilationUnit cu = cus[j]; 479 if (Util.equalsIgnoreJavaLikeExtension(cu.getElementName(), cuName)) 480 return cu; 481 } 482 } catch (JavaModelException e) { 483 // pkg does not exist 484 // -> try next package 485 } 486 } 487 return null; 488 } 489 490 /** 491 * Returns the package fragment whose path matches the given 492 * (absolute) path, or <code>null</code> if none exist. The domain of 493 * the search is bounded by the classpath of the <code>IJavaProject</code> 494 * this <code>NameLookup</code> was obtained from. 495 * The path can be: 496 * - internal to the workbench: "/Project/src" 497 * - external to the workbench: "c:/jdk/classes.zip/java/lang" 498 */ findPackageFragment(IPath path)499 public IPackageFragment findPackageFragment(IPath path) { 500 if (!path.isAbsolute()) { 501 throw new IllegalArgumentException(Messages.path_mustBeAbsolute); 502 } 503 /* 504 * TODO (jerome) this code should rather use the package fragment map to find the candidate package, then 505 * check if the respective enclosing root maps to the one on this given IPath. 506 */ 507 IResource possibleFragment = ResourcesPlugin.getWorkspace().getRoot().findMember(path); 508 if (possibleFragment == null) { 509 //external jar 510 for (int i = 0; i < this.packageFragmentRoots.length; i++) { 511 IPackageFragmentRoot root = this.packageFragmentRoots[i]; 512 if (!root.isExternal()) { 513 continue; 514 } 515 IPath rootPath = root.getPath(); 516 if (rootPath.isPrefixOf(path)) { 517 String name = path.toOSString(); 518 // + 1 is for the File.separatorChar 519 name = name.substring(rootPath.toOSString().length() + 1, name.length()); 520 name = name.replace(File.separatorChar, '.'); 521 IJavaElement[] list = null; 522 try { 523 list = root.getChildren(); 524 } catch (JavaModelException npe) { 525 continue; // the package fragment root is not present; 526 } 527 int elementCount = list.length; 528 for (int j = 0; j < elementCount; j++) { 529 IPackageFragment packageFragment = (IPackageFragment) list[j]; 530 if (nameMatches(name, packageFragment, false)) { 531 return packageFragment; 532 } 533 } 534 } 535 } 536 } else { 537 IJavaElement fromFactory = JavaCore.create(possibleFragment); 538 if (fromFactory == null) { 539 return null; 540 } 541 switch (fromFactory.getElementType()) { 542 case IJavaElement.PACKAGE_FRAGMENT: 543 return (IPackageFragment) fromFactory; 544 case IJavaElement.JAVA_PROJECT: 545 // default package in a default root 546 JavaProject project = (JavaProject) fromFactory; 547 try { 548 IClasspathEntry entry = project.getClasspathEntryFor(path); 549 if (entry != null) { 550 IPackageFragmentRoot root = 551 project.getPackageFragmentRoot(project.getResource()); 552 Object defaultPkgRoot = this.packageFragments.get(CharOperation.NO_STRINGS); 553 if (defaultPkgRoot == null) { 554 return null; 555 } 556 if (defaultPkgRoot instanceof PackageFragmentRoot && defaultPkgRoot.equals(root)) 557 return ((PackageFragmentRoot) root).getPackageFragment(CharOperation.NO_STRINGS); 558 else { 559 IPackageFragmentRoot[] roots = (IPackageFragmentRoot[]) defaultPkgRoot; 560 for (int i = 0; i < roots.length; i++) { 561 if (roots[i].equals(root)) { 562 return ((PackageFragmentRoot) root).getPackageFragment(CharOperation.NO_STRINGS); 563 } 564 } 565 } 566 } 567 } catch (JavaModelException e) { 568 return null; 569 } 570 return null; 571 case IJavaElement.PACKAGE_FRAGMENT_ROOT: 572 return ((PackageFragmentRoot)fromFactory).getPackageFragment(CharOperation.NO_STRINGS); 573 } 574 } 575 return null; 576 } 577 578 /** 579 * Returns the package fragments whose name matches the given 580 * (qualified) name, or <code>null</code> if none exist. 581 * 582 * The name can be: 583 * <ul> 584 * <li>empty: ""</li> 585 * <li>qualified: "pack.pack1.pack2"</li> 586 * </ul> 587 * @param partialMatch partial name matches qualify when <code>true</code>, 588 * only exact name matches qualify when <code>false</code> 589 */ findPackageFragments(String name, boolean partialMatch)590 public IPackageFragment[] findPackageFragments(String name, boolean partialMatch) { 591 return findPackageFragments(name, partialMatch, false); 592 } 593 594 /** 595 * Returns the package fragments whose name matches the given 596 * (qualified) name or pattern, or <code>null</code> if none exist. 597 * 598 * The name can be: 599 * <ul> 600 * <li>empty: ""</li> 601 * <li>qualified: "pack.pack1.pack2"</li> 602 * <li>a pattern: "pack.*.util"</li> 603 * </ul> 604 * @param partialMatch partial name matches qualify when <code>true</code>, 605 * @param patternMatch <code>true</code> when the given name might be a pattern, 606 * <code>false</code> otherwise. 607 */ findPackageFragments(String name, boolean partialMatch, boolean patternMatch)608 public IPackageFragment[] findPackageFragments(String name, boolean partialMatch, boolean patternMatch) { 609 boolean isStarPattern = name.equals("*"); //$NON-NLS-1$ 610 boolean hasPatternChars = isStarPattern || (patternMatch && (name.indexOf('*') >= 0 || name.indexOf('?') >= 0)); 611 if (partialMatch || hasPatternChars) { 612 String[] splittedName = Util.splitOn('.', name, 0, name.length()); 613 IPackageFragment[] oneFragment = null; 614 ArrayList pkgs = null; 615 char[] lowercaseName = hasPatternChars && !isStarPattern ? name.toLowerCase().toCharArray() : null; 616 Object[][] keys = this.packageFragments.keyTable; 617 for (int i = 0, length = keys.length; i < length; i++) { 618 String[] pkgName = (String[]) keys[i]; 619 if (pkgName != null) { 620 boolean match = isStarPattern || (hasPatternChars 621 ? CharOperation.match(lowercaseName, Util.concatCompoundNameToCharArray(pkgName), false) 622 : Util.startsWithIgnoreCase(pkgName, splittedName, partialMatch)); 623 if (match) { 624 Object value = this.packageFragments.valueTable[i]; 625 if (value instanceof PackageFragmentRoot) { 626 IPackageFragment pkg = ((PackageFragmentRoot) value).getPackageFragment(pkgName); 627 if (oneFragment == null) { 628 oneFragment = new IPackageFragment[] {pkg}; 629 } else { 630 if (pkgs == null) { 631 pkgs = new ArrayList(); 632 pkgs.add(oneFragment[0]); 633 } 634 pkgs.add(pkg); 635 } 636 } else { 637 IPackageFragmentRoot[] roots = (IPackageFragmentRoot[]) value; 638 for (int j = 0, length2 = roots.length; j < length2; j++) { 639 PackageFragmentRoot root = (PackageFragmentRoot) roots[j]; 640 IPackageFragment pkg = root.getPackageFragment(pkgName); 641 if (oneFragment == null) { 642 oneFragment = new IPackageFragment[] {pkg}; 643 } else { 644 if (pkgs == null) { 645 pkgs = new ArrayList(); 646 pkgs.add(oneFragment[0]); 647 } 648 pkgs.add(pkg); 649 } 650 } 651 } 652 } 653 } 654 } 655 if (pkgs == null) return oneFragment; 656 int resultLength = pkgs.size(); 657 IPackageFragment[] result = new IPackageFragment[resultLength]; 658 pkgs.toArray(result); 659 return result; 660 } else { 661 String[] splittedName = Util.splitOn('.', name, 0, name.length()); 662 int pkgIndex = this.packageFragments.getIndex(splittedName); 663 if (pkgIndex == -1) 664 return null; 665 Object value = this.packageFragments.valueTable[pkgIndex]; 666 // reuse existing String[] 667 String[] pkgName = (String[]) this.packageFragments.keyTable[pkgIndex]; 668 if (value instanceof PackageFragmentRoot) { 669 return new IPackageFragment[] {((PackageFragmentRoot) value).getPackageFragment(pkgName)}; 670 } else { 671 IPackageFragmentRoot[] roots = (IPackageFragmentRoot[]) value; 672 IPackageFragment[] result = new IPackageFragment[roots.length]; 673 for (int i= 0; i < roots.length; i++) { 674 result[i] = ((PackageFragmentRoot) roots[i]).getPackageFragment(pkgName); 675 } 676 return result; 677 } 678 } 679 } 680 681 /* 682 * Find secondary type for a project. 683 */ findSecondaryType(String packageName, String typeName, IJavaProject project, boolean waitForIndexes, IProgressMonitor monitor)684 private IType findSecondaryType(String packageName, String typeName, IJavaProject project, boolean waitForIndexes, IProgressMonitor monitor) { 685 JavaModelManager manager = JavaModelManager.getJavaModelManager(); 686 try { 687 IJavaProject javaProject = project; 688 Map<String, Map<String, IType>> secondaryTypePaths = manager.secondaryTypes(javaProject, waitForIndexes, monitor); 689 if (secondaryTypePaths.size() > 0) { 690 Map<String, IType> types = secondaryTypePaths.get(packageName==null?"":packageName); //$NON-NLS-1$ 691 if (types != null && types.size() > 0) { 692 IType type = types.get(typeName); 693 if (type != null) { 694 if (JavaModelManager.VERBOSE) { 695 Util.verbose("NameLookup FIND SECONDARY TYPES:"); //$NON-NLS-1$ 696 Util.verbose(" -> pkg name: " + packageName); //$NON-NLS-1$ 697 Util.verbose(" -> type name: " + typeName); //$NON-NLS-1$ 698 Util.verbose(" -> project: "+project.getElementName()); //$NON-NLS-1$ 699 Util.verbose(" -> type: " + type.getElementName()); //$NON-NLS-1$ 700 } 701 return type; 702 } 703 } 704 } 705 } 706 catch (JavaModelException jme) { 707 // give up 708 } 709 return null; 710 } 711 712 /** 713 * Find type in the given modules considering secondary types but without waiting for indexes. 714 * It means that secondary types may be not found under certain circumstances... 715 * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=118789" 716 */ findType(String typeName, String packageName, boolean partialMatch, int acceptFlags, boolean checkRestrictions, IPackageFragmentRoot[] moduleContext)717 public Answer findType(String typeName, String packageName, boolean partialMatch, int acceptFlags, boolean checkRestrictions, IPackageFragmentRoot[] moduleContext) { 718 return findType(typeName, 719 packageName, 720 partialMatch, 721 acceptFlags, 722 true/* consider secondary types */, 723 false/* do NOT wait for indexes */, 724 checkRestrictions, 725 null, 726 moduleContext); 727 } 728 729 /** 730 * Find type considering secondary types but without waiting for indexes. 731 * It means that secondary types may be not found under certain circumstances... 732 * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=118789" 733 */ findType(String typeName, String packageName, boolean partialMatch, int acceptFlags, boolean checkRestrictions)734 public Answer findType(String typeName, String packageName, boolean partialMatch, int acceptFlags, boolean checkRestrictions) { 735 return findType(typeName, 736 packageName, 737 partialMatch, 738 acceptFlags, 739 true/* consider secondary types */, 740 false/* do NOT wait for indexes */, 741 checkRestrictions, 742 null); 743 } 744 /** 745 * Find type. Considering secondary types and waiting for indexes depends on given corresponding parameters. 746 */ findType( String typeName, String packageName, boolean partialMatch, int acceptFlags, boolean considerSecondaryTypes, boolean waitForIndexes, boolean checkRestrictions, IProgressMonitor monitor)747 public Answer findType( 748 String typeName, 749 String packageName, 750 boolean partialMatch, 751 int acceptFlags, 752 boolean considerSecondaryTypes, 753 boolean waitForIndexes, 754 boolean checkRestrictions, 755 IProgressMonitor monitor) { 756 757 return findType(typeName, 758 packageName, 759 partialMatch, 760 acceptFlags, 761 considerSecondaryTypes, 762 waitForIndexes, 763 checkRestrictions, 764 monitor, 765 null); // no module 766 } 767 /** 768 * Find type. Considering secondary types and waiting for indexes depends on given corresponding parameters. 769 */ findType( String typeName, String packageName, boolean partialMatch, int acceptFlags, boolean considerSecondaryTypes, boolean waitForIndexes, boolean checkRestrictions, IProgressMonitor monitor, IPackageFragmentRoot[] moduleContext)770 public Answer findType( 771 String typeName, 772 String packageName, 773 boolean partialMatch, 774 int acceptFlags, 775 boolean considerSecondaryTypes, 776 boolean waitForIndexes, 777 boolean checkRestrictions, 778 IProgressMonitor monitor, 779 IPackageFragmentRoot[] moduleContext) { 780 if (packageName == null || packageName.length() == 0) { 781 packageName= IPackageFragment.DEFAULT_PACKAGE_NAME; 782 } else if (typeName.length() > 0 && ScannerHelper.isLowerCase(typeName.charAt(0))) { 783 // see if this is a known package and not a type 784 if (findPackageFragments(packageName + "." + typeName, false) != null) return null; //$NON-NLS-1$ 785 } 786 787 // Look for concerned package fragments 788 JavaElementRequestor elementRequestor = new JavaElementRequestor(); 789 seekPackageFragments(packageName, false, elementRequestor, moduleContext); 790 IPackageFragment[] packages= elementRequestor.getPackageFragments(); 791 792 // Try to find type in package fragments list 793 IType type = null; 794 int length= packages.length; 795 HashSet projects = null; 796 IJavaProject javaProject = null; 797 Answer suggestedAnswer = null; 798 for (int i= 0; i < length; i++) { 799 type = findType(typeName, packages[i], partialMatch, acceptFlags, waitForIndexes, considerSecondaryTypes); 800 if (type != null) { 801 AccessRestriction accessRestriction = null; 802 PackageFragmentRoot root = (PackageFragmentRoot) type.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); 803 ClasspathEntry entry = (ClasspathEntry) this.rootToResolvedEntries.get(root); 804 if (entry != null) { // reverse map always contains resolved CP entry 805 if (checkRestrictions) { 806 accessRestriction = getViolatedRestriction(typeName, packageName, entry, accessRestriction); 807 } 808 } 809 Answer answer = new Answer(type, accessRestriction, entry, 810 getModuleDescription(this.rootProject, root, this.rootToModule, this.rootToResolvedEntries::get)); 811 if (!answer.ignoreIfBetter()) { 812 if (answer.isBetter(suggestedAnswer)) 813 return answer; 814 } else if (answer.isBetter(suggestedAnswer)) 815 // remember suggestion and keep looking 816 suggestedAnswer = answer; 817 } 818 else if (suggestedAnswer == null && considerSecondaryTypes) { 819 if (javaProject == null) { 820 javaProject = packages[i].getJavaProject(); 821 } else if (projects == null) { 822 if (!javaProject.equals(packages[i].getJavaProject())) { 823 projects = new HashSet(3); 824 projects.add(javaProject); 825 projects.add(packages[i].getJavaProject()); 826 } 827 } else { 828 projects.add(packages[i].getJavaProject()); 829 } 830 } 831 } 832 if (suggestedAnswer != null) 833 // no better answer was found 834 return suggestedAnswer; 835 836 // If type was not found, try to find it as secondary in source folders 837 if (considerSecondaryTypes && javaProject != null) { 838 if (projects == null) { 839 type = findSecondaryType(packageName, typeName, javaProject, waitForIndexes, monitor); 840 } else { 841 Iterator allProjects = projects.iterator(); 842 while (type == null && allProjects.hasNext()) { 843 type = findSecondaryType(packageName, typeName, (IJavaProject) allProjects.next(), waitForIndexes, monitor); 844 } 845 } 846 } 847 if (type != null) { 848 ICompilationUnit unit = type.getCompilationUnit(); 849 if (unit != null && unit.isWorkingCopy()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=421902 850 IType[] types = null; 851 try { 852 types = unit.getTypes(); 853 } catch (JavaModelException e) { 854 return null; 855 } 856 boolean typeFound = false; 857 for (int i = 0, typesLength = types == null ? 0 : types.length; i < typesLength; i++) { 858 if (types[i].getElementName().equals(typeName)) { 859 typeFound = true; 860 break; 861 } 862 } 863 if (!typeFound) type = null; 864 } 865 } 866 return type == null ? null : new Answer(type, null, null); 867 } 868 getModuleDescriptionInfo(IModuleDescription moduleDesc)869 public static IModule getModuleDescriptionInfo(IModuleDescription moduleDesc) { 870 if (moduleDesc != null) { 871 try { 872 if (moduleDesc instanceof AutoModule) { 873 boolean nameFromManifest = ((AutoModule) moduleDesc).isAutoNameFromManifest(); 874 return IModule.createAutomatic(moduleDesc.getElementName().toCharArray(), nameFromManifest); 875 } else { 876 return ((AbstractModule) moduleDesc).getModuleInfo(); 877 } 878 } catch (JavaModelException e) { 879 if (!e.isDoesNotExist()) 880 Util.log(e); 881 } 882 } 883 return null; 884 } 885 886 /** Internal utility, which is able to answer explicit and automatic modules. */ getModuleDescription(JavaProject project, IPackageFragmentRoot root, Map<IPackageFragmentRoot,IModuleDescription> cache, Function<IPackageFragmentRoot,IClasspathEntry> rootToEntry)887 static IModuleDescription getModuleDescription(JavaProject project, IPackageFragmentRoot root, Map<IPackageFragmentRoot,IModuleDescription> cache, Function<IPackageFragmentRoot,IClasspathEntry> rootToEntry) { 888 IModuleDescription module = cache.get(root); 889 if (module != null) 890 return module != NO_MODULE ? module : null; 891 if (!Objects.equals(project, root.getJavaProject())) { 892 IClasspathEntry classpathEntry2 = rootToEntry.apply(root); 893 if (classpathEntry2 instanceof ClasspathEntry) { 894 if (!((ClasspathEntry) classpathEntry2).isModular()) { 895 // not on the module path and not a local source folder 896 cache.put(root, NO_MODULE); 897 return null; 898 } 899 } 900 } 901 try { 902 if (root.getKind() == IPackageFragmentRoot.K_SOURCE) 903 module = root.getJavaProject().getModuleDescription(); // from any root in this project 904 } catch (JavaModelException e) { 905 cache.put(root, NO_MODULE); 906 return null; 907 } 908 if (module == null) { 909 // 2nd attempt: try automatic module: 910 IClasspathEntry classpathEntry = rootToEntry.apply(root); 911 if (classpathEntry instanceof ClasspathEntry) { 912 if (((ClasspathEntry) classpathEntry).isModular()) { 913 module = root.getModuleDescription(); 914 if (module == null) { 915 // modular but no module-info implies this is an automatic module 916 module = ((PackageFragmentRoot) root).getAutomaticModuleDescription(classpathEntry); 917 } 918 } 919 } else if (root instanceof JrtPackageFragmentRoot) { 920 module = root.getModuleDescription(); // always considered modular 921 } 922 } 923 cache.put(root, module != null ? module : NO_MODULE); 924 return module; 925 } 926 getModuleDescriptionInfo(PackageFragmentRoot root)927 public IModule getModuleDescriptionInfo(PackageFragmentRoot root) { 928 IModuleDescription desc = getModuleDescription(this.rootProject, root, this.rootToModule, this.rootToResolvedEntries::get); 929 if (desc != null) { 930 return getModuleDescriptionInfo(desc); 931 } 932 return null; 933 } getViolatedRestriction(String typeName, String packageName, ClasspathEntry entry, AccessRestriction accessRestriction)934 private AccessRestriction getViolatedRestriction(String typeName, String packageName, ClasspathEntry entry, AccessRestriction accessRestriction) { 935 AccessRuleSet accessRuleSet = entry.getAccessRuleSet(); 936 if (accessRuleSet != null) { 937 // TODO (philippe) improve char[] <-> String conversions to avoid performing them on the fly 938 char[][] packageChars = CharOperation.splitOn('.', packageName.toCharArray()); 939 char[] typeChars = typeName.toCharArray(); 940 accessRestriction = accessRuleSet.getViolatedRestriction(CharOperation.concatWith(packageChars, typeChars, '/')); 941 } 942 return accessRestriction; 943 } 944 945 /** 946 * Returns the first type in the given package whose name 947 * matches the given (unqualified) name, or <code>null</code> if none 948 * exist. Specifying a <code>null</code> package will result in no matches. 949 * The domain of the search is bounded by the Java project from which 950 * this name lookup was obtained. 951 * 952 * @param name the name of the type to find 953 * @param pkg the package to search 954 * @param partialMatch partial name matches qualify when <code>true</code>, 955 * only exact name matches qualify when <code>false</code> 956 * @param acceptFlags a bit mask describing if classes, interfaces or both classes and interfaces 957 * are desired results. If no flags are specified, all types are returned. 958 * @param considerSecondaryTypes flag to know whether secondary types has to be considered 959 * during the search 960 * 961 * @see #ACCEPT_CLASSES 962 * @see #ACCEPT_INTERFACES 963 * @see #ACCEPT_ENUMS 964 * @see #ACCEPT_ANNOTATIONS 965 * @see #ACCEPT_RECORDS 966 */ findType(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, boolean waitForIndices, boolean considerSecondaryTypes)967 public IType findType(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, boolean waitForIndices, boolean considerSecondaryTypes) { 968 if (pkg == null) 969 return null; 970 971 SingleTypeRequestor typeRequestor = new SingleTypeRequestor(); 972 seekTypes(name, pkg, partialMatch, acceptFlags, typeRequestor, considerSecondaryTypes); 973 IType type = typeRequestor.getType(); 974 975 if (type == null && considerSecondaryTypes) { 976 type = findSecondaryType(pkg.getElementName(), name, pkg.getJavaProject(), waitForIndices, null); 977 } 978 return type; 979 } 980 981 /** 982 * Returns the first type in the given package whose name 983 * matches the given (unqualified) name, or <code>null</code> if none 984 * exist. Specifying a <code>null</code> package will result in no matches. 985 * The domain of the search is bounded by the Java project from which 986 * this name lookup was obtained. 987 * <br> 988 * Note that this method does not find secondary types. 989 * <br> 990 * @param name the name of the type to find 991 * @param pkg the package to search 992 * @param partialMatch partial name matches qualify when <code>true</code>, 993 * only exact name matches qualify when <code>false</code> 994 * @param acceptFlags a bit mask describing if classes, interfaces or both classes and interfaces 995 * are desired results. If no flags are specified, all types are returned. 996 * 997 * @see #ACCEPT_CLASSES 998 * @see #ACCEPT_INTERFACES 999 * @see #ACCEPT_ENUMS 1000 * @see #ACCEPT_ANNOTATIONS 1001 * @see #ACCEPT_RECORDS 1002 */ findType(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags)1003 public IType findType(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags) { 1004 if (pkg == null) return null; 1005 1006 // Return first found (ignore duplicates). 1007 SingleTypeRequestor typeRequestor = new SingleTypeRequestor(); 1008 seekTypes(name, pkg, partialMatch, acceptFlags, typeRequestor, false); 1009 return typeRequestor.getType(); 1010 } 1011 1012 /** 1013 * Returns the type specified by the qualified name, or <code>null</code> 1014 * if none exist. The domain of 1015 * the search is bounded by the Java project from which this name lookup was obtained. 1016 * 1017 * @param name the name of the type to find 1018 * @param partialMatch partial name matches qualify when <code>true</code>, 1019 * only exact name matches qualify when <code>false</code> 1020 * @param acceptFlags a bit mask describing if classes, interfaces or both classes and interfaces 1021 * are desired results. If no flags are specified, all types are returned. 1022 * 1023 * @see #ACCEPT_CLASSES 1024 * @see #ACCEPT_INTERFACES 1025 * @see #ACCEPT_ENUMS 1026 * @see #ACCEPT_ANNOTATIONS 1027 * @see #ACCEPT_RECORDS 1028 */ findType(String name, boolean partialMatch, int acceptFlags)1029 public IType findType(String name, boolean partialMatch, int acceptFlags) { 1030 NameLookup.Answer answer = findType(name, partialMatch, acceptFlags, false/*don't check restrictions*/); 1031 return answer == null ? null : answer.type; 1032 } 1033 findType(String name, boolean partialMatch, int acceptFlags, boolean checkRestrictions)1034 public Answer findType(String name, boolean partialMatch, int acceptFlags, boolean checkRestrictions) { 1035 return findType(name, partialMatch, acceptFlags, true/*consider secondary types*/, true/*wait for indexes*/, checkRestrictions, null); 1036 } findType(String name, boolean partialMatch, int acceptFlags, boolean considerSecondaryTypes, boolean waitForIndexes, boolean checkRestrictions, IProgressMonitor monitor)1037 public Answer findType(String name, boolean partialMatch, int acceptFlags, boolean considerSecondaryTypes, boolean waitForIndexes, boolean checkRestrictions, IProgressMonitor monitor) { 1038 int index= name.lastIndexOf('.'); 1039 if (index == 0) { 1040 return null; // bug 377710 - e.g. ".Foo" (no package, but not "default" package) 1041 } 1042 String className= null, packageName= null; 1043 if (index == -1) { 1044 packageName= IPackageFragment.DEFAULT_PACKAGE_NAME; 1045 className= name; 1046 } else { 1047 packageName= name.substring(0, index); 1048 className= name.substring(index + 1); 1049 } 1050 return findType(className, packageName, partialMatch, acceptFlags, considerSecondaryTypes, waitForIndexes, checkRestrictions, monitor); 1051 } findModule(char[] moduleName)1052 public Answer findModule(char[] moduleName) { 1053 JavaElementRequestor requestor = new JavaElementRequestor(); 1054 seekModule(moduleName, false, requestor); 1055 IModuleDescription[] modules = requestor.getModules(); 1056 if (modules.length == 0) { 1057 try { 1058 // FIXME(SHMOD): only considers source modules?? (MODULEPATH container is only experimental) 1059 JavaModelManager.getModulePathManager().seekModule(moduleName, false, requestor); 1060 modules = requestor.getModules(); 1061 } catch (JavaModelException e) { 1062 // TODO Auto-generated catch block 1063 } 1064 } 1065 if (modules.length > 0) { // TODO what to do?? 1066 return new Answer(modules[0]); 1067 } 1068 return null; 1069 } 1070 getMemberType(IType type, String name, int dot)1071 private IType getMemberType(IType type, String name, int dot) { 1072 while (dot != -1) { 1073 int start = dot+1; 1074 dot = name.indexOf('.', start); 1075 String typeName = name.substring(start, dot == -1 ? name.length() : dot); 1076 type = type.getType(typeName); 1077 } 1078 return type; 1079 } 1080 isPackage(String[] pkgName)1081 public boolean isPackage(String[] pkgName) { 1082 return this.packageFragments.get(pkgName) != null; 1083 } 1084 isPackage(String[] pkgName, IPackageFragmentRoot[] moduleContext)1085 public boolean isPackage(String[] pkgName, IPackageFragmentRoot[] moduleContext) { 1086 if (moduleContext == null) // includes the case where looking for module UNNAMED or ANY 1087 return isPackage(pkgName); 1088 1089 for (IPackageFragmentRoot moduleRoot : moduleContext) { 1090 if (moduleRoot.getPackageFragment(String.join(".", pkgName)).exists()) //$NON-NLS-1$ 1091 return true; 1092 } 1093 return false; 1094 } 1095 moduleMatches(IPackageFragmentRoot root, IPackageFragmentRoot[] moduleContext)1096 private boolean moduleMatches(IPackageFragmentRoot root, IPackageFragmentRoot[] moduleContext) { 1097 for (IPackageFragmentRoot moduleRoot : moduleContext) 1098 if (moduleRoot.equals(root)) 1099 return true; 1100 return false; 1101 } 1102 1103 /** 1104 * Returns true if the given element's name matches the 1105 * specified <code>searchName</code>, otherwise false. 1106 * 1107 * <p>The <code>partialMatch</code> argument indicates partial matches 1108 * should be considered. 1109 * NOTE: in partialMatch mode, the case will be ignored, and the searchName must already have 1110 * been lowercased. 1111 */ nameMatches(String searchName, IJavaElement element, boolean partialMatch)1112 protected boolean nameMatches(String searchName, IJavaElement element, boolean partialMatch) { 1113 if (partialMatch) { 1114 // partial matches are used in completion mode, thus case insensitive mode 1115 return element.getElementName().toLowerCase().startsWith(searchName); 1116 } else { 1117 return element.getElementName().equals(searchName); 1118 } 1119 } 1120 1121 /** 1122 * Returns true if the given cu's name matches the 1123 * specified <code>searchName</code>, otherwise false. 1124 * 1125 * <p>The <code>partialMatch</code> argument indicates partial matches 1126 * should be considered. 1127 * NOTE: in partialMatch mode, the case will be ignored, and the searchName must already have 1128 * been lowercased. 1129 */ nameMatches(String searchName, ICompilationUnit cu, boolean partialMatch)1130 protected boolean nameMatches(String searchName, ICompilationUnit cu, boolean partialMatch) { 1131 if (partialMatch) { 1132 // partial matches are used in completion mode, thus case insensitive mode 1133 return cu.getElementName().toLowerCase().startsWith(searchName); 1134 } else { 1135 return Util.equalsIgnoreJavaLikeExtension(cu.getElementName(), searchName); 1136 } 1137 } 1138 1139 /** 1140 * Notifies the given requestor of all package fragments with the 1141 * given name. Checks the requestor at regular intervals to see if the 1142 * requestor has canceled. The domain of 1143 * the search is bounded by the <code>IJavaProject</code> 1144 * this <code>NameLookup</code> was obtained from. 1145 * 1146 * @param partialMatch partial name matches qualify when <code>true</code>; 1147 * only exact name matches qualify when <code>false</code> 1148 */ seekPackageFragments(String name, boolean partialMatch, IJavaElementRequestor requestor, IPackageFragmentRoot[] moduleContext)1149 public void seekPackageFragments(String name, boolean partialMatch, IJavaElementRequestor requestor, IPackageFragmentRoot[] moduleContext) { 1150 if (moduleContext == null) { 1151 seekPackageFragments(name, partialMatch, requestor); 1152 return; 1153 } 1154 if (partialMatch) { 1155 seekModuleAwarePartialPackageFragments(name, requestor, moduleContext); 1156 return; 1157 } 1158 for (IPackageFragmentRoot moduleRoot : moduleContext) { 1159 IPackageFragment fragment = moduleRoot.getPackageFragment(name); 1160 if (fragment.exists()) 1161 requestor.acceptPackageFragment(fragment); 1162 } 1163 } 1164 1165 /** 1166 * Notifies the given requestor of all package fragments with the 1167 * given name. Checks the requestor at regular intervals to see if the 1168 * requestor has canceled. The domain of 1169 * the search is bounded by the <code>IJavaProject</code> 1170 * this <code>NameLookup</code> was obtained from. 1171 * 1172 * @param partialMatch partial name matches qualify when <code>true</code>; 1173 * only exact name matches qualify when <code>false</code> 1174 */ seekTypes(String pkgName, String name, boolean partialMatch, IJavaElementRequestor requestor, int acceptFlags, IPackageFragmentRoot[] moduleContext, String moduleName)1175 public void seekTypes(String pkgName, String name, boolean partialMatch, IJavaElementRequestor requestor, 1176 int acceptFlags, IPackageFragmentRoot[] moduleContext, String moduleName) { 1177 Selector selector = new Selector(moduleName); 1178 seekPackageFragments(pkgName, true /*partialMatch*/, selector, moduleContext); 1179 if (selector.pkgFragments.size() == 0) return; 1180 for (IPackageFragment pkg : selector.pkgFragments) { 1181 seekTypes(name, pkg, partialMatch, acceptFlags, requestor); 1182 } 1183 } 1184 seekModuleAwarePartialPackageFragments(String name, IJavaElementRequestor requestor, IPackageFragmentRoot[] moduleContext)1185 private void seekModuleAwarePartialPackageFragments(String name, IJavaElementRequestor requestor, IPackageFragmentRoot[] moduleContext) { 1186 boolean allPrefixMatch = CharOperation.equals(name.toCharArray(), CharOperation.ALL_PREFIX); 1187 String lName = name.toLowerCase(); 1188 Arrays.stream(this.packageFragments.keyTable) 1189 .filter(k -> k != null) 1190 .filter(k -> allPrefixMatch || Util.concatWith((String[])k, '.').toLowerCase().startsWith(lName)) 1191 .forEach(k -> { 1192 checkModulePackages(requestor, moduleContext, this.packageFragments.getIndex(k)); 1193 }); 1194 } 1195 checkModulePackages(IJavaElementRequestor requestor, IPackageFragmentRoot[] moduleContext, int pkgIndex)1196 private void checkModulePackages(IJavaElementRequestor requestor, IPackageFragmentRoot[] moduleContext, int pkgIndex) { 1197 Object value = this.packageFragments.valueTable[pkgIndex]; 1198 // reuse existing String[] 1199 String[] pkgName = (String[]) this.packageFragments.keyTable[pkgIndex]; 1200 if (value instanceof PackageFragmentRoot) { 1201 PackageFragmentRoot root = (PackageFragmentRoot) value; 1202 if (moduleMatches(root, moduleContext)) 1203 requestor.acceptPackageFragment(root.getPackageFragment(pkgName)); 1204 } else { 1205 IPackageFragmentRoot[] roots = (IPackageFragmentRoot[]) value; 1206 if (roots != null) { 1207 for (int i = 0, length = roots.length; i < length; i++) { 1208 if (requestor.isCanceled()) 1209 return; 1210 PackageFragmentRoot root = (PackageFragmentRoot) roots[i]; 1211 if (moduleMatches(root, moduleContext)) 1212 requestor.acceptPackageFragment(root.getPackageFragment(pkgName)); 1213 } 1214 } 1215 } 1216 } 1217 1218 @FunctionalInterface 1219 interface IPrefixMatcherCharArray { // note the reversal in the order of params wrt to the string version. matches(char[] prefix, char[] name, boolean isCaseSensitive)1220 boolean matches(char[] prefix, char[] name, boolean isCaseSensitive); 1221 } 1222 /** 1223 * Notifies the given requestor of all package fragments with the 1224 * given name. Checks the requestor at regular intervals to see if the 1225 * requestor has canceled. The domain of 1226 * the search is bounded by the <code>IJavaProject</code> 1227 * this <code>NameLookup</code> was obtained from. 1228 * 1229 * @param partialMatch partial name matches qualify when <code>true</code>; 1230 * only exact name matches qualify when <code>false</code> 1231 */ seekPackageFragments(String name, boolean partialMatch, IJavaElementRequestor requestor)1232 public void seekPackageFragments(String name, boolean partialMatch, IJavaElementRequestor requestor) { 1233 /* if (VERBOSE) { 1234 Util.verbose(" SEEKING PACKAGE FRAGMENTS"); //$NON-NLS-1$ 1235 Util.verbose(" -> name: " + name); //$NON-NLS-1$ 1236 Util.verbose(" -> partial match:" + partialMatch); //$NON-NLS-1$ 1237 } 1238 */ 1239 if (partialMatch) { 1240 String[] splittedName = Util.splitOn('.', name, 0, name.length()); 1241 Object[][] keys = this.packageFragments.keyTable; 1242 for (int i = 0, length = keys.length; i < length; i++) { 1243 if (requestor.isCanceled()) 1244 return; 1245 String[] pkgName = (String[]) keys[i]; 1246 if (pkgName != null && Util.startsWithIgnoreCase(pkgName, splittedName, partialMatch)) { 1247 Object value = this.packageFragments.valueTable[i]; 1248 if (value instanceof PackageFragmentRoot) { 1249 PackageFragmentRoot root = (PackageFragmentRoot) value; 1250 requestor.acceptPackageFragment(root.getPackageFragment(pkgName)); 1251 } else { 1252 IPackageFragmentRoot[] roots = (IPackageFragmentRoot[]) value; 1253 for (int j = 0, length2 = roots.length; j < length2; j++) { 1254 if (requestor.isCanceled()) 1255 return; 1256 PackageFragmentRoot root = (PackageFragmentRoot) roots[j]; 1257 requestor.acceptPackageFragment(root.getPackageFragment(pkgName)); 1258 } 1259 } 1260 } 1261 } 1262 } else { 1263 String[] splittedName = Util.splitOn('.', name, 0, name.length()); 1264 int pkgIndex = this.packageFragments.getIndex(splittedName); 1265 if (pkgIndex != -1) { 1266 Object value = this.packageFragments.valueTable[pkgIndex]; 1267 // reuse existing String[] 1268 String[] pkgName = (String[]) this.packageFragments.keyTable[pkgIndex]; 1269 if (value instanceof PackageFragmentRoot) { 1270 requestor.acceptPackageFragment(((PackageFragmentRoot) value).getPackageFragment(pkgName)); 1271 } else { 1272 IPackageFragmentRoot[] roots = (IPackageFragmentRoot[]) value; 1273 if (roots != null) { 1274 for (int i = 0, length = roots.length; i < length; i++) { 1275 if (requestor.isCanceled()) 1276 return; 1277 PackageFragmentRoot root = (PackageFragmentRoot) roots[i]; 1278 requestor.acceptPackageFragment(root.getPackageFragment(pkgName)); 1279 } 1280 } 1281 } 1282 } 1283 } 1284 } 1285 seekTypes(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor)1286 public void seekTypes(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor) { 1287 seekTypes(name, pkg, partialMatch, acceptFlags, requestor, true); 1288 } 1289 seekModuleReferences(String name, IJavaElementRequestor requestor, IJavaProject javaProject)1290 public void seekModuleReferences(String name, IJavaElementRequestor requestor, IJavaProject javaProject) { 1291 seekModule(name.toCharArray(), true /* prefix */, requestor); 1292 } seekModule(char[] name, boolean prefixMatch, IJavaElementRequestor requestor)1293 public void seekModule(char[] name, boolean prefixMatch, IJavaElementRequestor requestor) { 1294 1295 IPrefixMatcherCharArray prefixMatcher = prefixMatch 1296 ? CharOperation.equals(name, CharOperation.ALL_PREFIX) 1297 ? (x, y, isCaseSensitive) -> true 1298 : CharOperation::prefixEquals 1299 : CharOperation::equals; 1300 1301 int count= this.packageFragmentRoots.length; 1302 for (int i= 0; i < count; i++) { 1303 if (requestor.isCanceled()) 1304 return; 1305 IPackageFragmentRoot root= this.packageFragmentRoots[i]; 1306 IModuleDescription module = null; 1307 if (root instanceof JrtPackageFragmentRoot) { 1308 if (!prefixMatcher.matches(name, root.getElementName().toCharArray(), false)) { 1309 continue; 1310 } 1311 } 1312 module = getModuleDescription(this.rootProject, root, this.rootToModule, this.rootToResolvedEntries::get); 1313 if (module != null && prefixMatcher.matches(name, module.getElementName().toCharArray(), false)) { 1314 requestor.acceptModule(module); 1315 } 1316 } 1317 } 1318 /** 1319 * Notifies the given requestor of all types (classes and interfaces) in the 1320 * given package fragment with the given (unqualified) name. 1321 * Checks the requestor at regular intervals to see if the requestor 1322 * has canceled. If the given package fragment is <code>null</code>, all types in the 1323 * project whose simple name matches the given name are found. 1324 * 1325 * @param name The name to search 1326 * @param pkg The corresponding package fragment 1327 * @param partialMatch partial name matches qualify when <code>true</code>; 1328 * only exact name matches qualify when <code>false</code> 1329 * @param acceptFlags a bit mask describing if classes, interfaces or both classes and interfaces 1330 * are desired results. If no flags are specified, all types are returned. 1331 * @param requestor The requestor that collects the result 1332 * 1333 * @see #ACCEPT_CLASSES 1334 * @see #ACCEPT_INTERFACES 1335 * @see #ACCEPT_ENUMS 1336 * @see #ACCEPT_ANNOTATIONS 1337 * @see #ACCEPT_RECORDS 1338 */ seekTypes(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor, boolean considerSecondaryTypes)1339 public void seekTypes(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor, boolean considerSecondaryTypes) { 1340 /* if (VERBOSE) { 1341 Util.verbose(" SEEKING TYPES"); //$NON-NLS-1$ 1342 Util.verbose(" -> name: " + name); //$NON-NLS-1$ 1343 Util.verbose(" -> pkg: " + ((JavaElement) pkg).toStringWithAncestors()); //$NON-NLS-1$ 1344 Util.verbose(" -> partial match:" + partialMatch); //$NON-NLS-1$ 1345 } 1346 */ 1347 String matchName= partialMatch ? name.toLowerCase() : name; 1348 if (pkg == null) { 1349 findAllTypes(matchName, partialMatch, acceptFlags, requestor); 1350 return; 1351 } 1352 PackageFragmentRoot root= (PackageFragmentRoot) pkg.getParent(); 1353 try { 1354 1355 // look in working copies first 1356 int firstDot = -1; 1357 String topLevelTypeName = null; 1358 int packageFlavor= root.internalKind(); 1359 if (this.typesInWorkingCopies != null || packageFlavor == IPackageFragmentRoot.K_SOURCE) { 1360 firstDot = matchName.indexOf('.'); 1361 if (!partialMatch) 1362 topLevelTypeName = firstDot == -1 ? matchName : matchName.substring(0, firstDot); 1363 } 1364 if (this.typesInWorkingCopies != null) { 1365 if (seekTypesInWorkingCopies(matchName, pkg, firstDot, partialMatch, topLevelTypeName, acceptFlags, requestor, considerSecondaryTypes)) 1366 return; 1367 } 1368 1369 // look in model 1370 switch (packageFlavor) { 1371 case IPackageFragmentRoot.K_BINARY : 1372 matchName= matchName.replace('.', '$'); 1373 seekTypesInBinaryPackage(matchName, pkg, partialMatch, acceptFlags, requestor); 1374 break; 1375 case IPackageFragmentRoot.K_SOURCE : 1376 seekTypesInSourcePackage(matchName, pkg, firstDot, partialMatch, topLevelTypeName, acceptFlags, requestor); 1377 if (matchName.indexOf('$') != -1) { 1378 matchName= matchName.replace('$', '.'); 1379 firstDot = matchName.indexOf('.'); 1380 if (!partialMatch) 1381 topLevelTypeName = firstDot == -1 ? matchName : matchName.substring(0, firstDot); 1382 seekTypesInSourcePackage(matchName, pkg, firstDot, partialMatch, topLevelTypeName, acceptFlags, requestor); 1383 } 1384 break; 1385 default : 1386 return; 1387 } 1388 } catch (JavaModelException e) { 1389 return; 1390 } 1391 } 1392 1393 /** 1394 * Performs type search in a binary package. 1395 */ seekTypesInBinaryPackage(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor)1396 protected void seekTypesInBinaryPackage(String name, IPackageFragment pkg, boolean partialMatch, int acceptFlags, IJavaElementRequestor requestor) { 1397 long start = -1; 1398 if (VERBOSE) 1399 start = System.currentTimeMillis(); 1400 try { 1401 if (!partialMatch) { 1402 // exact match 1403 if (requestor.isCanceled()) return; 1404 ClassFile classFile = new ClassFile((PackageFragment) pkg, name); 1405 if (classFile.existsUsingJarTypeCache()) { 1406 IType type = classFile.getType(); 1407 if (acceptType(type, acceptFlags, false/*not a source type*/)) { 1408 requestor.acceptType(type); 1409 } 1410 } 1411 } else { 1412 IJavaElement[] classFiles= null; 1413 try { 1414 classFiles= pkg.getChildren(); 1415 } catch (JavaModelException npe) { 1416 return; // the package is not present 1417 } 1418 int length= classFiles.length; 1419 String unqualifiedName = name; 1420 int index = name.lastIndexOf('$'); 1421 if (index != -1) { 1422 //the type name of the inner type 1423 unqualifiedName = Util.localTypeName(name, index, name.length()); 1424 // unqualifiedName is empty if the name ends with a '$' sign. 1425 // See http://dev.eclipse.org/bugs/show_bug.cgi?id=14642 1426 } 1427 int matchLength = name.length(); 1428 for (int i = 0; i < length; i++) { 1429 if (requestor.isCanceled()) 1430 return; 1431 IJavaElement classFile= classFiles[i]; 1432 // MatchName will never have the extension ".class" and the elementName always will. 1433 String elementName = classFile.getElementName(); 1434 if (elementName.regionMatches(true /*ignore case*/, 0, name, 0, matchLength)) { 1435 if (classFile instanceof IOrdinaryClassFile) { 1436 IType type = ((IOrdinaryClassFile) classFile).getType(); 1437 String typeName = type.getElementName(); 1438 if (typeName.length() > 0 && !Character.isDigit(typeName.charAt(0))) { //not an anonymous type 1439 if (nameMatches(unqualifiedName, type, true/*partial match*/) && acceptType(type, acceptFlags, false/*not a source type*/)) 1440 requestor.acceptType(type); 1441 } 1442 } 1443 } 1444 } 1445 } 1446 } finally { 1447 if (VERBOSE) 1448 this.timeSpentInSeekTypesInBinaryPackage += System.currentTimeMillis()-start; 1449 } 1450 } 1451 1452 /** 1453 * Performs type search in a source package. 1454 */ seekTypesInSourcePackage( String name, IPackageFragment pkg, int firstDot, boolean partialMatch, String topLevelTypeName, int acceptFlags, IJavaElementRequestor requestor)1455 protected void seekTypesInSourcePackage( 1456 String name, 1457 IPackageFragment pkg, 1458 int firstDot, 1459 boolean partialMatch, 1460 String topLevelTypeName, 1461 int acceptFlags, 1462 IJavaElementRequestor requestor) { 1463 1464 long start = -1; 1465 if (VERBOSE) 1466 start = System.currentTimeMillis(); 1467 try { 1468 if (!partialMatch) { 1469 try { 1470 IJavaElement[] compilationUnits = pkg.getChildren(); 1471 for (int i = 0, length = compilationUnits.length; i < length; i++) { 1472 if (requestor.isCanceled()) 1473 return; 1474 IJavaElement cu = compilationUnits[i]; 1475 String cuName = cu.getElementName(); 1476 int lastDot = cuName.lastIndexOf('.'); 1477 if (lastDot != topLevelTypeName.length() || !topLevelTypeName.regionMatches(0, cuName, 0, lastDot)) 1478 continue; 1479 1480 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=351697 1481 // If we are looking at source location, just ignore binary types 1482 if (!(cu instanceof ICompilationUnit)) 1483 continue; 1484 IType type = ((ICompilationUnit) cu).getType(topLevelTypeName); 1485 type = getMemberType(type, name, firstDot); 1486 if (acceptType(type, acceptFlags, true/*a source type*/)) { // accept type checks for existence 1487 requestor.acceptType(type); 1488 break; // since an exact match was requested, no other matching type can exist 1489 } 1490 } 1491 } catch (JavaModelException e) { 1492 // package doesn't exist -> ignore 1493 } 1494 } else { 1495 try { 1496 String cuPrefix = firstDot == -1 ? name : name.substring(0, firstDot); 1497 IJavaElement[] compilationUnits = pkg.getChildren(); 1498 for (int i = 0, length = compilationUnits.length; i < length; i++) { 1499 if (requestor.isCanceled()) 1500 return; 1501 IJavaElement cu = compilationUnits[i]; 1502 if (!cu.getElementName().toLowerCase().startsWith(cuPrefix)) 1503 continue; 1504 try { 1505 IType[] types = ((ICompilationUnit) cu).getTypes(); 1506 for (int j = 0, typeLength = types.length; j < typeLength; j++) 1507 seekTypesInTopLevelType(name, firstDot, types[j], requestor, acceptFlags); 1508 } catch (JavaModelException e) { 1509 // cu doesn't exist -> ignore 1510 } 1511 } 1512 } catch (JavaModelException e) { 1513 // package doesn't exist -> ignore 1514 } 1515 } 1516 } finally { 1517 if (VERBOSE) 1518 this.timeSpentInSeekTypesInSourcePackage += System.currentTimeMillis()-start; 1519 } 1520 } 1521 isPrimaryType(String name, IType type, boolean partialMatch)1522 private boolean isPrimaryType(String name, IType type, boolean partialMatch) { 1523 /* 1524 * Please have a look at: NameLookup#NameLookup 1525 * The HashTable this.typesInWorkingCopies contains values which are HashTables themselves. 1526 * The values of these HashTables are either of IType or IType[]. 1527 * These values are types belonging to a compilation unit. Please check: 1528 * CompilationUnit#getTypes(). 1529 * Therefore the parents of these types would be compilation units. 1530 */ 1531 ICompilationUnit cu = (ICompilationUnit) type.getParent(); 1532 String cuName = cu.getElementName().substring(0, cu.getElementName().lastIndexOf('.')); 1533 /* 1534 * Secondary types along with primary types have their parent as the compilation unit. 1535 * The names of the primary type would match with their compilation unit. 1536 */ 1537 if (!cuName.equals(type.getElementName())) 1538 return false; 1539 if (partialMatch) { 1540 return cuName.regionMatches(0, name, 0, name.length()); 1541 } else { 1542 return cuName.equals(name); 1543 } 1544 } 1545 1546 /** 1547 * Notifies the given requestor of all types (classes and interfaces) in the 1548 * given type with the given (possibly qualified) name. Checks 1549 * the requestor at regular intervals to see if the requestor 1550 * has canceled. 1551 */ seekTypesInType(String prefix, int firstDot, IType type, IJavaElementRequestor requestor, int acceptFlags)1552 protected boolean seekTypesInType(String prefix, int firstDot, IType type, IJavaElementRequestor requestor, int acceptFlags) { 1553 IType[] types= null; 1554 try { 1555 types= type.getTypes(); 1556 } catch (JavaModelException npe) { 1557 return false; // the enclosing type is not present 1558 } 1559 int length= types.length; 1560 if (length == 0) return false; 1561 1562 String memberPrefix = prefix; 1563 boolean isMemberTypePrefix = false; 1564 if (firstDot != -1) { 1565 memberPrefix= prefix.substring(0, firstDot); 1566 isMemberTypePrefix = true; 1567 } 1568 for (int i= 0; i < length; i++) { 1569 if (requestor.isCanceled()) 1570 return false; 1571 IType memberType= types[i]; 1572 if (memberType.getElementName().toLowerCase().startsWith(memberPrefix)) 1573 if (isMemberTypePrefix) { 1574 String subPrefix = prefix.substring(firstDot + 1, prefix.length()); 1575 return seekTypesInType(subPrefix, subPrefix.indexOf('.'), memberType, requestor, acceptFlags); 1576 } else { 1577 if (acceptType(memberType, acceptFlags, true/*a source type*/)) { 1578 requestor.acceptMemberType(memberType); 1579 return true; 1580 } 1581 } 1582 } 1583 return false; 1584 } 1585 seekTypesInTopLevelType(String prefix, int firstDot, IType topLevelType, IJavaElementRequestor requestor, int acceptFlags)1586 protected boolean seekTypesInTopLevelType(String prefix, int firstDot, IType topLevelType, IJavaElementRequestor requestor, int acceptFlags) { 1587 if (!topLevelType.getElementName().toLowerCase().startsWith(prefix)) 1588 return false; 1589 if (firstDot == -1) { 1590 if (acceptType(topLevelType, acceptFlags, true/*a source type*/)) { 1591 requestor.acceptType(topLevelType); 1592 return true; 1593 } 1594 } else { 1595 return seekTypesInType(prefix, firstDot, topLevelType, requestor, acceptFlags); 1596 } 1597 return false; 1598 } 1599 1600 /* 1601 * Seeks the type with the given name in the map of types with precedence (coming from working copies) 1602 * Return whether a type has been found. 1603 */ seekTypesInWorkingCopies( String name, IPackageFragment pkg, int firstDot, boolean partialMatch, String topLevelTypeName, int acceptFlags, IJavaElementRequestor requestor, boolean considerSecondaryTypes)1604 protected boolean seekTypesInWorkingCopies( 1605 String name, 1606 IPackageFragment pkg, 1607 int firstDot, 1608 boolean partialMatch, 1609 String topLevelTypeName, 1610 int acceptFlags, 1611 IJavaElementRequestor requestor, 1612 boolean considerSecondaryTypes) { 1613 1614 if (!partialMatch) { 1615 HashMap typeMap = (HashMap) (this.typesInWorkingCopies == null ? null : this.typesInWorkingCopies.get(pkg)); 1616 if (typeMap != null) { 1617 Object object = typeMap.get(topLevelTypeName); 1618 if (object instanceof IType) { 1619 IType type = getMemberType((IType) object, name, firstDot); 1620 if (!considerSecondaryTypes && !isPrimaryType(name, (IType) object, false)) 1621 return false; 1622 if (acceptType(type, acceptFlags, true/*a source type*/)) { 1623 requestor.acceptType(type); 1624 return true; // don't continue with compilation unit 1625 } 1626 } else if (object instanceof IType[]) { 1627 if (object == NO_TYPES) { 1628 // all types where deleted -> type is hidden, OR it is the fake type package-info 1629 String packageInfoName = String.valueOf(TypeConstants.PACKAGE_INFO_NAME); 1630 if (packageInfoName.equals(name)) 1631 requestor.acceptType(pkg.getCompilationUnit(packageInfoName.concat(SUFFIX_STRING_java)).getType(name)); 1632 return true; 1633 } 1634 IType[] topLevelTypes = (IType[]) object; 1635 for (int i = 0, length = topLevelTypes.length; i < length; i++) { 1636 if (requestor.isCanceled()) 1637 return false; 1638 IType type = getMemberType(topLevelTypes[i], name, firstDot); 1639 if (acceptType(type, acceptFlags, true/*a source type*/)) { 1640 requestor.acceptType(type); 1641 return true; // return the first one 1642 } 1643 } 1644 } 1645 } 1646 } else { 1647 HashMap typeMap = (HashMap) (this.typesInWorkingCopies == null ? null : this.typesInWorkingCopies.get(pkg)); 1648 if (typeMap != null) { 1649 Iterator iterator = typeMap.values().iterator(); 1650 while (iterator.hasNext()) { 1651 if (requestor.isCanceled()) 1652 return false; 1653 Object object = iterator.next(); 1654 if (object instanceof IType) { 1655 if (!considerSecondaryTypes && !isPrimaryType(name, (IType) object, true)) 1656 continue; 1657 seekTypesInTopLevelType(name, firstDot, (IType) object, requestor, acceptFlags); 1658 } else if (object instanceof IType[]) { 1659 IType[] topLevelTypes = (IType[]) object; 1660 for (int i = 0, length = topLevelTypes.length; i < length; i++) 1661 seekTypesInTopLevelType(name, firstDot, topLevelTypes[i], requestor, acceptFlags); 1662 } 1663 } 1664 } 1665 } 1666 return false; 1667 } 1668 hasCompilationUnit(char[][] pkgName, IPackageFragmentRoot[] moduleContext)1669 public boolean hasCompilationUnit(char[][] pkgName, IPackageFragmentRoot[] moduleContext) { 1670 String packageName = CharOperation.toString(pkgName); 1671 if (packageName == null || packageName.length() == 0) { 1672 packageName= IPackageFragment.DEFAULT_PACKAGE_NAME; 1673 } 1674 1675 // Look for concerned package fragments 1676 JavaElementRequestor elementRequestor = new JavaElementRequestor(); 1677 seekPackageFragments(packageName, false, elementRequestor, moduleContext); 1678 IPackageFragment[] packages= elementRequestor.getPackageFragments(); 1679 for (IPackageFragment fragment : packages) { 1680 try { 1681 if (fragment.containsJavaResources()) 1682 return true; 1683 } catch (JavaModelException e) { 1684 // silent 1685 } 1686 } 1687 return false; 1688 } 1689 } 1690