From 6e402da6ed48bd68f5ae657b360a9ea4aa7e7c2b Mon Sep 17 00:00:00 2001 From: Andreas Schilling Date: Thu, 6 Mar 2025 10:52:34 +0100 Subject: [PATCH] Make chain resolution via null collections safe fix #717 --- ...endedStaticMetaModelFunctionalityTest.java | 35 +++++++++++++++++++ ...ollectionPropertyChainElementAccessor.java | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/core/esmf-aspect-model-java-generator/src/test/java/org/eclipse/esmf/aspectmodel/java/ExtendedStaticMetaModelFunctionalityTest.java b/core/esmf-aspect-model-java-generator/src/test/java/org/eclipse/esmf/aspectmodel/java/ExtendedStaticMetaModelFunctionalityTest.java index eb8a0e350..2bd43a213 100644 --- a/core/esmf-aspect-model-java-generator/src/test/java/org/eclipse/esmf/aspectmodel/java/ExtendedStaticMetaModelFunctionalityTest.java +++ b/core/esmf-aspect-model-java-generator/src/test/java/org/eclipse/esmf/aspectmodel/java/ExtendedStaticMetaModelFunctionalityTest.java @@ -151,6 +151,41 @@ void testCollectionPropertyChain() throws IOException, ReflectiveOperationExcept "nested-entity-string-2" ); } + @Test + void testCollectionPropertyChainWithNullCollectionInBetween() throws IOException, ReflectiveOperationException { + final TestAspect aspect = TestAspect.ASPECT_WITH_NESTED_ENTITY_LIST; + final StaticClassGenerationResult result = TestContext.generateStaticAspectCode().apply( getGenerators( aspect ) ); + + final Class aspectClass = findGeneratedClass( result, "AspectWithNestedEntityList" ); + final Class entityClass = findGeneratedClass( result, "TestFirstEntity" ); + final Class metaAspectClass = findGeneratedClass( result, "MetaAspectWithNestedEntityList" ); + final Class metaEntity = findGeneratedClass( result, "MetaTestFirstEntity" ); + final Class metaNestedEntity = findGeneratedClass( result, "MetaTestSecondEntity" ); + + final StaticContainerProperty> listProperty = + (StaticContainerProperty>) metaAspectClass.getField( "TEST_LIST" ).get( null ); + + final StaticContainerProperty> nestedListProperty = + (StaticContainerProperty>) metaEntity.getField( "TEST_SECOND_LIST" ).get( null ); + + final StaticProperty nestedEntityStringProperty = + (StaticProperty) metaNestedEntity.getField( "RANDOM_VALUE" ).get( null ); + + final ContainerPropertyChain, Object> entityStringCollectionChain = PropertyChain.fromCollection( listProperty ) + .via( nestedListProperty ) + .to( nestedEntityStringProperty ); + + final Object entityInstance = ConstructorUtils.invokeConstructor( entityClass, "Some Text", 123, 123.0f, null ); + final Object aspectInstance = ConstructorUtils.invokeConstructor( aspectClass, List.of( entityInstance ) ); + + assertThat( entityStringCollectionChain.getProperties() ).hasSize( 3 ); + assertThat( entityStringCollectionChain.getFirstProperty() ).isEqualTo( listProperty ); + assertThat( entityStringCollectionChain.getLastProperty() ).isEqualTo( nestedEntityStringProperty ); + assertThat( entityStringCollectionChain.getPropertyType() ).isEqualTo( List.class ); + assertThat( entityStringCollectionChain.getContainingType() ).isEqualTo( aspectClass ); + assertThat( entityStringCollectionChain.getValue( aspectInstance ) ).isEmpty(); + } + @Test void testSinglePropertyPredicates() throws IOException, ReflectiveOperationException { final TestAspect aspect = TestAspect.ASPECT_WITH_NESTED_ENTITY_LIST; diff --git a/core/esmf-aspect-static-meta-model-java/src/main/java/org/eclipse/esmf/staticmetamodel/propertychain/CollectionPropertyChainElementAccessor.java b/core/esmf-aspect-static-meta-model-java/src/main/java/org/eclipse/esmf/staticmetamodel/propertychain/CollectionPropertyChainElementAccessor.java index f682277e2..adb1e4459 100644 --- a/core/esmf-aspect-static-meta-model-java/src/main/java/org/eclipse/esmf/staticmetamodel/propertychain/CollectionPropertyChainElementAccessor.java +++ b/core/esmf-aspect-static-meta-model-java/src/main/java/org/eclipse/esmf/staticmetamodel/propertychain/CollectionPropertyChainElementAccessor.java @@ -38,7 +38,7 @@ public Object getValue( final Collection currentValue, return nextCollection.stream(); } - return Stream.of( nextValue ); + return Stream.ofNullable( nextValue ); } ).toList(); } }