From 7ec8faf3ab91ab2214bb61e3c58688c6d0be12db Mon Sep 17 00:00:00 2001 From: John Sullivan Date: Wed, 25 Aug 2021 15:24:04 -0400 Subject: [PATCH 1/5] added test for roundtripping objects that are both `Configurable` and `Provenancable` through provenance and back to check whether the produce the same object. Wrote a `structuralEquals` method on `ConfigurationData` to support it. --- .../test/java/org/tribuo/test/Helpers.java | 23 +++++++++- .../response/BinaryResponseProcessor.java | 2 + .../response/FieldResponseProcessor.java | 1 + .../ResponseProcessorRoundtripTest.java | 43 +++++++++++++++++++ pom.xml | 2 +- 5 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 Data/src/test/java/org/tribuo/data/columnar/processors/response/ResponseProcessorRoundtripTest.java diff --git a/Core/src/test/java/org/tribuo/test/Helpers.java b/Core/src/test/java/org/tribuo/test/Helpers.java index cd3f3cd50..ac739a65c 100644 --- a/Core/src/test/java/org/tribuo/test/Helpers.java +++ b/Core/src/test/java/org/tribuo/test/Helpers.java @@ -16,7 +16,11 @@ package org.tribuo.test; +import com.oracle.labs.mlrg.olcut.config.Configurable; +import com.oracle.labs.mlrg.olcut.config.ConfigurationData; +import com.oracle.labs.mlrg.olcut.config.ConfigurationManager; import com.oracle.labs.mlrg.olcut.provenance.ObjectProvenance; +import com.oracle.labs.mlrg.olcut.provenance.Provenancable; import com.oracle.labs.mlrg.olcut.provenance.ProvenanceUtil; import com.oracle.labs.mlrg.olcut.provenance.io.ObjectMarshalledProvenance; import org.junit.jupiter.api.Assertions; @@ -27,7 +31,6 @@ import org.tribuo.MutableFeatureMap; import org.tribuo.Output; import org.tribuo.impl.ListExample; -import org.tribuo.provenance.ModelProvenance; import org.tribuo.sequence.SequenceModel; import java.io.BufferedInputStream; @@ -40,7 +43,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.logging.Logger; +import java.util.stream.Collectors; /** * Test helpers @@ -76,6 +81,22 @@ public static Example mkExample(MockOutput label, String... features return ex; } + + public static

> void testConfigurableRoundtrip(C itm) { + ConfigurationManager cm = new ConfigurationManager(); + String name = cm.importConfigurable(itm, "item"); + List configData = cm.getComponentNames().stream() + .map(cm::getConfigurationData) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + + List provenData = ProvenanceUtil.extractConfiguration(itm.getProvenance()); + + Assertions.assertTrue(ConfigurationData.structuralEquals(configData, provenData, name, provenData.get(0).getName())); + } + + public static void testProvenanceMarshalling(ObjectProvenance inputProvenance) { List provenanceList = ProvenanceUtil.marshalProvenance(inputProvenance); ObjectProvenance unmarshalledProvenance = ProvenanceUtil.unmarshalProvenance(provenanceList); diff --git a/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java b/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java index 62cff6e06..4d56b99bb 100644 --- a/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java +++ b/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java @@ -86,8 +86,10 @@ public void postConfig() { throw new PropertyException(configName, "positiveResponses", "if fieldName is populated, positiveResponses must be blank"); } fieldNames = Collections.singletonList(fieldName); + fieldName = null; if(positiveResponse != null) { positiveResponses = Collections.singletonList(positiveResponse); + positiveResponse = null; } else { throw new PropertyException(configName, "positiveResponse", "if fieldName is populated positiveResponse must be populated"); } diff --git a/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java b/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java index 6d3e153aa..f7d9dcfe0 100644 --- a/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java +++ b/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java @@ -78,6 +78,7 @@ public void postConfig() { throw new PropertyException(configName, "defaultValues", "if fieldName is populated, defaultValues must be blank"); } fieldNames = Collections.singletonList(fieldName); + fieldName = null; if (defaultValue != null) { defaultValues = Collections.singletonList(defaultValue); } else { diff --git a/Data/src/test/java/org/tribuo/data/columnar/processors/response/ResponseProcessorRoundtripTest.java b/Data/src/test/java/org/tribuo/data/columnar/processors/response/ResponseProcessorRoundtripTest.java new file mode 100644 index 000000000..e5f68e430 --- /dev/null +++ b/Data/src/test/java/org/tribuo/data/columnar/processors/response/ResponseProcessorRoundtripTest.java @@ -0,0 +1,43 @@ +package org.tribuo.data.columnar.processors.response; + +import org.junit.jupiter.api.Test; +import org.tribuo.test.Helpers; +import org.tribuo.test.MockMultiOutput; +import org.tribuo.test.MockMultiOutputFactory; +import org.tribuo.test.MockOutput; +import org.tribuo.test.MockOutputFactory; + +import java.util.Arrays; + +public class ResponseProcessorRoundtripTest { + + @Test + public void binaryTest() throws NoSuchFieldException, IllegalAccessException { + BinaryResponseProcessor multiRespProc = new BinaryResponseProcessor<>( + Arrays.asList("R1", "R2"), + Arrays.asList("TRUE", "TRUE"), + new MockMultiOutputFactory(), + "true", "false", true); + + Helpers.testConfigurableRoundtrip(multiRespProc); + + BinaryResponseProcessor singleRespProc = new BinaryResponseProcessor<>("R1", "TRUE", new MockOutputFactory()); + + Helpers.testConfigurableRoundtrip(singleRespProc); + } + + @Test + public void fieldTest() { + FieldResponseProcessor multiRespProc = new FieldResponseProcessor<>( + Arrays.asList("R1", "R2"), + Arrays.asList("A", "B"), + new MockMultiOutputFactory(), + true, false); + + Helpers.testConfigurableRoundtrip(multiRespProc); + + FieldResponseProcessor singleRespProc = new FieldResponseProcessor<>("R1", "A", new MockOutputFactory()); + + Helpers.testConfigurableRoundtrip(singleRespProc); + } +} diff --git a/pom.xml b/pom.xml index 298939b73..4fe78ccda 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ UTF-8 - 5.1.6 + 5.1.7-SNAPSHOT 2.43 From b18faef7f0f3786e8210aeddbe6162a35b0a48de Mon Sep 17 00:00:00 2001 From: John Sullivan Date: Thu, 2 Sep 2021 10:50:56 -0400 Subject: [PATCH 2/5] added nulling out for defaultValue in `FieldResponseProcessor` and changed the logic in `FieldResponseProcessor.postConfig` to properly handle all cases. --- .../response/FieldResponseProcessor.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java b/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java index f7d9dcfe0..d25ff937b 100644 --- a/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java +++ b/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java @@ -64,15 +64,17 @@ public class FieldResponseProcessor> implements ResponseProc public void postConfig() { if (fieldName != null && fieldNames != null) { throw new PropertyException(configName, "fieldName, FieldNames", "only one of fieldName or fieldNames can be populated"); - } else if (fieldNames != null) { - if(defaultValue != null) { - defaultValues = defaultValues == null ? Collections.nCopies(fieldNames.size(), defaultValue) : defaultValues; + } else if (fieldNames != null) { // multiple fields + if (defaultValues != null && defaultValues.size() == fieldNames.size()) { // check first for multiple defaults + // this is the default case, nothing needs to be done + } else if (defaultValue != null) { // next fill in a single default + defaultValues = Collections.nCopies(fieldNames.size(), defaultValue); + defaultValue = null; + } else if (defaultValues != null) { // size mismatch between defaultValues and fieldNames + throw new PropertyException(configName, "defaultValues", "must either be empty or match the length of fieldNames"); } else { throw new PropertyException(configName, "defaultValue, defaultValues", "one of defaultValue or defaultValues must be populated"); } - if(defaultValues.size() != fieldNames.size()) { - throw new PropertyException(configName, "defaultValues", "must either be empty or match the length of fieldNames"); - } } else if (fieldName != null) { if(defaultValues != null) { throw new PropertyException(configName, "defaultValues", "if fieldName is populated, defaultValues must be blank"); From f70044686a76793eed2720068f2f46bd8c1fdc75 Mon Sep 17 00:00:00 2001 From: John Sullivan Date: Fri, 24 Sep 2021 16:15:40 -0400 Subject: [PATCH 3/5] Updated to use OLCUT v5.2.0 --- Core/src/test/java/org/tribuo/test/Helpers.java | 5 +++++ .../processors/response/ResponseProcessorRoundtripTest.java | 2 +- pom.xml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Core/src/test/java/org/tribuo/test/Helpers.java b/Core/src/test/java/org/tribuo/test/Helpers.java index ac739a65c..7c23f0586 100644 --- a/Core/src/test/java/org/tribuo/test/Helpers.java +++ b/Core/src/test/java/org/tribuo/test/Helpers.java @@ -82,6 +82,11 @@ public static Example mkExample(MockOutput label, String... features } + /** + * Takes an object that is both {@link Provenancable} and {@link Configurable} and tests whether the configuration + * and provenance representations are the same using {@link ConfigurationData#structuralEquals(List, List, String, String)}. + * @param itm The object whose equality is to be tested + */ public static

> void testConfigurableRoundtrip(C itm) { ConfigurationManager cm = new ConfigurationManager(); String name = cm.importConfigurable(itm, "item"); diff --git a/Data/src/test/java/org/tribuo/data/columnar/processors/response/ResponseProcessorRoundtripTest.java b/Data/src/test/java/org/tribuo/data/columnar/processors/response/ResponseProcessorRoundtripTest.java index e5f68e430..7facf855e 100644 --- a/Data/src/test/java/org/tribuo/data/columnar/processors/response/ResponseProcessorRoundtripTest.java +++ b/Data/src/test/java/org/tribuo/data/columnar/processors/response/ResponseProcessorRoundtripTest.java @@ -12,7 +12,7 @@ public class ResponseProcessorRoundtripTest { @Test - public void binaryTest() throws NoSuchFieldException, IllegalAccessException { + public void binaryTest() { BinaryResponseProcessor multiRespProc = new BinaryResponseProcessor<>( Arrays.asList("R1", "R2"), Arrays.asList("TRUE", "TRUE"), diff --git a/pom.xml b/pom.xml index 4fe78ccda..702004d45 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ UTF-8 - 5.1.7-SNAPSHOT + 5.2.0 2.43 From 7fcd42bfee3a817dd21b639a38c88d21481a4ae2 Mon Sep 17 00:00:00 2001 From: John Sullivan Date: Thu, 14 Oct 2021 13:02:50 -0400 Subject: [PATCH 4/5] Rewrote the `postConfig` logic on `FieldResponseProcessor` and `BinaryResponseProcessor` to be more readable. --- .../test/java/org/tribuo/test/Helpers.java | 4 +- .../response/BinaryResponseProcessor.java | 56 ++++++++++-------- .../response/FieldResponseProcessor.java | 58 ++++++++++--------- 3 files changed, 66 insertions(+), 52 deletions(-) diff --git a/Core/src/test/java/org/tribuo/test/Helpers.java b/Core/src/test/java/org/tribuo/test/Helpers.java index 7c23f0586..648630b0a 100644 --- a/Core/src/test/java/org/tribuo/test/Helpers.java +++ b/Core/src/test/java/org/tribuo/test/Helpers.java @@ -19,6 +19,7 @@ import com.oracle.labs.mlrg.olcut.config.Configurable; import com.oracle.labs.mlrg.olcut.config.ConfigurationData; import com.oracle.labs.mlrg.olcut.config.ConfigurationManager; +import com.oracle.labs.mlrg.olcut.provenance.ConfiguredObjectProvenance; import com.oracle.labs.mlrg.olcut.provenance.ObjectProvenance; import com.oracle.labs.mlrg.olcut.provenance.Provenancable; import com.oracle.labs.mlrg.olcut.provenance.ProvenanceUtil; @@ -81,13 +82,12 @@ public static Example mkExample(MockOutput label, String... features return ex; } - /** * Takes an object that is both {@link Provenancable} and {@link Configurable} and tests whether the configuration * and provenance representations are the same using {@link ConfigurationData#structuralEquals(List, List, String, String)}. * @param itm The object whose equality is to be tested */ - public static

> void testConfigurableRoundtrip(C itm) { + public static

> void testConfigurableRoundtrip(C itm) { ConfigurationManager cm = new ConfigurationManager(); String name = cm.importConfigurable(itm, "item"); List configData = cm.getComponentNames().stream() diff --git a/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java b/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java index 83972567a..c6cc95820 100644 --- a/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java +++ b/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java @@ -75,34 +75,42 @@ public class BinaryResponseProcessor> implements ResponsePro @ConfigurableName private String configName; + /** + * We support specifying field names and default values both singly through {@link #fieldName} and {@link #positiveResponse} + * and in a list through {@link #fieldNames} and {@link #positiveResponses}. Canonically all internal logic is driven by + * fieldNames and positiveResponses, so this method takes values populated in fieldName and positiveResponse and sets them + * appropriately. + */ @Override public void postConfig() { - if (fieldName != null && fieldNames != null) { // we can only have one path - throw new PropertyException(configName, "fieldName, FieldNames", "only one of fieldName or fieldNames can be populated"); - } else if (fieldNames != null) { - if(positiveResponse != null) { - positiveResponses = positiveResponses == null ? Collections.nCopies(fieldNames.size(), positiveResponse) : positiveResponses; - if(positiveResponses.size() != fieldNames.size()) { - throw new PropertyException(configName, "positiveResponses", "must either be empty or match the length of fieldNames"); - } - } else { - throw new PropertyException(configName, "positiveResponse, positiveResponses", "one of positiveResponse or positiveResponses must be populated"); - } - } else if (fieldName != null) { - if(positiveResponses != null) { - throw new PropertyException(configName, "positiveResponses", "if fieldName is populated, positiveResponses must be blank"); - } + + boolean bothFieldNamesPopulated = fieldName != null && fieldNames != null; + boolean neitherFieldNamesPopulated = fieldName == null && fieldNames == null; + boolean multipleFieldNamesPopulated = fieldNames != null; + boolean singleFieldNamePopulated = fieldName != null; + + boolean bothPositiveResponsesPopulated = positiveResponses != null && positiveResponse != null; + boolean neitherPositiveResponsesPopulated = positiveResponse == null && positiveResponses == null; + boolean multiplePositiveResponsesPopulated = positiveResponses != null; + boolean singlePositiveResponsePopulated = positiveResponse != null; + + if (bothFieldNamesPopulated || neitherFieldNamesPopulated) { + throw new PropertyException(configName, "fieldName, FieldNames", "exactly one of fieldName or fieldNames must be populated"); + } else if (bothPositiveResponsesPopulated || neitherPositiveResponsesPopulated) { + throw new PropertyException(configName, "positiveResponse, positiveResponses", "exactly one of positiveResponse or positiveResponses must be populated"); + } else if(multipleFieldNamesPopulated && multiplePositiveResponsesPopulated && fieldNames.size() != positiveResponses.size()) { //sizes don't match + throw new PropertyException(configName, "positiveResponses", "must match the length of fieldNames"); + } else if(multipleFieldNamesPopulated && singlePositiveResponsePopulated) { + positiveResponses = Collections.nCopies(fieldNames.size(), positiveResponse); + positiveResponse = null; + } else if(singleFieldNamePopulated && multiplePositiveResponsesPopulated) { + throw new PropertyException(configName, "positiveResponses", "if fieldName is populated, positiveResponses must be blank"); + } else if(singleFieldNamePopulated && singlePositiveResponsePopulated) { fieldNames = Collections.singletonList(fieldName); fieldName = null; - if(positiveResponse != null) { - positiveResponses = Collections.singletonList(positiveResponse); - positiveResponse = null; - } else { - throw new PropertyException(configName, "positiveResponse", "if fieldName is populated positiveResponse must be populated"); - } - } else { - throw new PropertyException(configName, "fieldName, fieldNames", "One of fieldName or fieldNames must be populated"); - } + positiveResponses = Collections.singletonList(positiveResponse); + positiveResponse = null; + } // the case where both positiveResponses and fieldNames are populated and their sizes match requires no action } /** diff --git a/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java b/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java index d25ff937b..577342b77 100644 --- a/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java +++ b/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java @@ -31,7 +31,7 @@ import java.util.Optional; /** - * A response processor that returns the value in a given field. + * A response processor that returns the value(s) in a given (set of) fields. */ public class FieldResponseProcessor> implements ResponseProcessor { @@ -60,35 +60,41 @@ public class FieldResponseProcessor> implements ResponseProc @ConfigurableName private String configName; + /** + * We support specifying field names and default values both singly through {@link #fieldName} and {@link #defaultValue} + * and in a list through {@link #fieldNames} and {@link #defaultValues}. Canonically all internal logic is driven by + * fieldNames and defaultValues, so this method takes values populated in fieldName and defaultValue and sets them + * appropriately. + */ @Override public void postConfig() { - if (fieldName != null && fieldNames != null) { - throw new PropertyException(configName, "fieldName, FieldNames", "only one of fieldName or fieldNames can be populated"); - } else if (fieldNames != null) { // multiple fields - if (defaultValues != null && defaultValues.size() == fieldNames.size()) { // check first for multiple defaults - // this is the default case, nothing needs to be done - } else if (defaultValue != null) { // next fill in a single default - defaultValues = Collections.nCopies(fieldNames.size(), defaultValue); - defaultValue = null; - } else if (defaultValues != null) { // size mismatch between defaultValues and fieldNames - throw new PropertyException(configName, "defaultValues", "must either be empty or match the length of fieldNames"); - } else { - throw new PropertyException(configName, "defaultValue, defaultValues", "one of defaultValue or defaultValues must be populated"); - } - } else if (fieldName != null) { - if(defaultValues != null) { - throw new PropertyException(configName, "defaultValues", "if fieldName is populated, defaultValues must be blank"); - } + boolean bothFieldNamesPopulated = fieldName != null && fieldNames != null; + boolean neitherFieldNamesPopulated = fieldName == null && fieldNames == null; + boolean multipleFieldNamesPopulated = fieldNames != null; + boolean singleFieldNamePopulated = fieldName != null; + + boolean bothDefaultValuesPopulated = defaultValues != null && defaultValue != null; + boolean neitherDefaultValuesPopulated = defaultValue == null && defaultValues == null; + boolean multipleDefaultValuesPopulated = defaultValues != null; + boolean singleDefaultValuePopulated = defaultValue != null; + + if (bothFieldNamesPopulated || neitherFieldNamesPopulated) { + throw new PropertyException(configName, "fieldName, FieldNames", "exactly one of fieldName or fieldNames must be populated"); + } else if (bothDefaultValuesPopulated || neitherDefaultValuesPopulated) { + throw new PropertyException(configName, "defaultValue, defaultValues", "exactly one of defaultValue or defaultValues must be populated"); + } else if(multipleFieldNamesPopulated && multipleDefaultValuesPopulated && fieldNames.size() != defaultValues.size()) { //sizes don't match + throw new PropertyException(configName, "defaultValues", "must match the length of fieldNames"); + } else if(multipleFieldNamesPopulated && singleDefaultValuePopulated) { + defaultValues = Collections.nCopies(fieldNames.size(), defaultValue); + defaultValue = null; + } else if(singleFieldNamePopulated && multipleDefaultValuesPopulated) { + throw new PropertyException(configName, "defaultValues", "if fieldName is populated, defaultValues must be blank"); + } else if(singleFieldNamePopulated && singleDefaultValuePopulated) { fieldNames = Collections.singletonList(fieldName); fieldName = null; - if (defaultValue != null) { - defaultValues = Collections.singletonList(defaultValue); - } else { - throw new PropertyException(configName, "defaultValue", "if fieldName is populated, defaultValue must be populated"); - } - } else { - throw new PropertyException(configName, "fieldName, fieldNames", "One of fieldName or fieldNames must be populated"); - } + defaultValues = Collections.singletonList(defaultValue); + defaultValue = null; + } // the case where both defaultValues and fieldNames are populated and their sizes match requires no action } /** From 51e13f1f873eb334a1f50f9046b81de5fdca1f32 Mon Sep 17 00:00:00 2001 From: John Sullivan Date: Thu, 14 Oct 2021 14:32:28 -0400 Subject: [PATCH 5/5] Cleaned up documentation for ResponseProcessors --- .../response/BinaryResponseProcessor.java | 21 ++++++++++++------- .../response/FieldResponseProcessor.java | 21 +++++++++++++------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java b/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java index c6cc95820..cbcf86f43 100644 --- a/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java +++ b/Data/src/main/java/org/tribuo/data/columnar/processors/response/BinaryResponseProcessor.java @@ -34,6 +34,16 @@ * A {@link ResponseProcessor} that takes a single value of the * field as the positive class and all other values as the negative * class. + *

+ * We support specifying field names and default values both singly through {@link #fieldName} and {@link #positiveResponse} + * and in a list through {@link #fieldNames} and {@link #positiveResponses}. The constructors and configuration preprocessing + * have differing behaviors based on which fields are populated: + *

+ * All other settings are invalid. */ public class BinaryResponseProcessor> implements ResponseProcessor { @@ -75,15 +85,12 @@ public class BinaryResponseProcessor> implements ResponsePro @ConfigurableName private String configName; - /** - * We support specifying field names and default values both singly through {@link #fieldName} and {@link #positiveResponse} - * and in a list through {@link #fieldNames} and {@link #positiveResponses}. Canonically all internal logic is driven by - * fieldNames and positiveResponses, so this method takes values populated in fieldName and positiveResponse and sets them - * appropriately. - */ @Override public void postConfig() { - + /* + * Canonically all internal logic is driven by fieldNames and positiveResponses, so this method takes values + * populated in fieldName and positiveResponse and sets them appropriately. + */ boolean bothFieldNamesPopulated = fieldName != null && fieldNames != null; boolean neitherFieldNamesPopulated = fieldName == null && fieldNames == null; boolean multipleFieldNamesPopulated = fieldNames != null; diff --git a/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java b/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java index 577342b77..fdb8ac7d1 100644 --- a/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java +++ b/Data/src/main/java/org/tribuo/data/columnar/processors/response/FieldResponseProcessor.java @@ -32,6 +32,16 @@ /** * A response processor that returns the value(s) in a given (set of) fields. + *

+ * We support specifying field names and default values both singly through {@link #fieldName} and {@link #defaultValue} + * and in a list through {@link #fieldNames} and {@link #defaultValues}. The constructors and configuration preprocessing + * have differing behaviors based on which fields are populated: + *

    + *
  • {@link #fieldNames} and {@link #defaultValues} are both populated and the same length: fieldNames[i]'s defaultValue is defaultValues[i] + *
  • {@link #fieldNames} and {@link #defaultValue} are both populated: defaultValue is broadcast across all fieldNames + *
  • {@link #fieldName} and {@link #defaultValue} are both populated: fieldNames[0] == fieldName, defaultValues[0] == defaultValue + *
+ * All other settings are invalid. */ public class FieldResponseProcessor> implements ResponseProcessor { @@ -60,14 +70,13 @@ public class FieldResponseProcessor> implements ResponseProc @ConfigurableName private String configName; - /** - * We support specifying field names and default values both singly through {@link #fieldName} and {@link #defaultValue} - * and in a list through {@link #fieldNames} and {@link #defaultValues}. Canonically all internal logic is driven by - * fieldNames and defaultValues, so this method takes values populated in fieldName and defaultValue and sets them - * appropriately. - */ @Override public void postConfig() { + /* + * Canonically all internal logic is driven by + * fieldNames and defaultValues, so this method takes values populated in fieldName and defaultValue and sets them + * appropriately. + */ boolean bothFieldNamesPopulated = fieldName != null && fieldNames != null; boolean neitherFieldNamesPopulated = fieldName == null && fieldNames == null; boolean multipleFieldNamesPopulated = fieldNames != null;