package cz.habarta.typescript.generator; import cz.habarta.typescript.generator.parser.*; import cz.habarta.typescript.generator.util.Predicate; import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner; import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult; import java.lang.reflect.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Input { private final List<SourceType<Type>> sourceTypes; private Input(List<SourceType<Type>> sourceTypes) { this.sourceTypes = sourceTypes; } public List<SourceType<Type>> getSourceTypes() { return sourceTypes; } public static Input from(Type... types) { final List<SourceType<Type>> sourceTypes = new ArrayList<>(); for (Type type : types) { sourceTypes.add(new SourceType<>(type)); } return new Input(sourceTypes); } public static Input fromClassNamesAndJaxrsApplication(List<String> classNames, List<String> classNamePatterns, String jaxrsApplicationClassName, boolean automaticJaxrsApplication, Predicate<String> isClassNameExcluded, ClassLoader classLoader) { final ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(classLoader); final ClasspathScanner classpathScanner = new ClasspathScanner(); final List<SourceType<Type>> types = new ArrayList<>(); if (classNames != null) { types.addAll(fromClassNames(classNames).getSourceTypes()); } if (classNamePatterns != null) { types.addAll(fromClassNamePatterns(classpathScanner.scanClasspath(), classNamePatterns).getSourceTypes()); } if (jaxrsApplicationClassName != null) { types.addAll(fromClassNames(Arrays.asList(jaxrsApplicationClassName)).sourceTypes); } if (automaticJaxrsApplication) { types.addAll(JaxrsApplicationScanner.scanAutomaticJaxrsApplication(classpathScanner.scanClasspath(), isClassNameExcluded)); } if (types.isEmpty()) { final String errorMessage = "No input classes found."; System.out.println(errorMessage); throw new RuntimeException(errorMessage); } return new Input(types); } finally { Thread.currentThread().setContextClassLoader(originalContextClassLoader); } } private static class ClasspathScanner { private ScanResult scanResult = null; public ScanResult scanClasspath() { if (scanResult == null) { System.out.println("Scanning classpath"); final Date scanStart = new Date(); final ScanResult result = new FastClasspathScanner().scan(); final int count = result.getNamesOfAllClasses().size(); final Date scanEnd = new Date(); final double timeInSeconds = (scanEnd.getTime() - scanStart.getTime()) / 1000.0; System.out.println(String.format("Scanning finished in %.2f seconds. Total number of classes: %d.", timeInSeconds, count)); scanResult = result; } return scanResult; } } private static Input fromClassNamePatterns(ScanResult scanResult, List<String> classNamePatterns) { final List<String> allClassNames = new ArrayList<>(); allClassNames.addAll(scanResult.getNamesOfAllStandardClasses()); allClassNames.addAll(scanResult.getNamesOfAllInterfaceClasses()); Collections.sort(allClassNames); final List<String> classNames = filterClassNames(allClassNames, classNamePatterns); System.out.println(String.format("Found %d classes matching pattern.", classNames.size())); return fromClassNames(classNames); } private static Input fromClassNames(List<String> classNames) { try { final List<SourceType<Type>> types = new ArrayList<>(); for (String className : classNames) { final Class<?> cls = Thread.currentThread().getContextClassLoader().loadClass(className); // skip synthetic classes (as those generated by java compiler for switch with enum) // and anonymous classes (should not be processed and they do not have SimpleName) if (!cls.isSynthetic() && !cls.isAnonymousClass()) { types.add(new SourceType<Type>(cls, null, null)); } } return new Input(types); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } static List<String> filterClassNames(List<String> classNames, List<String> globs) { final List<Pattern> regexps = globsToRegexps(globs); final List<String> result = new ArrayList<>(); for (String className : classNames) { if (classNameMatches(className, regexps)) { result.add(className); } } return result; } static boolean classNameMatches(String className, List<Pattern> regexps) { for (Pattern regexp : regexps) { if (regexp.matcher(className).matches()) { return true; } } return false; } static List<Pattern> globsToRegexps(List<String> globs) { final List<Pattern> regexps = new ArrayList<>(); for (String glob : globs) { regexps.add(globToRegexp(glob)); } return regexps; } /** * Creates regexp for glob pattern. * Replaces "*" with "[^.\$]*" and "**" with ".*". */ static Pattern globToRegexp(String glob) { final Pattern globToRegexpPattern = Pattern.compile("(\\*\\*)|(\\*)"); final Matcher matcher = globToRegexpPattern.matcher(glob); final StringBuffer sb = new StringBuffer(); int lastEnd = 0; while (matcher.find()) { sb.append(Pattern.quote(glob.substring(lastEnd, matcher.start()))); if (matcher.group(1) != null) { sb.append(Matcher.quoteReplacement(".*")); } if (matcher.group(2) != null) { sb.append(Matcher.quoteReplacement("[^.$]*")); } lastEnd = matcher.end(); } sb.append(Pattern.quote(glob.substring(lastEnd, glob.length()))); return Pattern.compile(sb.toString()); } }