|
| 1 | +package com.ibm.cldk.javaee; |
| 2 | + |
| 3 | +import com.github.javaparser.ast.body.CallableDeclaration; |
| 4 | +import com.github.javaparser.ast.body.TypeDeclaration; |
| 5 | +import com.ibm.cldk.javaee.utils.interfaces.AbstractEntrypointFinder; |
| 6 | +import com.ibm.cldk.schema.JCallable; |
| 7 | +import com.ibm.cldk.schema.JEntrypointReport; |
| 8 | +import com.ibm.cldk.schema.JModule; |
| 9 | +import com.ibm.cldk.schema.JType; |
| 10 | +import java.util.ArrayList; |
| 11 | +import java.util.List; |
| 12 | +import java.util.Map; |
| 13 | +import java.util.Set; |
| 14 | +import java.util.TreeSet; |
| 15 | + |
| 16 | +/** |
| 17 | + * Runs the entrypoint finders and keeps <em>which</em> of them matched, rather than collapsing them |
| 18 | + * with {@code anyMatch}. The framework names it returns land on the node |
| 19 | + * ({@code entrypoint_frameworks}); the failures it catches land in the run's |
| 20 | + * {@link JEntrypointReport}. |
| 21 | + * |
| 22 | + * <p><b>Why attribution lives on the node.</b> {@code L1Extractor} reuses a cached module |
| 23 | + * byte-for-byte and skips the build, so the finders never run for that file. Anything recorded only |
| 24 | + * during the walk would therefore under-report on a warm cache — which is exactly the ambiguous |
| 25 | + * empty the report exists to prevent. The tree carries the attribution whether the module was |
| 26 | + * rebuilt or reused; the application-level {@code frameworks_detected} is a union over it, computed |
| 27 | + * at assembly time. |
| 28 | + * |
| 29 | + * <p>The corollary, stated rather than left for a reader to assume: {@code unresolved} and |
| 30 | + * {@code errors} describe <em>this run's</em> pass, so they are genuinely empty for a file served |
| 31 | + * from cache — no finder ran for it, so nothing could fail. |
| 32 | + * |
| 33 | + * <p>A finder that throws does not abort the scan. One badly-shaped declaration must not cost the |
| 34 | + * other four finders their answer, and a silent {@code false} would be indistinguishable from "this |
| 35 | + * really is not an entrypoint" — so the failure is counted and named instead. |
| 36 | + */ |
| 37 | +public final class EntrypointScan { |
| 38 | + |
| 39 | + private EntrypointScan() {} |
| 40 | + |
| 41 | + /** Every ruleset the pass could match, matched or not — the {@code rulesets} vocabulary. */ |
| 42 | + public static List<String> rulesets() { |
| 43 | + List<String> out = new ArrayList<>(); |
| 44 | + EntrypointsFinderFactory.getEntrypointFinders().forEach(f -> out.add(f.framework())); |
| 45 | + return out; |
| 46 | + } |
| 47 | + |
| 48 | + /** |
| 49 | + * Fill in the parts of the report that are only knowable once the whole tree exists: |
| 50 | + * {@code rulesets} (always all five, matched or not) and {@code frameworks_detected} (the union |
| 51 | + * of the tree's node attributions). |
| 52 | + * |
| 53 | + * <p>The union is taken over the BUILT TREE, not over what the finders did during the walk, |
| 54 | + * which is what makes it correct on a warm cache — a reused module runs no finders but still |
| 55 | + * carries its attributions. |
| 56 | + */ |
| 57 | + public static void completeReport(JEntrypointReport report, Map<String, JModule> modules) { |
| 58 | + report.setRulesets(rulesets()); |
| 59 | + Set<String> detected = new TreeSet<>(); |
| 60 | + if (modules != null) { |
| 61 | + for (JModule module : modules.values()) { |
| 62 | + collect(module.getTypes(), detected); |
| 63 | + } |
| 64 | + } |
| 65 | + report.setFrameworksDetected(new ArrayList<>(detected)); |
| 66 | + } |
| 67 | + |
| 68 | + private static void collect(Map<String, JType> types, Set<String> detected) { |
| 69 | + if (types == null) { |
| 70 | + return; |
| 71 | + } |
| 72 | + for (JType type : types.values()) { |
| 73 | + detected.addAll(type.getEntrypointFrameworks()); |
| 74 | + for (JCallable callable : type.getCallables().values()) { |
| 75 | + detected.addAll(callable.getEntrypointFrameworks()); |
| 76 | + collect(callable.getTypes(), detected); |
| 77 | + } |
| 78 | + collect(type.getTypes(), detected); |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + /** The frameworks recognising {@code td} as an entrypoint class, in finder order. */ |
| 83 | + public static List<String> classFrameworks(TypeDeclaration<?> td, JEntrypointReport report) { |
| 84 | + List<String> out = new ArrayList<>(); |
| 85 | + EntrypointsFinderFactory.getEntrypointFinders().forEach(finder -> { |
| 86 | + try { |
| 87 | + if (finder.isEntrypointClass(td)) { |
| 88 | + out.add(finder.framework()); |
| 89 | + } |
| 90 | + } catch (RuntimeException e) { |
| 91 | + record(report, finder, e); |
| 92 | + } |
| 93 | + }); |
| 94 | + return out; |
| 95 | + } |
| 96 | + |
| 97 | + /** The frameworks recognising {@code cd} as an entrypoint method, in finder order. */ |
| 98 | + public static List<String> methodFrameworks(CallableDeclaration<?> cd, JEntrypointReport report) { |
| 99 | + List<String> out = new ArrayList<>(); |
| 100 | + EntrypointsFinderFactory.getEntrypointFinders().forEach(finder -> { |
| 101 | + try { |
| 102 | + if (finder.isEntrypointMethod(cd)) { |
| 103 | + out.add(finder.framework()); |
| 104 | + } |
| 105 | + } catch (RuntimeException e) { |
| 106 | + record(report, finder, e); |
| 107 | + } |
| 108 | + }); |
| 109 | + return out; |
| 110 | + } |
| 111 | + |
| 112 | + private static void record(JEntrypointReport report, AbstractEntrypointFinder finder, |
| 113 | + RuntimeException e) { |
| 114 | + if (report == null) { |
| 115 | + return; |
| 116 | + } |
| 117 | + report.getUnresolved().merge(finder.framework(), 1, Integer::sum); |
| 118 | + // Deduplicated: one finder failing the same way on 400 declarations is one fact, and 400 |
| 119 | + // copies of it would bury the other four finders' failures. |
| 120 | + String message = finder.framework() + ": " + e.getClass().getSimpleName() |
| 121 | + + (e.getMessage() == null ? "" : " — " + e.getMessage()); |
| 122 | + if (!report.getErrors().contains(message)) { |
| 123 | + report.getErrors().add(message); |
| 124 | + } |
| 125 | + } |
| 126 | +} |
0 commit comments