C
This commit is contained in:
@@ -476,46 +476,8 @@ public class CodebaseContext {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try direct match
|
||||
List<String> directImpls = interfaceToImpls.get(cleanName);
|
||||
String directImplsSource = directImpls != null ? "direct" : null;
|
||||
|
||||
// Try FQN match if input was simple name
|
||||
if (directImpls == null) {
|
||||
String fqn = simpleNameToFqn.get(typeName);
|
||||
if (fqn != null) {
|
||||
directImpls = interfaceToImpls.get(fqn);
|
||||
if (directImpls != null) {
|
||||
directImplsSource = "simple→fqn";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try simple name match if input was FQN
|
||||
if (directImpls == null && cleanName.contains(".")) {
|
||||
String simpleName = cleanName.substring(cleanName.lastIndexOf('.') + 1);
|
||||
List<String> simpleImpls = interfaceToImpls.get(simpleName);
|
||||
if (simpleImpls != null) {
|
||||
if (ambiguousSimpleNames.contains(simpleName)) {
|
||||
String pkg = cleanName.substring(0, cleanName.lastIndexOf('.'));
|
||||
List<String> pkgScoped = new ArrayList<>();
|
||||
for (String impl : simpleImpls) {
|
||||
if (impl.startsWith(pkg + ".")) {
|
||||
pkgScoped.add(impl);
|
||||
}
|
||||
}
|
||||
directImpls = pkgScoped.isEmpty() ? null : pkgScoped;
|
||||
} else {
|
||||
directImpls = simpleImpls;
|
||||
}
|
||||
if (directImpls != null) {
|
||||
directImplsSource = "fqn→simple";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (directImplsSource == null) {
|
||||
directImplsSource = "none";
|
||||
}
|
||||
List<String> directImpls = resolveDirectImplementations(typeName, cleanName);
|
||||
String directImplsSource = describeDirectImplsSource(typeName, cleanName);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(
|
||||
@@ -535,6 +497,80 @@ public class CodebaseContext {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Union direct subclasses/implementors from every index bucket that refers to the same supertype.
|
||||
* Generated stubs often index under a literal FQN ({@code extends abcd.ccc.AML}) while hand-written
|
||||
* sources index under the simple name ({@code extends AML}) — merge instead of stopping at the first hit.
|
||||
*/
|
||||
private List<String> resolveDirectImplementations(String typeName, String cleanName) {
|
||||
LinkedHashSet<String> merged = new LinkedHashSet<>();
|
||||
if (cleanName == null || cleanName.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
addImplsForKey(merged, cleanName);
|
||||
|
||||
if (!cleanName.contains(".")) {
|
||||
String fqn = simpleNameToFqn.get(typeName != null ? typeName : cleanName);
|
||||
if (fqn != null && !fqn.equals(cleanName)) {
|
||||
addImplsForKey(merged, fqn);
|
||||
}
|
||||
} else {
|
||||
String simpleName = cleanName.substring(cleanName.lastIndexOf('.') + 1);
|
||||
if (!ambiguousSimpleNames.contains(simpleName)) {
|
||||
addImplsForKey(merged, simpleName);
|
||||
} else {
|
||||
String pkg = cleanName.substring(0, cleanName.lastIndexOf('.'));
|
||||
List<String> simpleImpls = interfaceToImpls.get(simpleName);
|
||||
if (simpleImpls != null) {
|
||||
for (String impl : simpleImpls) {
|
||||
if (impl.startsWith(pkg + ".")) {
|
||||
merged.add(impl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return merged.isEmpty() ? null : new ArrayList<>(merged);
|
||||
}
|
||||
|
||||
private void addImplsForKey(Set<String> merged, String key) {
|
||||
List<String> impls = interfaceToImpls.get(key);
|
||||
if (impls != null) {
|
||||
merged.addAll(impls);
|
||||
}
|
||||
}
|
||||
|
||||
private String describeDirectImplsSource(String typeName, String cleanName) {
|
||||
if (cleanName == null || cleanName.isBlank()) {
|
||||
return "none";
|
||||
}
|
||||
boolean hasDirect = interfaceToImpls.containsKey(cleanName);
|
||||
boolean hasFqnAlias = false;
|
||||
boolean hasSimpleAlias = false;
|
||||
if (!cleanName.contains(".")) {
|
||||
String fqn = simpleNameToFqn.get(typeName != null ? typeName : cleanName);
|
||||
hasFqnAlias = fqn != null && interfaceToImpls.containsKey(fqn);
|
||||
} else {
|
||||
String simpleName = cleanName.substring(cleanName.lastIndexOf('.') + 1);
|
||||
hasSimpleAlias = interfaceToImpls.containsKey(simpleName);
|
||||
}
|
||||
if (hasDirect && (hasFqnAlias || hasSimpleAlias)) {
|
||||
return "merged";
|
||||
}
|
||||
if (hasDirect) {
|
||||
return "direct";
|
||||
}
|
||||
if (hasFqnAlias) {
|
||||
return "simple→fqn";
|
||||
}
|
||||
if (hasSimpleAlias) {
|
||||
return "fqn→simple";
|
||||
}
|
||||
return "none";
|
||||
}
|
||||
|
||||
private void indexEnum(EnumDeclaration ed, String parentFqn, Path javaFile) {
|
||||
String simpleName = ed.getName().getIdentifier();
|
||||
String fqn = parentFqn.isEmpty() ? simpleName : parentFqn + "." + simpleName;
|
||||
|
||||
@@ -275,6 +275,32 @@ class CodebaseContextTest {
|
||||
assertThat(context.getImplementations("b.Service")).containsExactly("b.BServiceImpl");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImplementationsShouldMergeFqnAndSimpleNameIndexBuckets() throws IOException {
|
||||
Files.createDirectories(tempDir.resolve("abcd/ccc"));
|
||||
Files.writeString(tempDir.resolve("abcd/ccc/AML.java"), """
|
||||
package abcd.ccc;
|
||||
public abstract class AML {}
|
||||
""");
|
||||
Files.writeString(tempDir.resolve("abcd/ccc/RealListener.java"), """
|
||||
package abcd.ccc;
|
||||
public class RealListener extends AML {}
|
||||
""");
|
||||
// KAPT-style stub: no import, FQN in extends clause
|
||||
Files.writeString(tempDir.resolve("abcd/ccc/Av.java"), """
|
||||
package abcd.ccc;
|
||||
public class Av extends abcd.ccc.AML {}
|
||||
""");
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
assertThat(context.getImplementations("abcd.ccc.AML"))
|
||||
.containsExactlyInAnyOrder("abcd.ccc.Av", "abcd.ccc.RealListener");
|
||||
assertThat(context.getImplementations("AML"))
|
||||
.containsExactlyInAnyOrder("abcd.ccc.Av", "abcd.ccc.RealListener");
|
||||
}
|
||||
|
||||
@Test
|
||||
void areSameTypeOrUnambiguousSimpleMatchShouldFailClosedForAmbiguousTypes() throws IOException {
|
||||
Files.createDirectories(tempDir.resolve("a"));
|
||||
|
||||
Reference in New Issue
Block a user