diff --git a/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/OptionalControlMapSubResourceParser.java b/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/OptionalControlMapSubResourceParser.java index a0dd7224f..d042008c7 100644 --- a/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/OptionalControlMapSubResourceParser.java +++ b/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/OptionalControlMapSubResourceParser.java @@ -13,6 +13,7 @@ public interface OptionalControlMapSubResourceParser extends ControlMapSubResourceParser, OptionalResourceControlMapModifier { + @Override default void defaultModify(Map control) { control.put(getControlKey(), defaultResult()); } diff --git a/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveAgeMaximumParser.java b/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveAgeMaximumParser.java index e7cee0f42..e816605c9 100644 --- a/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveAgeMaximumParser.java +++ b/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveAgeMaximumParser.java @@ -43,18 +43,20 @@ public boolean isStopLine(String line) { }.value(3, SC_KEY, PARSE_SC).value(7, COASTAL_KEY, PARSE_AGE).value(7, INTERIOR_KEY, PARSE_AGE) .value(7, T1_KEY, PARSE_AGE).value(7, T2_KEY, PARSE_AGE); - static ControlledValueParser PARSE_SC = ControlledValueParser.validate(ValueParser.INTEGER, (v, c) -> { - if (v < -1 || v > MAX_SC) { - return Optional.of("Site curve number must be in the range -1 to " + MAX_SC + " inclusive"); - } - return Optional.empty(); - }); - - static ValueParser PARSE_AGE = s -> { + static final ControlledValueParser PARSE_SC = ControlledValueParser + .validate(ValueParser.INTEGER, (v, c) -> { + if (v < -1 || v > MAX_SC) { + return Optional.of("Site curve number must be in the range -1 to " + MAX_SC + " inclusive"); + } + return Optional.empty(); + }); + + static final ValueParser PARSE_AGE = s -> { var value = ValueParser.FLOAT.parse(s); return value <= 0.0 ? MAX_AGE : value; }; + @Override @SuppressWarnings("serial") public Map defaultResult() { return new HashMap<>() { diff --git a/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParser.java b/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParser.java index bb0b40fb1..10b18b9a8 100644 --- a/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParser.java +++ b/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParser.java @@ -300,17 +300,16 @@ static ValueParser uncontrolled(ControlledValueParser delegate) { * return empty if the string is not a marker. * @return a ValueOrMarker */ - public static ValueParser> - valueOrMarker(ValueParser valueParser, ValueParser> markerParser) { + public static ValueParser> + valueOrMarker(ValueParser valueParser, ValueParser> markerParser) { return s -> { - var builder = new ValueOrMarker.Builder(); + var builder = new ValueOrMarker.Builder(); var marker = markerParser.parse(s).map(builder::marker); if (marker.isPresent()) { return marker.get(); } - var value = builder.value(valueParser.parse(s)); - return value; + return builder.value(valueParser.parse(s)); }; } @@ -331,11 +330,9 @@ public static ValueParser> optionalSingleton(Predicate t */ public static ValueParser> LAYER = s -> { switch (s.toUpperCase()) { - case "1": - case "P": + case "1", "P": return Optional.of(Layer.PRIMARY); - case "2": - case "S": + case "2", "S": return Optional.of(Layer.SECONDARY); case "V": return Optional.of(Layer.VETERAN); diff --git a/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/math/FloatMath.java b/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/math/FloatMath.java index 33ef0fdc1..7df223894 100644 --- a/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/math/FloatMath.java +++ b/vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/math/FloatMath.java @@ -39,6 +39,7 @@ public static float ceil(float f) { return (float) Math.ceil(f); } + @SuppressWarnings("squid:S4274") public static float clamp(float x, float min, float max) { assert max >= min : "Maximum " + max + " was less than minimum " + min; if (x < min) diff --git a/vdyp-core/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParserTest.java b/vdyp-core/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParserTest.java index 80a56f70f..4a57c23a4 100644 --- a/vdyp-core/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParserTest.java +++ b/vdyp-core/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParserTest.java @@ -1,16 +1,26 @@ package ca.bc.gov.nrs.vdyp.io.parse; +import static ca.bc.gov.nrs.vdyp.common.Utils.constMap; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.isMarker; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.isValue; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.notPresent; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; -public class ValueParserTest { +import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.model.Layer; +import ca.bc.gov.nrs.vdyp.test.VdypMatchers; + +class ValueParserTest { @Test - public void rangeParserTest() throws Exception { + void testRangeParser() throws Exception { var exclusiveParser = ValueParser.range(ValueParser.INTEGER, 10, false, 20, false, "Test"); var result = exclusiveParser.parse("11"); @@ -53,4 +63,127 @@ public void rangeParserTest() throws Exception { } + @Test + void testEnumParser() throws Exception { + var unit = ValueParser.enumParser(Layer.class); + + assertThat(unit.parse("PRIMARY"), is(Layer.PRIMARY)); + + var ex = assertThrows(ValueParseException.class, () -> unit.parse("NOT_A_LAYER")); + assertThat(ex, hasProperty("message", is("\"NOT_A_LAYER\" is not a valid Layer"))); + } + + @Test + void testValidateRangeInclusive() throws Exception { + var unit = ValueParser.validateRangeInclusive(10f, 20f, "Test"); + + assertThat(unit.apply(10f), notPresent()); + assertThat(unit.apply(20f), notPresent()); + assertThat(unit.apply(11f), notPresent()); + assertThat(unit.apply(19f), notPresent()); + assertThat(unit.apply(9f), present(is("Test is expected to be between 10.0 and 20.0 but was 9.0"))); + assertThat(unit.apply(21f), present(is("Test is expected to be between 10.0 and 20.0 but was 21.0"))); + + } + + @Test + void testValidateRangeInclusiveBadRange() throws Exception { + assertThrows(IllegalArgumentException.class, () -> ValueParser.validateRangeInclusive(20f, 10f, "Test")); + } + + @Test + void testPretestOptional() throws Exception { + var unit = ValueParser.pretestOptional(ValueParser.INTEGER, x -> !x.equals("IGNORE")); + + assertThat(unit.parse("1"), present(is(1))); + assertThat(unit.parse("2"), present(is(2))); + assertThat(unit.parse("IGNORE"), notPresent()); + assertThrows(ValueParseException.class, () -> unit.parse("DONT_IGNORE")); + } + + @Test + void testPosttestOptional() throws Exception { + var unit = ValueParser.posttestOptional(ValueParser.INTEGER, x -> !x.equals(1)); + + assertThat(unit.parse("0"), present(is(0))); + assertThat(unit.parse("1"), notPresent()); + assertThat(unit.parse("2"), present(is(2))); + assertThrows(ValueParseException.class, () -> unit.parse("DONT_IGNORE")); + } + + @Test + void testValueOrMarker() throws Exception { + var unit = ValueParser.valueOrMarker( + ValueParser.INTEGER, ValueParser.pretestOptional(ValueParser.STRING, x -> x.equals("MARKER")) + ); + + assertThat(unit.parse("0"), isValue(is(0))); + assertThat(unit.parse("1"), isValue(is(1))); + assertThat(unit.parse("MARKER"), isMarker(is("MARKER"))); + assertThrows(ValueParseException.class, () -> unit.parse("INVALID")); + } + + @Test + void testOptionalSingleton() throws Exception { + var unit = ValueParser.optionalSingleton(x -> x.equals("PASS"), "X"); + + assertThat(unit.parse("FAIL"), notPresent()); + assertThat(unit.parse("PASS"), present(is("X"))); + } + + @Test + void testLayer() throws Exception { + var unit = ValueParser.LAYER; + + assertThat(unit.parse("1"), present(is(Layer.PRIMARY))); + assertThat(unit.parse("P"), present(is(Layer.PRIMARY))); + + assertThat(unit.parse("2"), present(is(Layer.SECONDARY))); + assertThat(unit.parse("S"), present(is(Layer.SECONDARY))); + + assertThat(unit.parse("V"), present(is(Layer.VETERAN))); + + assertThat(unit.parse(""), notPresent()); + assertThat(unit.parse(" "), notPresent()); + assertThat(unit.parse("X"), notPresent()); + } + + @Test + void testToMap() throws Exception { + var map = Utils.constMap(x -> { + x.put("K3", 42); + x.put("K4", 23); + }); + var unit = ValueParser + .toMap(ValueParser.list(ValueParser.INTEGER), map, "K1", "K2", "K3", "K4"); + + assertThat(unit.parse(" 2 3"), equalTo(constMap(x -> { + x.put("K1", 2); + x.put("K2", 3); + x.put("K3", 42); + x.put("K4", 23); + }))); + assertThat(unit.parse(" 2 3 4"), equalTo(constMap(x -> { + x.put("K1", 2); + x.put("K2", 3); + x.put("K3", 4); + x.put("K4", 23); + }))); + assertThat(unit.parse(" 2 3 4 5"), equalTo(constMap(x -> { + x.put("K1", 2); + x.put("K2", 3); + x.put("K3", 4); + x.put("K4", 5); + }))); + + assertThrows(ValueParseException.class, () -> unit.parse(" 2 ")); + + assertThrows( + IllegalArgumentException.class, + () -> ValueParser + .toMap(ValueParser.list(ValueParser.INTEGER), map, "K1", "K3", "K2", "K4") + ); + + } + }