Skip to content

Commit f245bff

Browse files
committed
Make SDN class generic.
1 parent fd586eb commit f245bff

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* Copyright 2011-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mapping.context;
17+
18+
import org.springframework.data.mapping.PersistentEntity;
19+
import org.springframework.data.mapping.PersistentProperty;
20+
import org.springframework.data.mapping.PropertyPath;
21+
import org.springframework.data.projection.ProjectionFactory;
22+
import org.springframework.data.projection.ProjectionInformation;
23+
import org.springframework.data.util.TypeInformation;
24+
25+
import java.beans.PropertyDescriptor;
26+
import java.util.ArrayList;
27+
import java.util.Collection;
28+
import java.util.Collections;
29+
import java.util.HashSet;
30+
import java.util.List;
31+
import java.util.function.Predicate;
32+
33+
/**
34+
* This class is responsible for creating a List of {@link PropertyPath} entries that contains all reachable
35+
* properties (w/o circles).
36+
*/
37+
public class PropertyFilterSupport {
38+
39+
public static List<PropertyPath> addPropertiesFrom(Class<?> returnType, Class<?> domainType,
40+
ProjectionFactory projectionFactory,
41+
Predicate<Class<?>> simpleTypePredicate, // TODO SimpleTypeHolder or CustomConversions
42+
MappingContext<?, ?> mappingContext) {
43+
44+
ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(returnType);
45+
46+
boolean openProjection = !projectionInformation.isClosed(); // if not closed projection, we would need everything from the entity
47+
boolean typeFromHierarchy = returnType.isAssignableFrom(domainType) || // hierarchy
48+
domainType.isAssignableFrom(returnType); // interface domainType is interface
49+
50+
if (openProjection || typeFromHierarchy) {
51+
// *Mark wants to do something with object checks here
52+
return Collections.emptyList();
53+
}
54+
// if ^^ false -> DTO / interface projection
55+
56+
List<PropertyPath> propertyPaths = new ArrayList<>();
57+
for (PropertyDescriptor inputProperty : projectionInformation.getInputProperties()) {
58+
addPropertiesFrom(returnType, domainType, projectionFactory, simpleTypePredicate, propertyPaths, inputProperty.getName(), mappingContext);
59+
}
60+
return propertyPaths;
61+
}
62+
63+
private static void addPropertiesFrom(Class<?> returnedType, Class<?> domainType, ProjectionFactory factory,
64+
Predicate<Class<?>> simpleTypePredicate,
65+
Collection<PropertyPath> filteredProperties, String inputProperty,
66+
MappingContext<?, ?> mappingContext) {
67+
68+
ProjectionInformation projectionInformation = factory.getProjectionInformation(returnedType);
69+
PropertyPath propertyPath;
70+
71+
// If this is a closed projection we can assume that the return type (possible projection type) contains
72+
// only fields accessible with a property path.
73+
if (projectionInformation.isClosed()) {
74+
propertyPath = PropertyPath.from(inputProperty, returnedType);
75+
} else {
76+
// otherwise the domain type is used right from the start
77+
propertyPath = PropertyPath.from(inputProperty, domainType);
78+
}
79+
80+
Class<?> propertyType = propertyPath.getLeafType();
81+
// 1. Simple types can be added directly
82+
// 2. Something that looks like an entity needs to get processed as such
83+
// 3. Embedded projection
84+
if (simpleTypePredicate.test(propertyType)) {
85+
filteredProperties.add(propertyPath);
86+
} else if (mappingContext.hasPersistentEntityFor(propertyType)) {
87+
// avoid recursion / cycles
88+
if (propertyType.equals(domainType)) {
89+
return;
90+
}
91+
processEntity(propertyPath, filteredProperties, simpleTypePredicate, mappingContext);
92+
93+
} else {
94+
ProjectionInformation nestedProjectionInformation = factory.getProjectionInformation(propertyType);
95+
filteredProperties.add(propertyPath);
96+
// Closed projection should get handled as above (recursion)
97+
if (nestedProjectionInformation.isClosed()) {
98+
for (PropertyDescriptor nestedInputProperty : nestedProjectionInformation.getInputProperties()) {
99+
PropertyPath nestedPropertyPath = propertyPath.nested(nestedInputProperty.getName());
100+
filteredProperties.add(nestedPropertyPath);
101+
addPropertiesFrom(domainType, returnedType, factory, simpleTypePredicate, filteredProperties,
102+
nestedPropertyPath.toDotPath(), mappingContext);
103+
}
104+
} else {
105+
// an open projection at this place needs to get replaced with the matching (real) entity
106+
PropertyPath domainTypeBasedPropertyPath = PropertyPath.from(propertyPath.toDotPath(), domainType);
107+
processEntity(domainTypeBasedPropertyPath, filteredProperties, simpleTypePredicate, mappingContext);
108+
}
109+
}
110+
}
111+
112+
private static void processEntity(PropertyPath propertyPath, Collection<PropertyPath> filteredProperties,
113+
Predicate<Class<?>> ding,
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, ding, propertyEntityType, mappingContext, new HashSet<>());
126+
}
127+
128+
private static void addPropertiesFromEntity(Collection<PropertyPath> filteredProperties, PropertyPath propertyPath,
129+
Predicate<Class<?>> ding,
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, ding, 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<?>> ding, 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()), ding, mappingContext, processedEntities);
165+
});
166+
}
167+
168+
private static void addPropertiesFromEntity(Collection<PropertyPath> filteredProperties, PropertyPath propertyPath,
169+
Predicate<Class<?>> ding, 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 (ding.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, ding, propertyType, mappingContext, processedEntities);
183+
}
184+
}
185+
}

0 commit comments

Comments
 (0)