Skip to content
This repository has been archived by the owner on Apr 14, 2023. It is now read-only.

Commit

Permalink
Merge pull request #1398 from finos/1390-create-in-map-constraint
Browse files Browse the repository at this point in the history
 chore(#1390): added inMap constraint to the generation engine
  • Loading branch information
hashbyhayter authored Oct 2, 2019
2 parents 00e90a7 + 1993fe6 commit cd7a652
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2019 Scott Logic Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.scottlogic.deg.common.profile.constraints.atomic;

import com.scottlogic.deg.common.profile.Field;
import com.scottlogic.deg.generator.fieldspecs.whitelist.DistributedList;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class IsInMapConstraint implements AtomicConstraint {
public final Field field;
public final DistributedList<Object> legalValues;

public IsInMapConstraint(Field field, DistributedList<Object> legalValues) {
this.field = field;
this.legalValues = legalValues;

if (legalValues.distributedList().isEmpty()) {
throw new IllegalArgumentException("Cannot create an IsInMapConstraint for field '" +
field.name + "' with an empty set.");
}

if (legalValues.list().contains(null)) {
throw new IllegalArgumentException("Cannot create an IsInMapConstraint for field '" +
field.name + "' with a list containing null.");
}
}

@Override
public Field getField() {
return field;
}

public String toString(){
boolean overLimit = legalValues.list().size() > 3;
return String.format("%s in [%s%s](%d values)",
field.name,
legalValues.stream().limit(3).map(Object::toString).collect(Collectors.joining(", ")),
overLimit ? ", ..." : "",
legalValues.list().size());
}

@Override
public boolean equals(Object o){
if (this == o) return true;
if (o instanceof ViolatedAtomicConstraint) {
return o.equals(this);
}
if (o == null || getClass() != o.getClass()) return false;
IsInMapConstraint constraint = (IsInMapConstraint) o;
return Objects.equals(field, constraint.field) && Objects.equals(legalValues, constraint.legalValues);
}

@Override
public int hashCode(){
return Objects.hash(field, legalValues);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2019 Scott Logic Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.scottlogic.deg.common.profile.constraints.atomic;

import com.scottlogic.deg.common.profile.Field;
import com.scottlogic.deg.generator.fieldspecs.whitelist.DistributedList;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import static com.scottlogic.deg.common.profile.FieldBuilder.createField;

public class IsInMapConstraintTests {

@Test
public void testConstraintThrowsIfGivenEmptySet(){
Field field1 = createField("TestField");

Assertions.assertThrows(
IllegalArgumentException.class,
() -> new IsInMapConstraint(field1, DistributedList.empty()));
}

@Test
public void testConstraintThrowsIfGivenNullInASet(){
Field field1 = createField("TestField");

Assertions.assertThrows(
IllegalArgumentException.class,
() -> new IsInMapConstraint(field1, DistributedList.singleton(null)));
}

@Test
public void testConstraintThrowsNothingIfGivenAValidSet(){
Field field1 = createField("TestField");
Assertions.assertDoesNotThrow(
() -> new IsInMapConstraint(field1, DistributedList.singleton("foo")));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ private FieldSpec construct(AtomicConstraint constraint, boolean negate) {
return construct(((NotConstraint) constraint).negatedConstraint, !negate);
} else if (constraint instanceof IsInSetConstraint) {
return construct((IsInSetConstraint) constraint, negate);
} else if (constraint instanceof IsInMapConstraint) {
return construct((IsInMapConstraint) constraint, negate);
} else if (constraint instanceof EqualToConstraint) {
return construct((EqualToConstraint) constraint, negate);
} else if (constraint instanceof IsGreaterThanConstantConstraint) {
Expand Down Expand Up @@ -101,6 +103,14 @@ private FieldSpec construct(IsInSetConstraint constraint, boolean negate) {
return FieldSpec.fromList(constraint.legalValues);
}

private FieldSpec construct(IsInMapConstraint constraint, boolean negate) {
if (negate) {
throw new UnsupportedOperationException("negation of inMap not supported");
}

return FieldSpec.fromList(constraint.legalValues);
}

private FieldSpec construct(EqualToConstraint constraint, boolean negate) {
if (negate) {
return FieldSpec.empty().withBlacklist(Collections.singleton(constraint.value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public static Constraint create(AtomicConstraintType type, Field field, Object v
return new EqualToConstraint(field, value);
case IS_IN_SET:
return new IsInSetConstraint(field, (DistributedList<Object>)value);
case IS_IN_MAP:
return new IsInMapConstraint(field, (DistributedList<Object>)value);
case IS_NULL:
return new IsNullConstraint(field);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private Object tryGetValue(ConstraintDTO dto, Types type){
}

if (dto.file != null && dto.is.equals(AtomicConstraintType.IS_IN_MAP.getText())){
throw new UnsupportedOperationException("inMap is unsupported");
return fromFileReader.listFromMapFile(dto.file, dto.key);
}

return getValue(dto.value, type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ public DistributedList<Object> setFromFile(String file) {
.collect(Collectors.toList()));
}

public DistributedList<Object> listFromMapFile(String file, String key) {
InputStream streamFromPath = createStreamFromPath(appendPath(file));

DistributedList<String> names = CsvInputStreamReader.retrieveLines(streamFromPath, key);
closeStream(streamFromPath);

return new DistributedList<>(
names.distributedList().stream()
.map(holder -> new WeightedElement<>((Object) holder.element(), holder.weight()))
.collect(Collectors.toList()));
}

private String appendPath(String path) {
return fromFilePath + path;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.scottlogic.deg.profile.reader.file;

import com.scottlogic.deg.common.ValidationException;
import com.scottlogic.deg.generator.fieldspecs.whitelist.DistributedList;
import com.scottlogic.deg.generator.fieldspecs.whitelist.WeightedElement;
import org.apache.commons.csv.CSVFormat;
Expand All @@ -26,7 +27,7 @@
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

public final class CsvInputStreamReader {
Expand All @@ -38,16 +39,44 @@ private CsvInputStreamReader() {
public static DistributedList<String> retrieveLines(InputStream stream) {
List<CSVRecord> records = parse(stream);
return new DistributedList<>(records.stream()
.map(CsvInputStreamReader::createWeightedElement)
.map(CsvInputStreamReader::createWeightedElementFromRecord)
.collect(Collectors.toList()));
}

private static WeightedElement<String> createWeightedElement(CSVRecord record) {
if (record.size() > 1) {
return new WeightedElement<>(record.get(0), Double.parseDouble(record.get(1)));
} else {
return WeightedElement.withDefaultWeight(record.get(0));
public static DistributedList<String> retrieveLines(InputStream stream, String key) {
List<CSVRecord> records = parse(stream);

int index = getIndexForKey(records.get(0), key);

//Remove the header
records.remove(0);

return new DistributedList<>(records.stream()
.map(record -> record.get(index))
.map(record -> createWeightedElement(record, Optional.empty()))
.collect(Collectors.toList()));
}

private static int getIndexForKey(CSVRecord header, String key) {
int index = 0;
for (String title : header) {
if (title.equals(key)) {
return index;
}
index++;
}
throw new ValidationException("unable to find data for key " + key);
}

private static WeightedElement<String> createWeightedElementFromRecord(CSVRecord record) {
return createWeightedElement(record.get(0),
record.size() == 1 ? Optional.empty() : Optional.of(Double.parseDouble(record.get(1))));
}


private static WeightedElement<String> createWeightedElement(String element, Optional<Double> weight) {
return weight.map(integer -> new WeightedElement<>(element, integer))
.orElseGet(() -> WeightedElement.withDefaultWeight(element));
}

private static List<CSVRecord> parse(InputStream stream) {
Expand Down

0 comments on commit cd7a652

Please sign in to comment.