1515 */
1616package org .springframework .data .mapping .context ;
1717
18+ import java .util .ArrayList ;
1819import java .util .Collection ;
1920import java .util .Collections ;
2021import java .util .Iterator ;
2324import java .util .function .Consumer ;
2425
2526import org .springframework .data .mapping .PropertyPath ;
27+ import org .springframework .data .projection .ProjectionInformation ;
2628import org .springframework .data .util .ClassTypeInformation ;
2729import org .springframework .data .util .Streamable ;
2830import org .springframework .data .util .TypeInformation ;
3436 *
3537 * @param <M> the mapped type acting as view onto the domain type.
3638 * @param <D> the domain type.
39+ * @author Mark Paluch
40+ * @author Christoph Strobl
3741 * @since 2.7
3842 */
3943public class EntityProjection <M , D > implements Streamable <EntityProjection .PropertyProjection <?, ?>> {
@@ -42,61 +46,63 @@ public class EntityProjection<M, D> implements Streamable<EntityProjection.Prope
4246 private final TypeInformation <D > domainType ;
4347 private final List <PropertyProjection <?, ?>> properties ;
4448 private final boolean projection ;
45- private final boolean closedProjection ;
49+ private final ProjectionType projectionType ;
4650
4751 EntityProjection (TypeInformation <M > mappedType , TypeInformation <D > domainType ,
48- List <PropertyProjection <?, ?>> properties , boolean projection , boolean closedProjection ) {
52+ List <PropertyProjection <?, ?>> properties , boolean projection , ProjectionType projectionType ) {
4953 this .mappedType = mappedType ;
5054 this .domainType = domainType ;
51- this .properties = properties ;
55+ this .properties = new ArrayList <>( properties ) ;
5256 this .projection = projection ;
53- this .closedProjection = closedProjection ;
57+ this .projectionType = projectionType ;
5458 }
5559
5660 /**
5761 * Create a projecting variant of a mapped type.
5862 *
59- * @param mappedType
60- * @param domainType
61- * @param properties
62- * @return
63+ * @param mappedType the target projection type. Must not be {@literal null}.
64+ * @param domainType the source domain type. Must not be {@literal null}.
65+ * @param properties properties to include.
66+ * @param projectionType must not be {@literal null}.
67+ * @return new instance of {@link EntityProjection}.
6368 */
6469 public static <M , D > EntityProjection <M , D > projecting (TypeInformation <M > mappedType , TypeInformation <D > domainType ,
65- List <PropertyProjection <?, ?>> properties , boolean closedProjection ) {
66- return new EntityProjection <>(mappedType , domainType , properties , true , closedProjection );
70+ List <PropertyProjection <?, ?>> properties , ProjectionType projectionType ) {
71+ return new EntityProjection <>(mappedType , domainType , properties , true , projectionType );
6772 }
6873
6974 /**
7075 * Create a non-projecting variant of a mapped type.
7176 *
72- * @param mappedType
73- * @param domainType
74- * @param properties
75- * @return
77+ * @param mappedType the target projection type. Must not be {@literal null}.
78+ * @param domainType the source domain type. Must not be {@literal null}.
79+ * @param properties properties to include.
80+ * @return new instance of {@link EntityProjection}.
7681 */
7782 public static <M , D > EntityProjection <M , D > nonProjecting (TypeInformation <M > mappedType ,
7883 TypeInformation <D > domainType , List <PropertyProjection <?, ?>> properties ) {
79- return new EntityProjection <>(mappedType , domainType , properties , false , false );
84+ return new EntityProjection <>(mappedType , domainType , properties , false , ProjectionType . CLOSED );
8085 }
8186
8287 /**
8388 * Create a non-projecting variant of a {@code type}.
8489 *
85- * @param type
86- * @return
90+ * @param type must not be {@literal null}.
91+ * @return new instance of {@link EntityProjection}.
8792 */
8893 public static <T > EntityProjection <T , T > nonProjecting (Class <T > type ) {
8994 ClassTypeInformation <T > typeInformation = ClassTypeInformation .from (type );
90- return new EntityProjection <>(typeInformation , typeInformation , Collections .emptyList (), false , false );
95+ return new EntityProjection <>(typeInformation , typeInformation , Collections .emptyList (), false ,
96+ ProjectionType .CLOSED );
9197 }
9298
9399 /**
94100 * Performs the given action for each element of the {@link Streamable} recursively until all elements of the graph
95- * have been processed or the action throws an exception . Unless otherwise specified by the implementing class,
96- * actions are performed in the order of iteration (if an iteration order is specified). Exceptions thrown by the
97- * action are relayed to the caller.
101+ * have been processed or the action throws an {@link Exception} . Unless otherwise specified by the implementing
102+ * class, actions are performed in the order of iteration (if an iteration order is specified). Exceptions thrown by
103+ * the action are relayed to the caller.
98104 *
99- * @param action
105+ * @param action must not be {@literal null}.
100106 */
101107 public void forEachRecursive (Consumer <? super PropertyProjection <?, ?>> action ) {
102108
@@ -128,6 +134,7 @@ public TypeInformation<M> getMappedType() {
128134 /**
129135 * @return the actual mapped type used by this type view. Should be used for collection-like and map-like properties
130136 * to determine the actual view type.
137+ * @throws IllegalStateException if the actual type cannot be resolved.
131138 */
132139 public TypeInformation <?> getActualMappedType () {
133140 return mappedType .getRequiredActualType ();
@@ -143,6 +150,7 @@ public TypeInformation<D> getDomainType() {
143150 /**
144151 * @return the actual domain type represented by this type view. Should be used for collection-like and map-like
145152 * properties to determine the actual domain type.
153+ * @throws IllegalStateException if the actual type cannot be resolved.
146154 */
147155 public TypeInformation <?> getActualDomainType () {
148156 return domainType .getRequiredActualType ();
@@ -159,7 +167,8 @@ public boolean isProjection() {
159167 * @return {@code true} if the {@link #getMappedType()} is a closed projection.
160168 */
161169 public boolean isClosedProjection () {
162- return isProjection () && closedProjection ;
170+ return isProjection ()
171+ && (ProjectionType .CLOSED .equals (projectionType ) || ProjectionType .DTO .equals (projectionType ));
163172 }
164173
165174 List <PropertyProjection <?, ?>> getProperties () {
@@ -207,36 +216,38 @@ public static class PropertyProjection<M, D> extends EntityProjection<M, D> {
207216 private final PropertyPath propertyPath ;
208217
209218 PropertyProjection (PropertyPath propertyPath , TypeInformation <M > mappedType , TypeInformation <D > domainType ,
210- List <PropertyProjection <?, ?>> properties , boolean projecting , boolean closedProjection ) {
211- super (mappedType , domainType , properties , projecting , closedProjection );
219+ List <PropertyProjection <?, ?>> properties , boolean projecting , ProjectionType projectionType ) {
220+ super (mappedType , domainType , properties , projecting , projectionType );
212221 this .propertyPath = propertyPath ;
213222 }
214223
215224 /**
216225 * Create a projecting variant of a mapped type.
217226 *
218- * @param propertyPath
219- * @param mappedType
220- * @param domainType
221- * @param properties
222- * @return
227+ * @param propertyPath the {@link PropertyPath path} to the actual property.
228+ * @param mappedType the target projection type. Must not be {@literal null}.
229+ * @param domainType the source domain type. Must not be {@literal null}.
230+ * @param properties properties to include.
231+ * @param projectionType must not be {@literal null}.
232+ * @return new instance of {@link PropertyProjection}.
223233 */
224234 public static <M , D > PropertyProjection <M , D > projecting (PropertyPath propertyPath , TypeInformation <M > mappedType ,
225- TypeInformation <D > domainType , List <PropertyProjection <?, ?>> properties , boolean closedProjection ) {
226- return new PropertyProjection <>(propertyPath , mappedType , domainType , properties , true , closedProjection );
235+ TypeInformation <D > domainType , List <PropertyProjection <?, ?>> properties , ProjectionType projectionType ) {
236+ return new PropertyProjection <>(propertyPath , mappedType , domainType , properties , true , projectionType );
227237 }
228238
229239 /**
230240 * Create a non-projecting variant of a mapped type.
231241 *
232- * @param propertyPath
233- * @param mappedType
234- * @param domainType
242+ * @param propertyPath the {@link PropertyPath path} to the actual property.
243+ * @param mappedType the target projection type. Must not be {@literal null}.
244+ * @param domainType the source domain type. Must not be {@literal null}.
235245 * @return
236246 */
237247 public static <M , D > PropertyProjection <M , D > nonProjecting (PropertyPath propertyPath ,
238248 TypeInformation <M > mappedType , TypeInformation <D > domainType ) {
239- return new PropertyProjection <>(propertyPath , mappedType , domainType , Collections .emptyList (), false , false );
249+ return new PropertyProjection <>(propertyPath , mappedType , domainType , Collections .emptyList (), false ,
250+ ProjectionType .OPEN );
240251 }
241252
242253 /**
@@ -264,39 +275,79 @@ public String toString() {
264275 public static class ContainerPropertyProjection <M , D > extends PropertyProjection <M , D > {
265276
266277 ContainerPropertyProjection (PropertyPath propertyPath , TypeInformation <M > mappedType , TypeInformation <D > domainType ,
267- List <PropertyProjection <?, ?>> properties , boolean projecting , boolean closedProjection ) {
268- super (propertyPath , mappedType , domainType , properties , projecting , closedProjection );
278+ List <PropertyProjection <?, ?>> properties , boolean projecting , ProjectionType projectionType ) {
279+ super (propertyPath , mappedType , domainType , properties , projecting , projectionType );
269280 }
270281
271282 /**
272283 * Create a projecting variant of a mapped type.
273284 *
274- * @param propertyPath
275- * @param mappedType
276- * @param domainType
277- * @param properties
278- * @return
285+ * @param propertyPath the {@link PropertyPath path} to the actual property.
286+ * @param mappedType the target projection type. Must not be {@literal null}.
287+ * @param domainType the source domain type. Must not be {@literal null}.
288+ * @param properties properties to include.
289+ * @param projectionType must not be {@literal null}.
290+ * @return new instance of {@link ContainerPropertyProjection}.
279291 */
280292 public static <M , D > ContainerPropertyProjection <M , D > projecting (PropertyPath propertyPath ,
281293 TypeInformation <M > mappedType , TypeInformation <D > domainType , List <PropertyProjection <?, ?>> properties ,
282- boolean closedProjection ) {
283- return new ContainerPropertyProjection <>(propertyPath , mappedType , domainType , properties , true ,
284- closedProjection );
294+ ProjectionType projectionType ) {
295+ return new ContainerPropertyProjection <>(propertyPath , mappedType , domainType , properties , true , projectionType );
285296 }
286297
287298 /**
288299 * Create a non-projecting variant of a mapped type.
289300 *
290- * @param propertyPath
291- * @param mappedType
292- * @param domainType
293- * @return
301+ * @param propertyPath the {@link PropertyPath path} to the actual property.
302+ * @param mappedType the target projection type. Must not be {@literal null}.
303+ * @param domainType the source domain type. Must not be {@literal null}.
304+ * @return new instance of {@link ContainerPropertyProjection}.
294305 */
295306 public static <M , D > ContainerPropertyProjection <M , D > nonProjecting (PropertyPath propertyPath ,
296307 TypeInformation <M > mappedType , TypeInformation <D > domainType ) {
297308 return new ContainerPropertyProjection <>(propertyPath , mappedType , domainType , Collections .emptyList (), false ,
298- false );
309+ ProjectionType . OPEN );
299310 }
300311
301312 }
313+
314+ /**
315+ * Projection type.
316+ *
317+ * @since 2.7
318+ */
319+ public enum ProjectionType {
320+
321+ /**
322+ * A DTO projection defines a value type that hold properties for the fields that are supposed to be retrieved.
323+ */
324+ DTO ,
325+
326+ /**
327+ * An open projection has accessor methods in the interface that can be used to compute new values by using the
328+ * {@link org.springframework.beans.factory.annotation.Value} annotation.
329+ */
330+ OPEN ,
331+
332+ /**
333+ * A closed projection only contains accessor methods that all match properties of the target aggregate.
334+ */
335+ CLOSED ;
336+
337+ /**
338+ * Obtain the {@link ProjectionType} from a given {@link ProjectionInformation}.
339+ *
340+ * @param information must not be {@literal null}.
341+ * @return the {@link ProjectionType} according to {@link ProjectionInformation#getType() type} and
342+ * {@link ProjectionInformation#isClosed()}.
343+ */
344+ public static ProjectionType from (ProjectionInformation information ) {
345+
346+ if (!information .getType ().isInterface ()) {
347+ return DTO ;
348+ }
349+
350+ return information .isClosed () ? CLOSED : OPEN ;
351+ }
352+ }
302353}
0 commit comments