private static Pattern loadExtensionPointSuffixes() {
		Set<String> extensionPointSuffixes = new HashSet<>();

		Collection<ResourceFile> moduleRootDirectories = Application.getModuleRootDirectories();
		if (moduleRootDirectories.isEmpty()) {
			throw new AssertException("Could not find modules for Class Searcher!");
		}

		log.trace("Scanning module root directories: {}", moduleRootDirectories);

		for (ResourceFile moduleRoot : moduleRootDirectories) {
			ResourceFile file = new ResourceFile(moduleRoot, "data/ExtensionPoint.manifest");
			if (file.exists()) {
				for (String line : FileUtilities.getLinesQuietly(file)) {
					line = line.trim();
					try {
						Pattern.compile(line);
						extensionPointSuffixes.add(line);
					}
					catch (PatternSyntaxException e) {
						throw new AssertException(
							"Error parsing extension point suffix '%s' found in '%s'"
									.formatted(line, file));
					}
				}
			}
		}

		// Build regex of the form .*(suffix1|suffix2|suffix3|...)$
		// If one suffix ends with another suffix, precedence should be given to the shorter one.
		// This will result in some false positives, but will prevent some corner error cases as
		// described in this example:
		// There are 2 valid suffixes, Plugin and BobPlugin. Someone makes a new class:
		//
		//     class BillBobPlugin extends Plugin
		//
		// The person who made this class was unaware that BobPlugin was also a valid suffix. If
		// we were to match on the longest suffix, BillBobPlugin would erroneously be associated
		// with BobPlugin, and getClasses() would fail. Now consider this example:
		//
		//     class BillBobPlugin extends BobPlugin
		//
		// Since BillBobPlugin will be associated with the shorter "Plugin" suffix, it will be
		// grouped with the other Plugin extension points.  However, when getClasses(BobPlugin.class)
		// is called, we retrieve the same shortest suffix from the given "BobPlugin" class name, 
		// which is Plugin. This will result in BillBobPlugin getting properly discovered from the
		// Plugin group. Final checks are performed to make sure the provided class is assignable
		// from any class in the group, which filters out the bad associations.
		StringBuilder buffy = new StringBuilder(".*(");
		String between = "";
		for (String suffix : extensionPointSuffixes) {
			suffix = suffix.trim();
			if (suffix.isEmpty()) {
				continue;
			}

			buffy.append(between);
			buffy.append(suffix);
			between = "|";
		}
		buffy.append(")$");
		log.trace("Using extension point pattern: {}", buffy);
		return Pattern.compile(buffy.toString());
	}
Sorts the given {@link List} of {@link Class}es first by their defined priority, then by
their name.

@param list The {@link List} of {@link Class}es to sort. This {@link List} will be modified.
Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java