Skip to content

Commit d8486cd

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

File tree

11 files changed

+583
-28
lines changed

11 files changed

+583
-28
lines changed

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

+125-13
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66

77
import java.util.ArrayList;
88
import java.util.Collections;
9+
import java.util.LinkedHashSet;
910
import java.util.List;
1011
import java.util.Map;
12+
import java.util.Set;
1113
import java.util.concurrent.ConcurrentHashMap;
14+
import java.util.function.Predicate;
1215

1316
import org.hibernate.models.UnknownClassException;
17+
import org.hibernate.models.internal.util.CollectionHelper;
1418
import org.hibernate.models.spi.ClassDetails;
1519
import org.hibernate.models.spi.ModelsContext;
20+
import org.hibernate.models.spi.TypeDetails;
1621

1722
import static org.hibernate.models.spi.ClassDetails.CLASS_CLASS_DETAILS;
1823
import static org.hibernate.models.spi.ClassDetails.OBJECT_CLASS_DETAILS;
@@ -30,18 +35,22 @@ public abstract class AbstractClassDetailsRegistry implements MutableClassDetail
3035
protected final Map<String, ClassDetails> classDetailsMap;
3136

3237
// subtype per type
33-
protected final Map<String, List<ClassDetails>> subTypeClassDetailsMap;
38+
protected final Map<String, Set<ClassDetails>> directSubTypeMap;
39+
// implementor by interface
40+
protected final Map<String, Set<ClassDetails>> directImplementorMap;
3441

3542
protected AbstractClassDetailsRegistry(ModelsContext context) {
36-
this( new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), context );
43+
this( new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), context );
3744
}
3845

3946
protected AbstractClassDetailsRegistry(
4047
Map<String, ClassDetails> classDetailsMap,
41-
Map<String, List<ClassDetails>> subTypeClassDetailsMap,
48+
Map<String, Set<ClassDetails>> directSubTypeMap,
49+
Map<String, Set<ClassDetails>> directImplementorMap,
4250
ModelsContext context) {
4351
this.classDetailsMap = classDetailsMap;
44-
this.subTypeClassDetailsMap = subTypeClassDetailsMap;
52+
this.directSubTypeMap = directSubTypeMap;
53+
this.directImplementorMap = directImplementorMap;
4554
this.context = context;
4655

4756
classDetailsMap.put( CLASS_CLASS_DETAILS.getName(), CLASS_CLASS_DETAILS );
@@ -51,13 +60,22 @@ protected AbstractClassDetailsRegistry(
5160
}
5261

5362
@Override
54-
public List<ClassDetails> getDirectSubTypes(String superTypeName) {
55-
return subTypeClassDetailsMap.get( superTypeName );
63+
public List<ClassDetails> getDirectSubTypes(String typeName) {
64+
final Set<ClassDetails> directSubtypes = getDirectSubtypes( typeName );
65+
return CollectionHelper.isNotEmpty( directSubtypes )
66+
? new ArrayList<>( directSubtypes )
67+
: List.of();
5668
}
5769

5870
@Override
59-
public void forEachDirectSubType(String superTypeName, ClassDetailsConsumer consumer) {
60-
final List<ClassDetails> directSubTypes = getDirectSubTypes( superTypeName );
71+
public Set<ClassDetails> getDirectSubtypes(String typeName) {
72+
final Set<ClassDetails> directSubtypes = directSubTypeMap.get( typeName );
73+
return directSubtypes != null ? directSubtypes : Set.of();
74+
}
75+
76+
@Override
77+
public void forEachDirectSubtype(String typeName, ClassDetailsConsumer consumer) {
78+
final List<ClassDetails> directSubTypes = getDirectSubTypes( typeName );
6179
if ( directSubTypes == null ) {
6280
return;
6381
}
@@ -66,6 +84,85 @@ public void forEachDirectSubType(String superTypeName, ClassDetailsConsumer cons
6684
}
6785
}
6886

87+
@Override
88+
public Set<ClassDetails> getDirectImplementors(String interfaceName) {
89+
final Set<ClassDetails> implementors = directImplementorMap.get( interfaceName );
90+
return implementors != null ? implementors : Set.of();
91+
}
92+
93+
@Override
94+
public void forEachDirectImplementor(String interfaceName, ClassDetailsConsumer consumer) {
95+
final Set<ClassDetails> directImplementors = getDirectImplementors( interfaceName );
96+
if ( directImplementors != null ) {
97+
directImplementors.forEach( consumer::consume );
98+
}
99+
}
100+
101+
@Override
102+
public Set<ClassDetails> findConcreteTypes(String base, boolean includeBase) {
103+
final Set<ClassDetails> result = new LinkedHashSet<>();
104+
walkImplementors( base, includeBase, classDetails -> {
105+
if ( !classDetails.isAbstract() && !classDetails.isInterface() ) {
106+
result.add( classDetails );
107+
}
108+
109+
});
110+
return result;
111+
}
112+
113+
@Override
114+
public Set<ClassDetails> collectImplementors(String base, boolean includeBase, Predicate<ClassDetails> exclusions) {
115+
final Set<ClassDetails> result = new LinkedHashSet<>();
116+
walkImplementors( base, includeBase, classDetails -> {
117+
if ( exclusions == null || !exclusions.test( classDetails ) ) {
118+
result.add( classDetails );
119+
}
120+
} );
121+
return result;
122+
}
123+
124+
@Override
125+
public void walkImplementors(String base, boolean includeBase, ClassDetailsConsumer consumer) {
126+
if ( includeBase ) {
127+
final ClassDetails baseDetails = resolveClassDetails( base );
128+
consumer.consume( baseDetails );
129+
}
130+
131+
forEachDirectSubtype( base, (subType) -> {
132+
consumer.consume( subType );
133+
walkSubtypes( subType, consumer );
134+
} );
135+
136+
forEachDirectImplementor( base, (implementor) -> {
137+
consumer.consume( implementor );
138+
walkInterfaceImplementors( implementor, consumer );
139+
} );
140+
}
141+
142+
private void walkSubtypes(ClassDetails base, ClassDetailsConsumer consumer) {
143+
forEachDirectSubtype( base.getName(), (subType) -> {
144+
consumer.consume( subType );
145+
walkSubtypes( subType, consumer );
146+
} );
147+
}
148+
149+
private void walkInterfaceImplementors(ClassDetails implementor, ClassDetailsConsumer consumer) {
150+
if ( implementor.isInterface() ) {
151+
// the direct interface implementor is itself an interface...
152+
forEachDirectImplementor( implementor.getName(), (implementorImplementor) -> {
153+
consumer.consume( implementorImplementor );
154+
walkInterfaceImplementors( implementorImplementor, consumer );
155+
} );
156+
}
157+
else {
158+
// the direct interface implementor is itself a class...
159+
forEachDirectSubtype( implementor.getName(), (subtype) -> {
160+
consumer.consume( subtype );
161+
walkSubtypes( subtype, consumer );
162+
} );
163+
}
164+
}
165+
69166
@Override
70167
public ClassDetails findClassDetails(String name) {
71168
return classDetailsMap.get( name );
@@ -119,14 +216,25 @@ public void addClassDetails(String name, ClassDetails classDetails) {
119216
classDetailsMap.put( name, classDetails );
120217

121218
if ( classDetails.getSuperClass() != null ) {
122-
List<ClassDetails> subTypes = subTypeClassDetailsMap.get( classDetails.getSuperClass().getName() );
219+
Set<ClassDetails> subTypes = directSubTypeMap.get( classDetails.getSuperClass().getName() );
123220
//noinspection Java8MapApi
124221
if ( subTypes == null ) {
125-
subTypes = new ArrayList<>();
126-
subTypeClassDetailsMap.put( classDetails.getSuperClass().getName(), subTypes );
222+
subTypes = new LinkedHashSet<>();
223+
directSubTypeMap.put( classDetails.getSuperClass().getName(), subTypes );
127224
}
128225
subTypes.add( classDetails );
129226
}
227+
228+
final List<TypeDetails> implementedInterfaces = classDetails.getImplementedInterfaces();
229+
if ( implementedInterfaces != null ) {
230+
implementedInterfaces.forEach( (implementedInterface) -> {
231+
final Set<ClassDetails> directImplementors = directImplementorMap.computeIfAbsent(
232+
implementedInterface.getName(),
233+
(interfaceName) -> new LinkedHashSet<>()
234+
);
235+
directImplementors.add( classDetails );
236+
} );
237+
}
130238
}
131239

132240
@Override
@@ -172,7 +280,11 @@ public Map<String, ClassDetails> getClassDetailsMap() {
172280
return Collections.unmodifiableMap( classDetailsMap );
173281
}
174282

175-
public Map<String, List<ClassDetails>> getSubTypeClassDetailsMap() {
176-
return Collections.unmodifiableMap( subTypeClassDetailsMap );
283+
public Map<String, Set<ClassDetails>> getDirectSubTypeMap() {
284+
return Collections.unmodifiableMap( directSubTypeMap );
285+
}
286+
287+
public Map<String, Set<ClassDetails>> getDirectImplementorMap() {
288+
return Collections.unmodifiableMap( directImplementorMap );
177289
}
178290
}

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

+107-10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
package org.hibernate.models.spi;
66

77
import java.util.List;
8+
import java.util.Set;
9+
import java.util.function.Predicate;
810

911
import org.hibernate.models.UnknownClassException;
1012

@@ -18,6 +20,12 @@
1820
* @author Steve Ebersole
1921
*/
2022
public interface ClassDetailsRegistry {
23+
/**
24+
* Resolves a managed-class by name. If there is currently no such registration,
25+
* one is created.
26+
*/
27+
ClassDetails resolveClassDetails(String name);
28+
2129
/**
2230
* Find the managed-class with the given {@code name}, if there is one.
2331
* Returns {@code null} if there are none registered with that name.
@@ -48,33 +56,122 @@ default ClassDetails getClassDetails(String name) {
4856
void forEachClassDetails(ClassDetailsConsumer consumer);
4957

5058
/**
51-
* Get the list of all direct subtypes for the named managed-class. Returns
52-
* {@code null} if there are none
59+
* Get the list of all direct subtypes for the named managed-class.
60+
*
61+
* @deprecated Use {@linkplain #getDirectSubtypes(String)} instead.
5362
*/
63+
@Deprecated
5464
List<ClassDetails> getDirectSubTypes(String superTypeName);
5565

66+
/**
67+
* Get the list of all direct subtypes for the named managed-class.
68+
*/
69+
Set<ClassDetails> getDirectSubtypes(String superTypeName);
70+
5671
/**
5772
* Visit each direct subtype of the named managed-class
73+
*
74+
* @deprecated Use {@linkplain #forEachDirectSubtype} instead.
5875
*/
59-
@SuppressWarnings("unused")
60-
void forEachDirectSubType(String superTypeName, ClassDetailsConsumer consumer);
76+
@Deprecated
77+
default void forEachDirectSubType(String typeName, ClassDetailsConsumer consumer) {
78+
forEachDirectSubtype( typeName, consumer );
79+
}
6180

6281
/**
63-
* Resolves a managed-class by name. If there is currently no such registration,
64-
* one is created.
82+
* Visit each direct subtype of the named managed-class
6583
*/
66-
ClassDetails resolveClassDetails(String name);
84+
void forEachDirectSubtype(String typeName, ClassDetailsConsumer consumer);
6785

68-
default <S> S as(Class<S> type) {
69-
//noinspection unchecked
70-
return (S) this;
86+
/**
87+
* Get the list of all direct implementors for the named interface.
88+
*
89+
* @return The direct implementors of the named interface.
90+
*
91+
* @apiNote Does not verify that {@code interfaceName} actually names an interface.
92+
*/
93+
Set<ClassDetails> getDirectImplementors(String interfaceName);
94+
95+
/**
96+
* Visit each direct implementor of the named interface
97+
*
98+
* @apiNote Does not verify that {@code interfaceName} actually names an
99+
* interface. If it does not, no callbacks will happen.
100+
*/
101+
void forEachDirectImplementor(String interfaceName, ClassDetailsConsumer consumer);
102+
103+
/**
104+
* Find ClassDetails for all non-abstract subtypes / implementors
105+
* of the given base (which might be a class or interface).
106+
*
107+
* @param base The name of the class/interface from which to start walking.
108+
*
109+
* @apiNote If {@code base} is a concrete type, it will also be returned.
110+
* @see #findConcreteTypes(String, boolean)
111+
*/
112+
default Set<ClassDetails> findConcreteTypes(String base) {
113+
return findConcreteTypes( base, true );
114+
}
115+
116+
/**
117+
* Find ClassDetails for all non-abstract subtypes / implementors
118+
* of the given base (which might be a class or interface).
119+
*
120+
* @param base The name of the class/interface from which to start walking.
121+
* @param includeBase Whether to include {@code base} if it is concrete type.
122+
*
123+
* @see #getDirectSubTypes
124+
* @see #getDirectImplementors
125+
*/
126+
Set<ClassDetails> findConcreteTypes(String base, boolean includeBase);
127+
128+
/**
129+
* Walks the inheritance tree downward, starting from {@code base},
130+
* calling the consumer for each subclass and interface which is directly
131+
* an implementor.
132+
*
133+
* @param base The type from which to start.
134+
* @param includeBase Whether to include {@code base} if it is concrete type.
135+
* @param consumer The callback.
136+
*/
137+
void walkImplementors(String base, boolean includeBase, ClassDetailsConsumer consumer);
138+
139+
/**
140+
* Walks the inheritance tree, starting from {@code base}, "downward"
141+
* calling the consumer for each subclass and interface which is directly
142+
* an implementor.
143+
*
144+
* @param base The type from which to start.
145+
* @param includeBase Whether to include {@code base} if it is concrete type.
146+
*/
147+
default Set<ClassDetails> collectImplementors(String base, boolean includeBase) {
148+
return collectImplementors( base, includeBase, null );
71149
}
72150

151+
/**
152+
* Walks the inheritance tree, starting from {@code base}, "downward"
153+
* calling the consumer for each subclass and interface which is directly
154+
* an implementor.
155+
*
156+
* @param base The type from which to start.
157+
* @param includeBase Whether to include {@code base} if it is concrete type.
158+
* @param exclusions Exclusive check to filter ClassDetails out of the result.
159+
*/
160+
Set<ClassDetails> collectImplementors(String base, boolean includeBase, Predicate<ClassDetails> exclusions);
161+
73162
/**
74163
* Access to the ClassDetailsBuilder used in this registry
75164
*/
76165
ClassDetailsBuilder getClassDetailsBuilder();
77166

167+
@SuppressWarnings("unchecked")
168+
default <S> S as(Class<S> type) {
169+
if ( type.isInstance( this ) ) {
170+
return (S) this;
171+
}
172+
throw new UnsupportedOperationException( "Unsure how to cast " + this + " to " + type.getName() );
173+
}
174+
78175
@FunctionalInterface
79176
interface ClassDetailsConsumer {
80177
void consume(ClassDetails classDetails);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.models.testing.tests.classes;
6+
7+
/**
8+
* @author Steve Ebersole
9+
*/
10+
public interface Customer extends Person {
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.models.testing.tests.classes;
6+
7+
/**
8+
* @author Steve Ebersole
9+
*/
10+
public class CustomerImpl extends PersonImpl implements Customer {
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright: Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.models.testing.tests.classes;
6+
7+
/**
8+
* @author Steve Ebersole
9+
*/
10+
public interface Employee extends Person {
11+
}

0 commit comments

Comments
 (0)