Skip to content

Commit 4d067ef

Browse files
committed
Fix issue with constrained complex collections.
1 parent db05887 commit 4d067ef

File tree

7 files changed

+327
-42
lines changed

7 files changed

+327
-42
lines changed

core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/json/AspectModelJsonPayloadGenerator.java

Lines changed: 73 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.util.Random;
3535
import java.util.function.BiFunction;
3636
import java.util.function.Function;
37+
import java.util.function.Supplier;
3738
import java.util.stream.Stream;
3839

3940
import javax.xml.datatype.DatatypeFactory;
@@ -162,7 +163,7 @@ private static ObjectMapper createObjectMapper() {
162163
}
163164

164165
private Map<String, Object> transformAspectProperties() {
165-
return transformProperties( aspect.getProperties() );
166+
return transformProperties( aspect.getProperties(), true );
166167
}
167168

168169
/**
@@ -172,15 +173,15 @@ private Map<String, Object> transformAspectProperties() {
172173
@SuppressWarnings( "squid:S2250" )
173174
// Amount of elements in list is in regard to amount of properties in Aspect Model. Even in bigger aspects this
174175
// should not lead to performance issues
175-
private Map<String, Object> transformProperties( final List<Property> properties ) {
176+
private Map<String, Object> transformProperties( final List<Property> properties, final boolean useModelExampleValue ) {
176177
return Stream.concat(
177178
properties.stream().filter( recursiveProperty::contains ).map( this::recursiveProperty ),
178179
properties.stream()
179180
.filter( property -> !recursiveProperty.contains( property ) )
180181
.filter( property -> !property.isAbstract() )
181182
.map( property -> {
182183
recursiveProperty.add( property );
183-
final Map<String, Object> result = transformProperty( new BasicProperty( property ) );
184+
final Map<String, Object> result = transformProperty( new BasicProperty( property ), useModelExampleValue );
184185
recursiveProperty.remove( property );
185186
return result;
186187
} )
@@ -207,21 +208,22 @@ private Map<String, Object> recursiveProperty( final Property property ) {
207208
* @param property the property to transform
208209
* @return a map representing the property names as key and the property values as value
209210
*/
210-
private Map<String, Object> transformProperty( final BasicProperty property ) {
211+
private Map<String, Object> transformProperty( final BasicProperty property, final boolean useModelExampleValue ) {
211212
return transformers.stream()
212-
.map( transformer -> transformer.apply( property ) )
213+
.map( transformer -> transformer.apply( property, useModelExampleValue ) )
213214
.filter( propertiesMap -> !propertiesMap.isEmpty() )
214215
.findFirst()
215216
.orElseThrow( () -> new IllegalArgumentException( "No transformer for " + property.getName() + " available." ) );
216217
}
217218

218-
private Map<String, Object> transformCollectionProperty( final BasicProperty property ) {
219+
private Map<String, Object> transformCollectionProperty( final BasicProperty property, final boolean useModelExampleValue ) {
219220
final Characteristic characteristic = property.getCharacteristic();
220221
if ( characteristic.is( Collection.class ) ) {
221222
final List<Object> collectionValues = getCollectionValues( property, (Collection) characteristic );
222223
return toMap( property.getName(), collectionValues );
223224
} else if ( isConstrainedCollection( characteristic ) ) {
224-
return toMap( property.getName(), getCollectionValues( property, characteristic.as( Trait.class ).getBaseCharacteristic().as( Collection.class ) ) );
225+
return toMap( property.getName(), getCollectionValues( property, characteristic.as( Trait.class ).getBaseCharacteristic().as( Collection.class ),
226+
(LengthConstraint) characteristic.as( Trait.class ).getConstraints().get( 0 ) ) );
225227
}
226228
return ImmutableMap.of();
227229
}
@@ -235,23 +237,23 @@ private boolean isConstrainedCollection( final Characteristic characteristic ) {
235237
trait.getConstraints().get( 0 ).is( LengthConstraint.class );
236238
}
237239

238-
private Map<String, Object> transformAbstractEntityProperty( final BasicProperty property ) {
240+
private Map<String, Object> transformAbstractEntityProperty( final BasicProperty property, final boolean useModelExampleValue ) {
239241
final Optional<AbstractEntity> dataType = getForCharacteristic( property.getCharacteristic(), AbstractEntity.class );
240242
if ( dataType.isPresent() ) {
241243
final AbstractEntity abstractEntity = dataType.get();
242244
final ComplexType extendingComplexType = abstractEntity.getExtendingElements().get( 0 );
243-
final Map<String, Object> generatedProperties = transformProperties( extendingComplexType.getAllProperties() );
245+
final Map<String, Object> generatedProperties = transformProperties( extendingComplexType.getAllProperties(), useModelExampleValue );
244246
generatedProperties.put( "@type", extendingComplexType.getName() );
245247
return toMap( property.getName(), generatedProperties );
246248
}
247249
return ImmutableMap.of();
248250
}
249251

250-
private Map<String, Object> transformEntityProperty( final BasicProperty property ) {
252+
private Map<String, Object> transformEntityProperty( final BasicProperty property, final boolean useModelExmplevalue ) {
251253
final Optional<Entity> dataType = getForCharacteristic( property.getCharacteristic(), Entity.class );
252254
if ( dataType.isPresent() ) {
253255
final Entity entity = dataType.get();
254-
final Map<String, Object> generatedProperties = transformProperties( entity.getAllProperties() );
256+
final Map<String, Object> generatedProperties = transformProperties( entity.getAllProperties(), useModelExmplevalue );
255257
if ( entity.getExtends().isPresent() ) {
256258
generatedProperties.put( "@type", entity.getName() );
257259
}
@@ -260,7 +262,7 @@ private Map<String, Object> transformEntityProperty( final BasicProperty propert
260262
return ImmutableMap.of();
261263
}
262264

263-
private Map<String, Object> transformEnumeration( final BasicProperty property ) {
265+
private Map<String, Object> transformEnumeration( final BasicProperty property, final boolean useModelExampleValue ) {
264266
return getForCharacteristic( property.getCharacteristic(), Enumeration.class )
265267
.map( enumeration -> extractEnumerationValues( property, enumeration ) )
266268
.orElseGet( ImmutableMap::of );
@@ -271,24 +273,24 @@ private Map<String, Object> extractEnumerationValues( final BasicProperty proper
271273
return toMap( property.getName(), firstValue.accept( valueToPayloadStructure, null ) );
272274
}
273275

274-
private Map<String, Object> transformEitherProperty( final BasicProperty property ) {
276+
private Map<String, Object> transformEitherProperty( final BasicProperty property, final boolean useModelExampleValue ) {
275277
final Characteristic characteristic = property.getCharacteristic();
276278
return getForCharacteristic( characteristic, Either.class )
277279
.map( value -> transformProperty(
278-
new BasicProperty( AspectModelJsonPayloadGenerator.EITHER_LEFT, value.getLeft(), Optional.empty() ) ) )
280+
new BasicProperty( AspectModelJsonPayloadGenerator.EITHER_LEFT, value.getLeft(), Optional.empty() ), useModelExampleValue ) )
279281
.map( value -> toMap( property.getName(), value ) )
280282
.orElseGet( ImmutableMap::of );
281283
}
282284

283-
private Map<String, Object> transformSimpleProperty( final BasicProperty basicProperty ) {
284-
return toMap( basicProperty.getName(), getExampleValueOrElseRandom( basicProperty ) );
285+
private Map<String, Object> transformSimpleProperty( final BasicProperty basicProperty, final boolean useModelExampleValue ) {
286+
return toMap( basicProperty.getName(), getExampleValueOrElseRandom( basicProperty, useModelExampleValue ) );
285287
}
286288

287289
/**
288290
* @param property the property to transform
289291
* @return the {@link Property#getExampleValue()} or if absent a random value.
290292
*/
291-
private Object getExampleValueOrElseRandom( final BasicProperty property ) {
293+
private Object getExampleValueOrElseRandom( final BasicProperty property, final boolean useModelExampleValue ) {
292294
final Characteristic characteristic = property.getCharacteristic();
293295
if ( characteristic.is( State.class ) ) {
294296
return characteristic.as( State.class ).getDefaultValue();
@@ -303,6 +305,10 @@ private Object getExampleValueOrElseRandom( final BasicProperty property ) {
303305
}
304306
final Characteristic effectiveCharacteristics = elementCharacteristics.orElse( characteristic );
305307

308+
if ( !useModelExampleValue ) {
309+
return generateExampleValue( effectiveCharacteristics );
310+
}
311+
306312
return property.getExampleValue().map( exampleValue ->
307313
exampleValue.as( ScalarValue.class ).getValue() ).orElseGet( () -> generateExampleValue( effectiveCharacteristics ) );
308314
}
@@ -312,21 +318,37 @@ private Map<String, Object> toMap( final String key, final Object value ) {
312318
}
313319

314320
private List<Object> getCollectionValues( final BasicProperty property, final Collection collection ) {
321+
return getCollectionValues( property, collection, null );
322+
}
323+
324+
private List<Object> getCollectionValues( final BasicProperty property, final Collection collection, final LengthConstraint lengthConstraint ) {
315325
final Type dataType = collection.getDataType().orElseThrow( () -> new IllegalArgumentException( "DataType for collection is required." ) );
326+
327+
if ( dataType.is( Scalar.class ) ) {
328+
final Object payload = getExampleValueOrElseRandom( property, lengthConstraint == null );
329+
return payload instanceof List ? (List) payload : ImmutableList.of( payload );
330+
}
331+
332+
final BigInteger minLength = lengthConstraint == null ? BigInteger.ONE : lengthConstraint.getMinValue().orElse( BigInteger.ONE );
333+
final List<Object> returnValues = new ArrayList<>();
334+
// Fill in minLength elements
335+
for ( int i = 0; i < minLength.intValue(); i++ ) {
336+
returnValues.add( generateCollectionValue( dataType, minLength.intValue() ) );
337+
}
338+
return returnValues;
339+
}
340+
341+
private Object generateCollectionValue( final Type dataType, final int minCount ) {
316342
if ( dataType.is( AbstractEntity.class ) ) {
317343
final AbstractEntity abstractEntity = dataType.as( AbstractEntity.class );
318344
final ComplexType extendingComplexType = abstractEntity.getExtendingElements().get( 0 );
319-
final Map<String, Object> propertyValueMap = transformProperties( extendingComplexType.getAllProperties() );
345+
final Map<String, Object> propertyValueMap = transformProperties( extendingComplexType.getAllProperties(), minCount < 2 );
320346
propertyValueMap.put( "@type", extendingComplexType.getName() );
321-
return ImmutableList.of( propertyValueMap );
347+
return propertyValueMap;
322348
}
323349
if ( dataType.is( Entity.class ) ) {
324350
final Entity entity = dataType.as( Entity.class );
325-
return ImmutableList.of( transformProperties( entity.getAllProperties() ) );
326-
}
327-
if ( dataType.is( Scalar.class ) ) {
328-
final Object payload = getExampleValueOrElseRandom( property );
329-
return payload instanceof List ? (List) payload : ImmutableList.of( payload );
351+
return transformProperties( entity.getAllProperties(), minCount < 2 );
330352
}
331353
throw new IllegalArgumentException( String.format( "DataType %s is unknown", dataType ) );
332354
}
@@ -348,7 +370,7 @@ private Object generateExampleValue( final Characteristic characteristic ) {
348370
* Transforms an {@link BasicProperty} to a map.
349371
* If no transformation can be applied, an empty map will returned.
350372
*/
351-
private interface Transformer extends Function<BasicProperty, Map<String, Object>> {
373+
private interface Transformer extends BiFunction<BasicProperty, Boolean, Map<String, Object>> {
352374
}
353375

354376
private static class BasicProperty {
@@ -495,30 +517,40 @@ private Optional<DateFormat> getDateFormat( final String urn ) {
495517
}
496518

497519
@SuppressWarnings( "unchecked" )
498-
private Object getRandomValue( final LengthConstraint lengthConstraint,
499-
final java.lang.reflect.Type exampleValueType, final Characteristic traitBaseCharacteristic ) {
500-
final BigInteger maxLength = lengthConstraint.getMaxValue().orElse( BigInteger.valueOf( Integer.MAX_VALUE ) );
501-
final BigInteger minLength = lengthConstraint.getMinValue().orElse( BigInteger.ZERO );
502-
520+
private Object getRandomValue( final LengthConstraint lengthConstraint, final java.lang.reflect.Type exampleValueType,
521+
final Characteristic traitBaseCharacteristic ) {
503522
if ( traitBaseCharacteristic.is( Collection.class ) ) {
504-
final List<Object> returnValues = new ArrayList<>();
505-
// Fill in minLength elements
506-
for ( int i = 0; i < minLength.intValue(); i++ ) {
507-
returnValues.add( defaultEasyRandom.nextObject( (Class<?>) exampleValueType ) );
508-
}
509-
// Add between minLength and maxLength-minLength elements, but not more than 5
510-
final int amount = getRandomInteger( minLength.intValue(), Math.min( maxLength.intValue() - minLength.intValue(), 5 ) );
511-
for ( int i = 0; i < amount; i++ ) {
512-
returnValues.add( defaultEasyRandom.nextObject( (Class<?>) exampleValueType ) );
513-
}
514-
return returnValues;
523+
return getRandomValues( lengthConstraint, () -> defaultEasyRandom.nextObject( (Class<?>) exampleValueType ) );
515524
}
516525

526+
final BigInteger maxLength = lengthConstraint.getMaxValue().orElse( BigInteger.valueOf( 30 ) );
527+
final BigInteger minLength = lengthConstraint.getMinValue().orElse( BigInteger.ZERO );
517528
final EasyRandomParameters easyRandomParameters = new EasyRandomParameters().stringLengthRange( minLength.intValue(), maxLength.intValue() );
518529
final EasyRandom easyRandom = new EasyRandom( easyRandomParameters );
519530
return easyRandom.nextObject( (Class<?>) exampleValueType );
520531
}
521532

533+
public List<Object> getRandomValues( final LengthConstraint lengthConstraint, final Supplier<Object> valueGenerator ) {
534+
final BigInteger maxLength =
535+
lengthConstraint == null ? BigInteger.ONE : lengthConstraint.getMaxValue().orElse( BigInteger.valueOf( Integer.MAX_VALUE ) );
536+
final BigInteger minLength = lengthConstraint == null ? BigInteger.ONE : lengthConstraint.getMinValue().orElse( BigInteger.ZERO );
537+
538+
final List<Object> returnValues = new ArrayList<>();
539+
// Fill in minLength elements
540+
for ( int i = 0; i < minLength.intValue(); i++ ) {
541+
returnValues.add( valueGenerator.get() );
542+
}
543+
if ( minLength.intValue() == maxLength.intValue() ) {
544+
return returnValues;
545+
}
546+
// Add between minLength and maxLength-minLength elements, but not more than 5
547+
final int amount = getRandomInteger( minLength.intValue(), Math.min( maxLength.intValue() - minLength.intValue(), 5 ) );
548+
for ( int i = 0; i < amount; i++ ) {
549+
returnValues.add( valueGenerator.get() );
550+
}
551+
return returnValues;
552+
}
553+
522554
private Number getRandomValue( final RangeConstraint rangeConstraint, final java.lang.reflect.Type valueType, final Resource dataTypeResource ) {
523555
final Number min = calculateLowerBound( rangeConstraint, valueType, dataTypeResource );
524556
final Number max = calculateUpperBound( rangeConstraint, valueType, dataTypeResource );

core/esmf-aspect-model-document-generators/src/test/java/org/eclipse/esmf/aspectmodel/generator/json/AspectModelJsonPayloadGeneratorTest.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
package org.eclipse.esmf.aspectmodel.generator.json;
1414

1515
import static org.assertj.core.api.Assertions.assertThat;
16-
import static org.junit.jupiter.api.Assertions.assertTrue;
16+
import static org.junit.jupiter.api.Assertions.*;
1717

1818
import java.io.IOException;
1919
import java.math.BigDecimal;
@@ -24,6 +24,7 @@
2424
import java.util.ArrayList;
2525
import java.util.Arrays;
2626
import java.util.Collection;
27+
import java.util.Iterator;
2728
import java.util.List;
2829
import java.util.Locale;
2930
import java.util.Map;
@@ -51,6 +52,7 @@
5152
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.AspectWithCollectionOfSimpleType;
5253
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.AspectWithCollectionWithAbstractEntity;
5354
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.AspectWithComplexEntityCollectionEnum;
55+
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.AspectWithComplexSet;
5456
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.AspectWithConstrainedSet;
5557
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.AspectWithConstraintProperties;
5658
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.AspectWithConstraints;
@@ -80,6 +82,7 @@
8082
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.AspectWithStructuredValue;
8183
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.Entity;
8284
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.ExtendingTestEntity;
85+
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.Id;
8386
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.NestedEntity;
8487
import org.eclipse.esmf.aspectmodel.generator.json.testclasses.TestEntityWithSimpleTypes;
8588
import org.eclipse.esmf.aspectmodel.jackson.AspectModelJacksonModule;
@@ -604,6 +607,18 @@ void testGenerateJsonForAspectWithConstrainedSetProperty( final KnownVersion met
604607
assertThat( aspectWithConstrainedSet.getTestProperty() ).hasSizeGreaterThan( 0 );
605608
}
606609

610+
@ParameterizedTest
611+
@MethodSource( "allVersions" )
612+
void testGenerateJsonForAspectWithComplexSet( final KnownVersion metaModelVersion ) throws IOException {
613+
final String generatedJson = generateJsonForModel( TestAspect.ASPECT_WITH_COMPLEX_SET, metaModelVersion );
614+
final AspectWithComplexSet aspectWithComplexSet = parseJson( generatedJson, AspectWithComplexSet.class );
615+
assertThat( aspectWithComplexSet.getTestProperty() ).hasSize( 2 );
616+
final Iterator<Id> values = aspectWithComplexSet.getTestProperty().iterator();
617+
final Id id1 = values.next();
618+
final Id id2 = values.next();
619+
assertNotEquals( id1, id2 );
620+
}
621+
607622
private String generateJsonForModel( final TestAspect model, final KnownVersion testedVersion ) {
608623
final VersionedModel versionedModel = TestResources.getModel( model, testedVersion ).get();
609624
final Aspect aspect = AspectModelLoader.getSingleAspectUnchecked( versionedModel );
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.eclipse.esmf.aspectmodel.generator.json.testclasses;
2+
3+
import java.util.Objects;
4+
import java.util.Set;
5+
6+
import javax.validation.constraints.NotNull;
7+
import javax.validation.constraints.Size;
8+
9+
import com.fasterxml.jackson.annotation.JsonCreator;
10+
import com.fasterxml.jackson.annotation.JsonProperty;
11+
12+
/**
13+
* Generated class for Test Aspect. This is a test description
14+
*/
15+
public class AspectWithComplexSet {
16+
17+
@NotNull
18+
@Size( min = 2 )
19+
20+
private Set<Id> testProperty;
21+
22+
@JsonCreator
23+
public AspectWithComplexSet( @JsonProperty( value = "testProperty" ) final Set<Id> testProperty ) {
24+
super(
25+
26+
);
27+
this.testProperty = testProperty;
28+
}
29+
30+
/**
31+
* Returns Test Property
32+
*
33+
* @return {@link #testProperty}
34+
*/
35+
public Set<Id> getTestProperty() {
36+
return testProperty;
37+
}
38+
39+
@Override
40+
public boolean equals( final Object o ) {
41+
if ( this == o ) {
42+
return true;
43+
}
44+
if ( o == null || getClass() != o.getClass() ) {
45+
return false;
46+
}
47+
48+
final AspectWithComplexSet that = (AspectWithComplexSet) o;
49+
return Objects.equals( testProperty, that.testProperty );
50+
}
51+
52+
@Override
53+
public int hashCode() {
54+
return Objects.hash( testProperty );
55+
}
56+
}

0 commit comments

Comments
 (0)