Skip to content

Commit bbda259

Browse files
committed
#128 - Track interface implementors
1 parent 9e6a913 commit bbda259

File tree

3 files changed

+125
-9
lines changed

3 files changed

+125
-9
lines changed

hibernate-models/src/main/java/org/hibernate/models/internal/AbstractClassDetailsRegistry.java

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.hibernate.models.UnknownClassException;
1414
import org.hibernate.models.spi.ClassDetails;
1515
import org.hibernate.models.spi.ModelsContext;
16+
import org.hibernate.models.spi.TypeDetails;
1617

1718
import static org.hibernate.models.spi.ClassDetails.CLASS_CLASS_DETAILS;
1819
import static org.hibernate.models.spi.ClassDetails.OBJECT_CLASS_DETAILS;
@@ -30,18 +31,22 @@ public abstract class AbstractClassDetailsRegistry implements MutableClassDetail
3031
protected final Map<String, ClassDetails> classDetailsMap;
3132

3233
// subtype per type
33-
protected final Map<String, List<ClassDetails>> subTypeClassDetailsMap;
34+
protected final Map<String, List<ClassDetails>> directSubTypeMap;
35+
// implementor by interface
36+
protected final Map<String, List<ClassDetails>> directImplementorMap;
3437

3538
protected AbstractClassDetailsRegistry(ModelsContext context) {
36-
this( new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), context );
39+
this( new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), context );
3740
}
3841

3942
protected AbstractClassDetailsRegistry(
4043
Map<String, ClassDetails> classDetailsMap,
41-
Map<String, List<ClassDetails>> subTypeClassDetailsMap,
44+
Map<String, List<ClassDetails>> directSubTypeMap,
45+
Map<String, List<ClassDetails>> directImplementorMap,
4246
ModelsContext context) {
4347
this.classDetailsMap = classDetailsMap;
44-
this.subTypeClassDetailsMap = subTypeClassDetailsMap;
48+
this.directSubTypeMap = directSubTypeMap;
49+
this.directImplementorMap = directImplementorMap;
4550
this.context = context;
4651

4752
classDetailsMap.put( CLASS_CLASS_DETAILS.getName(), CLASS_CLASS_DETAILS );
@@ -52,7 +57,7 @@ protected AbstractClassDetailsRegistry(
5257

5358
@Override
5459
public List<ClassDetails> getDirectSubTypes(String superTypeName) {
55-
return subTypeClassDetailsMap.get( superTypeName );
60+
return directSubTypeMap.get( superTypeName );
5661
}
5762

5863
@Override
@@ -66,6 +71,62 @@ public void forEachDirectSubType(String superTypeName, ClassDetailsConsumer cons
6671
}
6772
}
6873

74+
@Override
75+
public List<ClassDetails> getDirectImplementors(String interfaceName) {
76+
return directImplementorMap.get( interfaceName );
77+
}
78+
79+
@Override
80+
public void forEachDirectImplementor(String interfaceName, ClassDetailsConsumer consumer) {
81+
final List<ClassDetails> directImplementors = getDirectImplementors( interfaceName );
82+
if ( directImplementors != null ) {
83+
directImplementors.forEach( consumer::consume );
84+
}
85+
}
86+
87+
@Override
88+
public List<ClassDetails> findConcreteTypes(String base) {
89+
final List<ClassDetails> result = new ArrayList<>();
90+
91+
forEachDirectSubType( base, (subType) -> {
92+
if ( !subType.isAbstract() ) {
93+
result.add( subType );
94+
}
95+
collectConcreteTypesFromClass( subType, result::add );
96+
} );
97+
98+
forEachDirectImplementor( base, (implementor) -> {
99+
collectConcreteTypesFromInterfaceImplementor( implementor, result::add );
100+
} );
101+
102+
return result;
103+
}
104+
105+
private void collectConcreteTypesFromClass(ClassDetails base, ClassDetailsConsumer collector) {
106+
forEachDirectSubType( base.getName(), (subType) -> {
107+
if ( !subType.isAbstract() ) {
108+
collector.consume( subType );
109+
}
110+
collectConcreteTypesFromClass( subType, collector );
111+
} );
112+
}
113+
114+
private void collectConcreteTypesFromInterfaceImplementor(ClassDetails implementor, ClassDetailsConsumer collector) {
115+
if ( implementor.isInterface() ) {
116+
// the direct interface implementor is itself an interface...
117+
forEachDirectImplementor( implementor.getName(), (implementorImplementor) -> {
118+
collectConcreteTypesFromInterfaceImplementor( implementor, collector );
119+
} );
120+
}
121+
else {
122+
// the direct interface implementor is itself a class...
123+
if ( !implementor.isAbstract() ) {
124+
collector.consume( implementor );
125+
}
126+
collectConcreteTypesFromClass( implementor, collector );
127+
}
128+
}
129+
69130
@Override
70131
public ClassDetails findClassDetails(String name) {
71132
return classDetailsMap.get( name );
@@ -119,14 +180,25 @@ public void addClassDetails(String name, ClassDetails classDetails) {
119180
classDetailsMap.put( name, classDetails );
120181

121182
if ( classDetails.getSuperClass() != null ) {
122-
List<ClassDetails> subTypes = subTypeClassDetailsMap.get( classDetails.getSuperClass().getName() );
183+
List<ClassDetails> subTypes = directSubTypeMap.get( classDetails.getSuperClass().getName() );
123184
//noinspection Java8MapApi
124185
if ( subTypes == null ) {
125186
subTypes = new ArrayList<>();
126-
subTypeClassDetailsMap.put( classDetails.getSuperClass().getName(), subTypes );
187+
directSubTypeMap.put( classDetails.getSuperClass().getName(), subTypes );
127188
}
128189
subTypes.add( classDetails );
129190
}
191+
192+
final List<TypeDetails> implementedInterfaces = classDetails.getImplementedInterfaces();
193+
if ( implementedInterfaces != null ) {
194+
implementedInterfaces.forEach( (implementedInterface) -> {
195+
final List<ClassDetails> directImplementors = directImplementorMap.computeIfAbsent(
196+
implementedInterface.getName(),
197+
(interfaceName) -> new ArrayList<>()
198+
);
199+
directImplementors.add( classDetails );
200+
} );
201+
}
130202
}
131203

132204
@Override
@@ -172,7 +244,7 @@ public Map<String, ClassDetails> getClassDetailsMap() {
172244
return Collections.unmodifiableMap( classDetailsMap );
173245
}
174246

175-
public Map<String, List<ClassDetails>> getSubTypeClassDetailsMap() {
176-
return Collections.unmodifiableMap( subTypeClassDetailsMap );
247+
public Map<String, List<ClassDetails>> getDirectSubTypeMap() {
248+
return Collections.unmodifiableMap( directSubTypeMap );
177249
}
178250
}

hibernate-models/src/main/java/org/hibernate/models/spi/ClassDetailsRegistry.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,27 @@ default ClassDetails getClassDetails(String name) {
5959
@SuppressWarnings("unused")
6060
void forEachDirectSubType(String superTypeName, ClassDetailsConsumer consumer);
6161

62+
/**
63+
* Get the list of all direct implementors for the named interface.
64+
* Returns {@code null} if there are none.
65+
*/
66+
List<ClassDetails> getDirectImplementors(String interfaceName);
67+
68+
/**
69+
* Visit each direct implementor of the named interface
70+
*/
71+
@SuppressWarnings("unused")
72+
void forEachDirectImplementor(String interfaceName, ClassDetailsConsumer consumer);
73+
74+
/**
75+
* Find ClassDetails for all non-abstract subtypes / implementors
76+
* of the given base (which might be a class or interface).
77+
*
78+
* @see #getDirectSubTypes
79+
* @see #getDirectImplementors
80+
*/
81+
List<ClassDetails> findConcreteTypes(String base);
82+
6283
/**
6384
* Resolves a managed-class by name. If there is currently no such registration,
6485
* one is created.

hibernate-models/src/test/java/org/hibernate/models/testing/tests/classes/InheritanceTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,27 @@ void testForEachDirectSubType() {
164164
assertThat( subTypes ).hasSize( 0 );
165165
}
166166

167+
@Test
168+
void testImplementors() {
169+
final ModelsContext modelsContext = buildModelContext(
170+
RootClass.class,
171+
TrunkClass.class,
172+
BranchClass.class,
173+
LeafClass.class
174+
);
175+
176+
final ClassDetailsRegistry classDetailsRegistry = modelsContext.getClassDetailsRegistry();
177+
final List<ClassDetails> directImplementors = classDetailsRegistry.getDirectImplementors( Intf.class.getName() );
178+
assertThat( directImplementors ).hasSize( 1 );
179+
final ClassDetails directImplementor = directImplementors.get( 0 );
180+
assertThat( directImplementor.getClassName() ).isEqualTo( BranchClass.class.getName() );
181+
assertThat( directImplementor.isImplementor( Intf.class ) ).isTrue();
182+
183+
final List<ClassDetails> concreteTypes = classDetailsRegistry.findConcreteTypes( Intf.class.getName() );
184+
assertThat( concreteTypes ).hasSize( 2 );
185+
assertThat( concreteTypes.stream().map( ClassDetails::getName ) ).contains(
186+
BranchClass.class.getName(),
187+
LeafClass.class.getName()
188+
);
189+
}
167190
}

0 commit comments

Comments
 (0)