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