private static Map<String, Set<ClassFileInfo>> findClasses(TaskMonitor monitor)
			throws CancelledException {
		log.info("Searching for classes...");

		List<ClassDir> classDirs = new ArrayList<>();
		List<ClassJar> classJars = new ArrayList<>();

		for (String searchPath : gatherSearchPaths()) {
			String lcSearchPath = searchPath.toLowerCase();
			File searchFile = new File(searchPath);
			if ((lcSearchPath.endsWith(".jar") || lcSearchPath.endsWith(".zip")) &&
				searchFile.exists()) {

				if (ClassJar.ignoreJar(searchPath)) {
					log.trace("Ignoring jar file: {}", searchPath);
					continue;
				}

				log.trace("Searching jar file: {}", searchPath);
				classJars.add(new ClassJar(searchPath, monitor));
			}
			else if (searchFile.isDirectory()) {
				log.trace("Searching classpath directory: {}", searchPath);
				classDirs.add(new ClassDir(searchPath, monitor));
			}
		}

		List<ClassFileInfo> classList = new ArrayList<>();
		for (ClassDir dir : classDirs) {
			monitor.checkCancelled();
			dir.getClasses(classList, monitor);
		}
		for (ClassJar jar : classJars) {
			monitor.checkCancelled();
			jar.getClasses(classList, monitor);
		}

		// We can't load more than one class with the same name, so de-duplicate them
		Map<String, ClassFileInfo> uniqueClassMap = new HashMap<>();
		for (ClassFileInfo info : classList) {
			ClassFileInfo existing = uniqueClassMap.get(info.name());
			if (existing == null) {
				uniqueClassMap.put(info.name(), info);
			}
			else {
				log.info("Ignoring class '%s' from '%s'. Already found at '%s'."
						.formatted(info.name(), info.path(), existing.path()));
			}
		}

		return uniqueClassMap.values()
				.stream()
				.collect(Collectors.groupingBy(ClassFileInfo::suffix, Collectors.toSet()));
	}
Scans the disk to find potential extension point class files. Matching is performed by file
name only. The class files are not opened or loaded by this method.

@param monitor the progress monitor for the disk scan
@return A {@link Map} of discovered {@link ClassFileInfo class information}, keyed by their
  extension point suffix
@throws CancelledException if the user cancelled the operation
Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java