1515 */
1616package org .springframework .data .mapping .context ;
1717
18- import org .springframework .data .mapping .PersistentEntity ;
19- import org .springframework .data .mapping .PersistentProperty ;
2018import org .springframework .data .mapping .PropertyPath ;
2119import org .springframework .data .projection .ProjectionFactory ;
2220import org .springframework .data .projection .ProjectionInformation ;
23- import org .springframework .data .util .TypeInformation ;
2421
2522import java .beans .PropertyDescriptor ;
26- import java .util .ArrayList ;
27- import java .util .Collection ;
2823import java .util .Collections ;
29- import java .util .HashSet ;
30- import java .util .List ;
24+ import java .util .Map ;
25+ import java .util .concurrent . ConcurrentHashMap ;
3126import java .util .function .Predicate ;
3227
3328/**
3631 */
3732public class PropertyFilterSupport {
3833
39- public static List <PropertyPath > addPropertiesFrom (Class <?> returnType , Class <?> domainType ,
40- ProjectionFactory projectionFactory ,
41- Predicate <Class <?>> simpleTypePredicate , // TODO SimpleTypeHolder or CustomConversions
42- MappingContext <?, ?> mappingContext ) {
34+ public static Map <PropertyPath , Boolean > addPropertiesFrom (Class <?> returnType , Class <?> domainType ,
35+ ProjectionFactory projectionFactory ,
36+ Predicate <Class <?>> simpleTypePredicate , // TODO SimpleTypeHolder or CustomConversions
37+ MappingContext <?, ?> mappingContext ) {
4338
4439 ProjectionInformation projectionInformation = projectionFactory .getProjectionInformation (returnType );
4540
@@ -49,11 +44,11 @@ public static List<PropertyPath> addPropertiesFrom(Class<?> returnType, Class<?>
4944
5045 if (openProjection || typeFromHierarchy ) {
5146 // *Mark wants to do something with object checks here
52- return Collections .emptyList ();
47+ return Collections .emptyMap ();
5348 }
5449 // if ^^ false -> DTO / interface projection
5550
56- List <PropertyPath > propertyPaths = new ArrayList <>();
51+ Map <PropertyPath , Boolean > propertyPaths = new ConcurrentHashMap <>();
5752 for (PropertyDescriptor inputProperty : projectionInformation .getInputProperties ()) {
5853 addPropertiesFrom (returnType , domainType , projectionFactory , simpleTypePredicate , propertyPaths , inputProperty .getName (), mappingContext );
5954 }
@@ -62,7 +57,7 @@ public static List<PropertyPath> addPropertiesFrom(Class<?> returnType, Class<?>
6257
6358 private static void addPropertiesFrom (Class <?> returnedType , Class <?> domainType , ProjectionFactory factory ,
6459 Predicate <Class <?>> simpleTypePredicate ,
65- Collection <PropertyPath > filteredProperties , String inputProperty ,
60+ Map <PropertyPath , Boolean > filteredProperties , String inputProperty ,
6661 MappingContext <?, ?> mappingContext ) {
6762
6863 ProjectionInformation projectionInformation = factory .getProjectionInformation (returnedType );
@@ -82,104 +77,30 @@ private static void addPropertiesFrom(Class<?> returnedType, Class<?> domainType
8277 // 2. Something that looks like an entity needs to get processed as such
8378 // 3. Embedded projection
8479 if (simpleTypePredicate .test (propertyType )) {
85- filteredProperties .add (propertyPath );
80+ filteredProperties .put (propertyPath , false );
8681 } else if (mappingContext .hasPersistentEntityFor (propertyType )) {
87- // avoid recursion / cycles
88- if (propertyType .equals (domainType )) {
89- return ;
90- }
91- processEntity (propertyPath , filteredProperties , simpleTypePredicate , mappingContext );
92-
82+ filteredProperties .put (propertyPath , true );
9383 } else {
9484 ProjectionInformation nestedProjectionInformation = factory .getProjectionInformation (propertyType );
95- filteredProperties .add (propertyPath );
9685 // Closed projection should get handled as above (recursion)
9786 if (nestedProjectionInformation .isClosed ()) {
87+ filteredProperties .put (propertyPath , false );
9888 for (PropertyDescriptor nestedInputProperty : nestedProjectionInformation .getInputProperties ()) {
9989 PropertyPath nestedPropertyPath = propertyPath .nested (nestedInputProperty .getName ());
100- filteredProperties .add (nestedPropertyPath );
90+
91+ if (propertyPath .hasNext () && (domainType .equals (propertyPath .getLeafProperty ().getOwningType ().getType ())
92+ || returnedType .equals (propertyPath .getLeafProperty ().getOwningType ().getType ()))) {
93+ break ;
94+ }
10195 addPropertiesFrom (domainType , returnedType , factory , simpleTypePredicate , filteredProperties ,
10296 nestedPropertyPath .toDotPath (), mappingContext );
10397 }
10498 } else {
10599 // an open projection at this place needs to get replaced with the matching (real) entity
106100 PropertyPath domainTypeBasedPropertyPath = PropertyPath .from (propertyPath .toDotPath (), domainType );
107- processEntity (domainTypeBasedPropertyPath , filteredProperties , simpleTypePredicate , mappingContext );
101+ filteredProperties . put (domainTypeBasedPropertyPath , true );
108102 }
109103 }
110104 }
111105
112- private static void processEntity (PropertyPath propertyPath , Collection <PropertyPath > filteredProperties ,
113- Predicate <Class <?>> simpleTypePredicate ,
114- MappingContext <?, ?> mappingContext ) {
115-
116- PropertyPath leafProperty = propertyPath .getLeafProperty ();
117- TypeInformation <?> propertyParentType = leafProperty .getOwningType ();
118- String inputProperty = leafProperty .getSegment ();
119-
120- PersistentEntity <?, ?> persistentEntity = mappingContext .getPersistentEntity (propertyParentType );
121- PersistentProperty <?> persistentProperty = persistentEntity .getPersistentProperty (inputProperty );
122- Class <?> propertyEntityType = persistentProperty .getActualType ();
123-
124- // Use domain type as root type for the property path
125- addPropertiesFromEntity (filteredProperties , propertyPath , simpleTypePredicate , propertyEntityType , mappingContext , new HashSet <>());
126- }
127-
128- private static void addPropertiesFromEntity (Collection <PropertyPath > filteredProperties , PropertyPath propertyPath ,
129- Predicate <Class <?>> simpleTypePredicate ,
130- Class <?> propertyType , MappingContext <?, ?> mappingContext ,
131- Collection <PersistentEntity <?, ?>> processedEntities ) {
132-
133- PersistentEntity <?, ?> persistentEntityFromProperty = mappingContext .getPersistentEntity (propertyType );
134- // break the recursion / cycles
135- if (hasProcessedEntity (persistentEntityFromProperty , processedEntities )) {
136- return ;
137- }
138- processedEntities .add (persistentEntityFromProperty );
139-
140- // save base/root entity/projection type to avoid recursion later
141- Class <?> pathRootType = propertyPath .getOwningType ().getType ();
142- if (mappingContext .hasPersistentEntityFor (pathRootType )) {
143- processedEntities .add (mappingContext .getPersistentEntity (pathRootType ));
144- }
145-
146- takeAllPropertiesFromEntity (filteredProperties , simpleTypePredicate , propertyPath , mappingContext , persistentEntityFromProperty , processedEntities );
147- }
148-
149- private static boolean hasProcessedEntity (PersistentEntity <?, ?> persistentEntityFromProperty ,
150- Collection <PersistentEntity <?, ?>> processedEntities ) {
151-
152- return processedEntities .contains (persistentEntityFromProperty );
153- }
154-
155- private static void takeAllPropertiesFromEntity (Collection <PropertyPath > filteredProperties ,
156- Predicate <Class <?>> simpleTypePredicate , PropertyPath propertyPath ,
157- MappingContext <?, ?> mappingContext ,
158- PersistentEntity <?, ?> persistentEntityFromProperty ,
159- Collection <PersistentEntity <?, ?>> processedEntities ) {
160-
161- filteredProperties .add (propertyPath );
162-
163- persistentEntityFromProperty .doWithAll (persistentProperty -> {
164- addPropertiesFromEntity (filteredProperties , propertyPath .nested (persistentProperty .getName ()), simpleTypePredicate , mappingContext , processedEntities );
165- });
166- }
167-
168- private static void addPropertiesFromEntity (Collection <PropertyPath > filteredProperties , PropertyPath propertyPath ,
169- Predicate <Class <?>> simpleTypePredicate , MappingContext <?, ?> mappingContext ,
170- Collection <PersistentEntity <?, ?>> processedEntities ) {
171-
172- // break the recursion / cycles
173- if (filteredProperties .contains (propertyPath )) {
174- return ;
175- }
176- Class <?> propertyType = propertyPath .getLeafType ();
177- // simple types can get added directly to the list.
178- if (simpleTypePredicate .test (propertyType )) {
179- filteredProperties .add (propertyPath );
180- // Other types are handled also as entities because there cannot be any nested projection within a real entity.
181- } else if (mappingContext .hasPersistentEntityFor (propertyType )) {
182- addPropertiesFromEntity (filteredProperties , propertyPath , simpleTypePredicate , propertyType , mappingContext , processedEntities );
183- }
184- }
185106}
0 commit comments