From 4ce9a71a115a0cb61ce7f1a3f5c3fa2498bc1865 Mon Sep 17 00:00:00 2001 From: cnathe Date: Fri, 3 Oct 2025 08:53:38 -0500 Subject: [PATCH 1/5] Issue 53625: remap assay lookup keys before validation so that validators can validate the remapped value --- .../assay/AbstractAssayTsvDataHandler.java | 62 ++++++++++--------- .../api/property/LookupValidator.java | 13 +++- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java b/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java index cd6972adf59..8e201340389 100644 --- a/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java +++ b/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java @@ -842,10 +842,40 @@ else if (entry.getKey().equalsIgnoreCase(ProvenanceService.PROVENANCE_INPUT_PROP map.put(pd.getName(), o); } - // validate the data value for the non-sample lookup fields - // note that sample lookup mapping and validation will happen separately below in the code which handles populating materialInputs boolean isSampleLookupById = lookupToAllSamplesById.contains(pd) || lookupToSampleTypeById.containsKey(pd); boolean isSampleLookupByName = lookupToAllSamplesByName.contains(pd) || lookupToSampleTypeByName.containsKey(pd); + + // If we have a String value for a lookup column, attempt to use the table's unique indices or display value to convert the String into the lookup value + // See similar conversion performed in SimpleTranslator.RemapPostConvertColumn + // Issue 47509: if the value is a string and is for a SampleId lookup field, let the code below which handles populating materialInputs take care of the remapping. + // Issue 53625: remap before validation so that validators can validate the remapped value + if (o instanceof String s && remappableLookup.containsKey(pd) && !isSampleLookupById) + { + TableInfo lookupTable = remappableLookup.get(pd); + try + { + Object remapped = cache.remap(lookupTable, s, true); + if (remapped == null) + { + if (pd.getConceptURI() != null && SAMPLE_CONCEPT_URI.equals(pd.getConceptURI())) + errors.add(new PropertyValidationError(o + " not found in the current context.", pd.getName())); + else + errors.add(new PropertyValidationError("Failed to convert '" + pd.getName() + "': Could not translate value: " + o, pd.getName())); + } + else if (o != remapped) + { + o = remapped; + map.put(pd.getName(), remapped); + } + } + catch (ConversionException e) + { + errors.add(new PropertyValidationError(e.getMessage(), pd.getName())); + } + } + + // validate the data value for the non-sample lookup fields + // note that sample lookup mapping and validation will happen separately below in the code which handles populating materialInputs if (validatorMap.containsKey(pd) && !isSampleLookupById && !isSampleLookupByName) { for (ColumnValidator validator : validatorMap.get(pd)) @@ -944,34 +974,6 @@ else if (o instanceof MvFieldWrapper mvWrapper) } } - // If we have a String value for a lookup column, attempt to use the table's unique indices or display value to convert the String into the lookup value - // See similar conversion performed in SimpleTranslator.RemapPostConvertColumn - // Issue 47509: if the value is a string and is for a SampleId lookup field, let the code below which handles populating materialInputs take care of the remapping. - if (o instanceof String s && remappableLookup.containsKey(pd) && !isSampleLookupById) - { - TableInfo lookupTable = remappableLookup.get(pd); - try - { - Object remapped = cache.remap(lookupTable, s, true); - if (remapped == null) - { - if (pd.getConceptURI() != null && SAMPLE_CONCEPT_URI.equals(pd.getConceptURI())) - errors.add(new PropertyValidationError(o + " not found in the current context.", pd.getName())); - else - errors.add(new PropertyValidationError("Failed to convert '" + pd.getName() + "': Could not translate value: " + o, pd.getName())); - } - else if (o != remapped) - { - o = remapped; - map.put(pd.getName(), remapped); - } - } - catch (ConversionException e) - { - errors.add(new PropertyValidationError(e.getMessage(), pd.getName())); - } - } - if (!valueMissing && o == ERROR_VALUE && !wrongTypes.contains(pd.getName())) { wrongTypes.add(pd.getName()); diff --git a/experiment/src/org/labkey/experiment/api/property/LookupValidator.java b/experiment/src/org/labkey/experiment/api/property/LookupValidator.java index 0cd1a54b988..7a8c0a30dcd 100644 --- a/experiment/src/org/labkey/experiment/api/property/LookupValidator.java +++ b/experiment/src/org/labkey/experiment/api/property/LookupValidator.java @@ -15,6 +15,7 @@ */ package org.labkey.experiment.api.property; +import org.apache.commons.beanutils.ConversionException; import org.jetbrains.annotations.NotNull; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.ColumnRenderProperties; @@ -232,7 +233,17 @@ public boolean validate(IPropertyValidator validator, assert value != null : "Shouldn't be validating a null value"; if (value != null) - value = ConvertHelper.convert(value, crpField.getJavaObjectClass()); + { + try + { + value = ConvertHelper.convert(value, crpField.getJavaObjectClass()); + } + catch (ConversionException e) + { + // Issue 53625: If we can't convert the value to the correct type, don't try to validate it against the lookup + return false; + } + } if (crpField instanceof PropertyDescriptor field) { From eeb60e5b35c2ce4f5664e240db462b06171dc6a6 Mon Sep 17 00:00:00 2001 From: cnathe Date: Fri, 3 Oct 2025 10:58:21 -0500 Subject: [PATCH 2/5] AssayTest.testAssayLookupValidatorConversion test case for issue 53625 --- .../labkey/test/tests/study/AssayTest.java | 91 +++++++++++++++++-- 1 file changed, 85 insertions(+), 6 deletions(-) diff --git a/study/test/src/org/labkey/test/tests/study/AssayTest.java b/study/test/src/org/labkey/test/tests/study/AssayTest.java index 2dc10c99865..884a6a44288 100644 --- a/study/test/src/org/labkey/test/tests/study/AssayTest.java +++ b/study/test/src/org/labkey/test/tests/study/AssayTest.java @@ -34,19 +34,24 @@ import org.labkey.test.pages.ReactAssayDesignerPage; import org.labkey.test.pages.assay.AssayBeginPage; import org.labkey.test.params.FieldDefinition; +import org.labkey.test.params.FieldInfo; import org.labkey.test.params.experiment.SampleTypeDefinition; import org.labkey.test.tests.AbstractAssayTest; import org.labkey.test.tests.AuditLogTest; import org.labkey.test.util.AuditLogHelper; import org.labkey.test.util.DataRegionTable; +import org.labkey.test.util.DomainUtils; import org.labkey.test.util.LogMethod; import org.labkey.test.util.SampleTypeHelper; import org.labkey.test.util.StudyHelper; +import org.labkey.test.util.TestDataGenerator; +import org.labkey.test.util.data.TestDataUtils; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -58,6 +63,8 @@ public class AssayTest extends AbstractAssayTest private static final String INVESTIGATOR = "Dr. No"; private static final String GRANT = "SPECTRE"; private static final String DESCRIPTION = "World Domination."; + private static final String ISSUE_53625_ASSAY = TestDataGenerator.randomDomainName("Issue53625", DomainUtils.DomainKind.Assay); + private static final String ISSUE_53625_PROJECT = "Issue53625Project"; private static final String ISSUE_53616_ASSAY = "Issue53616Assay"; private static final String ISSUE_53616_PROJECT = "Issue53616Project" + TRICKY_CHARACTERS_FOR_PROJECT_NAMES; private static final String SAMPLE_FIELD_TEST_ASSAY = "SampleFieldTestAssay"; @@ -79,6 +86,7 @@ protected void doCleanup(boolean afterTest) throws TestTimeoutException _containerHelper.deleteProject(getProjectName(), false); _containerHelper.deleteProject(SAMPLE_FIELD_PROJECT_NAME, false); _containerHelper.deleteProject(ISSUE_53616_PROJECT, false); + _containerHelper.deleteProject(ISSUE_53625_PROJECT, false); _userHelper.deleteUsers(false, TEST_ASSAY_USR_PI1, TEST_ASSAY_USR_TECH1); } @@ -176,7 +184,7 @@ public void testSampleFieldUpdate() ssHelper = SampleTypeHelper.beginAtSampleTypesList(this, getCurrentContainerPath()); ssHelper.createSampleType(otherDefinition, "Name\nOS_1\nOS_2"); - importSampleAssayData(TEST_RUN1, "OS_1"); + importAssayData(SAMPLE_FIELD_TEST_ASSAY, TEST_RUN1, "SampleField\nOS_1"); goToManageAssays().clickAndWait(Locator.linkWithText(SAMPLE_FIELD_TEST_ASSAY)); clickAndWait(Locator.linkWithText("view results")); assertElementPresent("Sample lookup failed for: OS_1", new Locator.LinkLocator("OS_1"), 1); @@ -193,7 +201,7 @@ public void testSampleFieldUpdate() log("Verify updates saved successfully"); assertEquals("Error saving initial assay", 0, checker().errorsSinceMark()); - importSampleAssayData(TEST_RUN2, "S_1"); + importAssayData(SAMPLE_FIELD_TEST_ASSAY, TEST_RUN2, "SampleField\nS_1"); goToManageAssays().clickAndWait(Locator.linkWithText(SAMPLE_FIELD_TEST_ASSAY)); clickAndWait(Locator.linkWithText("view results")); // assertElementPresent("Sample lookup failed for: OS_1", new Locator.LinkLocator("OS_1"), 1); //TODO this becomes the RowId "<123>" after change. Issue #40047 @@ -211,7 +219,7 @@ public void testSampleFieldUpdate() assertEquals("Error saving updated sample field", 0, checker().errorsSinceMark()); log("Verify updates saved successfully"); - importSampleAssayData(TEST_RUN3, "S_2\nOS_2"); + importAssayData(SAMPLE_FIELD_TEST_ASSAY, TEST_RUN3, "SampleField\nS_2\nOS_2"); assertEquals("Error importing data after assay sample field update", 0, checker().errorsSinceMark()); goToManageAssays().clickAndWait(Locator.linkWithText(SAMPLE_FIELD_TEST_ASSAY)); @@ -223,14 +231,14 @@ public void testSampleFieldUpdate() } - private void importSampleAssayData(String runName, String sampleId) + private void importAssayData(String assayName, String runName, String runDataStr) { goToManageAssays(); - clickAndWait(Locator.linkWithText(SAMPLE_FIELD_TEST_ASSAY)); + clickAndWait(Locator.linkWithText(assayName)); clickButton("Import Data", "Run Data"); setFormElement(AssayConstants.ASSAY_NAME_FIELD_LOCATOR, runName); click(AssayConstants.TEXT_AREA_DATA_PROVIDER_LOCATOR); - setFormElement(AssayConstants.TEXT_AREA_DATA_COLLECTOR_LOCATOR, "SampleField\n" + sampleId); + setFormElement(AssayConstants.TEXT_AREA_DATA_COLLECTOR_LOCATOR, runDataStr); clickButton("Save and Finish"); } @@ -996,6 +1004,77 @@ private void verifySpecimensPresent(int aaa07Count, int controlCount, int baq000 assertTextPresent("BAQ00051", baq00051Count * 2); } + @Test // Issue 53625 + public void testAssayLookupValidatorConversion() + { + _containerHelper.createProject(ISSUE_53625_PROJECT, "Assay"); + goToProjectHome(ISSUE_53625_PROJECT); + + log("Create a list with an int key and a string value"); + String lookToListName = TestDataGenerator.randomDomainName("lookToList", DomainUtils.DomainKind.IntList); + String keyName = TestDataGenerator.randomFieldName("key", null, DomainUtils.DomainKind.Assay); + FieldInfo valueField = FieldInfo.random("value", FieldDefinition.ColumnType.String, DomainUtils.DomainKind.Assay); + _listHelper.createList(ISSUE_53625_PROJECT, lookToListName, keyName, valueField.getFieldDefinition()); + _listHelper.bulkImportData(TestDataUtils.tsvStringFromRowMaps(List.of(Map.of(valueField.getName(), "One"), Map.of(valueField.getName(), "Two")), List.of(valueField.getName()), true)); + + log("Create an assay with a results lookup field to the list, with lookup validator set"); + goToProjectHome(ISSUE_53625_PROJECT); + FieldInfo lookupField = FieldInfo.random("lookup", new FieldDefinition.IntLookup(null, "lists", lookToListName)); + ReactAssayDesignerPage designerPage = _assayHelper.createAssayDesign("General", ISSUE_53625_ASSAY); + designerPage.goToBatchFields() + .removeAllFields(false); + designerPage.goToResultsFields() + .removeAllFields(false) + .manuallyDefineFields(lookupField.getFieldDefinition().setLookupValidatorEnabled(true)); + designerPage.clickFinish(); + + log("Verify importing an assay run with valid and invalid values for the lookup field"); + verifyAssayImportForLookupValidator(ISSUE_53625_ASSAY, lookupField, "RunWithLookupValidator", true); + + log("Turn off lookup field validator and test the imports again"); + designerPage = _assayHelper.clickEditAssayDesign(); + designerPage.goToResultsFields() + .getField(lookupField.getName()) + .setLookupValidatorEnabled(false); + designerPage.clickFinish(); + verifyAssayImportForLookupValidator(ISSUE_53625_ASSAY, lookupField, "RunWithoutLookupValidator", false); + } + + private void verifyAssayImportForLookupValidator(String assayName, FieldInfo lookupField, String runName, boolean validatorOn) + { + String runDataStr = TestDataUtils.tsvStringFromRowMaps(List.of( + Map.of(lookupField.getName(), "One"), // valid + Map.of(lookupField.getName(), "99")), // invalid + List.of(lookupField.getName()), true + ); + importAssayData(assayName, runName, runDataStr); + assertTextPresent("Could not translate value: 99"); + if (validatorOn) assertTextPresent("Value '99' was not present in lookup target"); + else assertTextNotPresent("Value '99' was not present in lookup target"); + clickButton("Cancel"); + + runDataStr = TestDataUtils.tsvStringFromRowMaps(List.of( + Map.of(lookupField.getName(), "Three"), // invalid + Map.of(lookupField.getName(), "2")), // valid + List.of(lookupField.getName()), true + ); + importAssayData(assayName, runName, runDataStr); + assertTextPresent("Failed to convert"); + assertTextPresent("Could not translate value: Three"); + clickButton("Cancel"); + + runDataStr = TestDataUtils.tsvStringFromRowMaps(List.of( + Map.of(lookupField.getName(), "One"), // valid + Map.of(lookupField.getName(), "2")), // valid + List.of(lookupField.getName()), true + ); + importAssayData(assayName, runName, runDataStr); + clickAndWait(Locator.linkWithText(runName)); + DataRegionTable dataTable = new DataRegionTable("Data", getDriver()); + checker().verifyEquals("Incorrect number of results shown.", 2, dataTable.getDataRowCount()); + checker().verifyEquals("Lookup values not as expected.", List.of("One", "Two"), dataTable.getColumnDataAsText(lookupField.getLabel())); + } + @Override protected BrowserType bestBrowser() { From c7f786002b7609b14a7266ce5e5ecd94cca3c611 Mon Sep 17 00:00:00 2001 From: cnathe Date: Fri, 3 Oct 2025 16:49:33 -0500 Subject: [PATCH 3/5] don't try/catch within LookupValidator.validate, handle that in the AbstractAssayTsvDataHandler.checkData --- .../api/assay/AbstractAssayTsvDataHandler.java | 16 ++++++++++++---- .../experiment/api/property/LookupValidator.java | 13 +------------ 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java b/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java index 8e201340389..05f521a0ba1 100644 --- a/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java +++ b/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java @@ -849,8 +849,9 @@ else if (entry.getKey().equalsIgnoreCase(ProvenanceService.PROVENANCE_INPUT_PROP // See similar conversion performed in SimpleTranslator.RemapPostConvertColumn // Issue 47509: if the value is a string and is for a SampleId lookup field, let the code below which handles populating materialInputs take care of the remapping. // Issue 53625: remap before validation so that validators can validate the remapped value - if (o instanceof String s && remappableLookup.containsKey(pd) && !isSampleLookupById) + if (o != null && remappableLookup.containsKey(pd) && !isSampleLookupById) { + String s = o instanceof String ? (String) o : o.toString(); TableInfo lookupTable = remappableLookup.get(pd); try { @@ -880,9 +881,16 @@ else if (o != remapped) { for (ColumnValidator validator : validatorMap.get(pd)) { - String error = validator.validate(rowNum, o, validatorContext); - if (error != null) - errors.add(new PropertyValidationError(error, pd.getName())); + try + { + String error = validator.validate(rowNum, o, validatorContext); + if (error != null) + errors.add(new PropertyValidationError(error, pd.getName())); + } + catch (ConversionException e) + { + errors.add(new PropertyValidationError(e.getMessage(), pd.getName())); + } } } diff --git a/experiment/src/org/labkey/experiment/api/property/LookupValidator.java b/experiment/src/org/labkey/experiment/api/property/LookupValidator.java index 7a8c0a30dcd..0cd1a54b988 100644 --- a/experiment/src/org/labkey/experiment/api/property/LookupValidator.java +++ b/experiment/src/org/labkey/experiment/api/property/LookupValidator.java @@ -15,7 +15,6 @@ */ package org.labkey.experiment.api.property; -import org.apache.commons.beanutils.ConversionException; import org.jetbrains.annotations.NotNull; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.ColumnRenderProperties; @@ -233,17 +232,7 @@ public boolean validate(IPropertyValidator validator, assert value != null : "Shouldn't be validating a null value"; if (value != null) - { - try - { - value = ConvertHelper.convert(value, crpField.getJavaObjectClass()); - } - catch (ConversionException e) - { - // Issue 53625: If we can't convert the value to the correct type, don't try to validate it against the lookup - return false; - } - } + value = ConvertHelper.convert(value, crpField.getJavaObjectClass()); if (crpField instanceof PropertyDescriptor field) { From c00d0c3c4b9660420d8099a23d386371e1efc56d Mon Sep 17 00:00:00 2001 From: cnathe Date: Fri, 3 Oct 2025 16:49:53 -0500 Subject: [PATCH 4/5] update test case for the issue found when just a numeric value is provided --- .../labkey/test/tests/study/AssayTest.java | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/study/test/src/org/labkey/test/tests/study/AssayTest.java b/study/test/src/org/labkey/test/tests/study/AssayTest.java index 884a6a44288..fc612123a4f 100644 --- a/study/test/src/org/labkey/test/tests/study/AssayTest.java +++ b/study/test/src/org/labkey/test/tests/study/AssayTest.java @@ -1012,14 +1012,18 @@ public void testAssayLookupValidatorConversion() log("Create a list with an int key and a string value"); String lookToListName = TestDataGenerator.randomDomainName("lookToList", DomainUtils.DomainKind.IntList); - String keyName = TestDataGenerator.randomFieldName("key", null, DomainUtils.DomainKind.Assay); - FieldInfo valueField = FieldInfo.random("value", FieldDefinition.ColumnType.String, DomainUtils.DomainKind.Assay); + String keyName = TestDataGenerator.randomFieldName("key", null, DomainUtils.DomainKind.IntList); + FieldInfo valueField = FieldInfo.random("value", FieldDefinition.ColumnType.String, DomainUtils.DomainKind.IntList); _listHelper.createList(ISSUE_53625_PROJECT, lookToListName, keyName, valueField.getFieldDefinition()); - _listHelper.bulkImportData(TestDataUtils.tsvStringFromRowMaps(List.of(Map.of(valueField.getName(), "One"), Map.of(valueField.getName(), "Two")), List.of(valueField.getName()), true)); + _listHelper.bulkImportData(TestDataUtils.tsvStringFromRowMaps(List.of( + Map.of(valueField.getName(), "One"), + Map.of(valueField.getName(), "Two"), + Map.of(valueField.getName(), "123") + ), List.of(valueField.getName()), true)); log("Create an assay with a results lookup field to the list, with lookup validator set"); goToProjectHome(ISSUE_53625_PROJECT); - FieldInfo lookupField = FieldInfo.random("lookup", new FieldDefinition.IntLookup(null, "lists", lookToListName)); + FieldInfo lookupField = new FieldInfo("lookup", new FieldDefinition.IntLookup(null, "lists", lookToListName)); ReactAssayDesignerPage designerPage = _assayHelper.createAssayDesign("General", ISSUE_53625_ASSAY); designerPage.goToBatchFields() .removeAllFields(false); @@ -1065,14 +1069,27 @@ private void verifyAssayImportForLookupValidator(String assayName, FieldInfo loo runDataStr = TestDataUtils.tsvStringFromRowMaps(List.of( Map.of(lookupField.getName(), "One"), // valid - Map.of(lookupField.getName(), "2")), // valid + Map.of(lookupField.getName(), "2"), // valid + Map.of(lookupField.getName(), "123")), // valid List.of(lookupField.getName()), true ); importAssayData(assayName, runName, runDataStr); clickAndWait(Locator.linkWithText(runName)); DataRegionTable dataTable = new DataRegionTable("Data", getDriver()); - checker().verifyEquals("Incorrect number of results shown.", 2, dataTable.getDataRowCount()); - checker().verifyEquals("Lookup values not as expected.", List.of("One", "Two"), dataTable.getColumnDataAsText(lookupField.getLabel())); + checker().verifyEquals("Incorrect number of results shown.", 3, dataTable.getDataRowCount()); + checker().verifyEquals("Lookup values not as expected.", List.of("One", "Two", "123"), dataTable.getColumnDataAsText(lookupField.getLabel())); + + // test with just the numeric value since that was causing issues during manual testing + runName = runName + "NumericOnly"; + runDataStr = TestDataUtils.tsvStringFromRowMaps(List.of( + Map.of(lookupField.getName(), "123")), // valid + List.of(lookupField.getName()), true + ); + importAssayData(assayName, runName, runDataStr); + clickAndWait(Locator.linkWithText(runName)); + dataTable = new DataRegionTable("Data", getDriver()); + checker().verifyEquals("Incorrect number of results shown.", 1, dataTable.getDataRowCount()); + checker().verifyEquals("Lookup values not as expected.", List.of("123"), dataTable.getColumnDataAsText(lookupField.getLabel())); } @Override From d0de01d530fe5f77d7c4e7b2d2a09ea959f436ff Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 7 Oct 2025 08:36:12 -0500 Subject: [PATCH 5/5] CR feedback - remove unnecessary null check and try/catch --- .../assay/AbstractAssayTsvDataHandler.java | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java b/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java index 05f521a0ba1..ebf14e7febc 100644 --- a/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java +++ b/api/src/org/labkey/api/assay/AbstractAssayTsvDataHandler.java @@ -853,25 +853,18 @@ else if (entry.getKey().equalsIgnoreCase(ProvenanceService.PROVENANCE_INPUT_PROP { String s = o instanceof String ? (String) o : o.toString(); TableInfo lookupTable = remappableLookup.get(pd); - try + Object remapped = cache.remap(lookupTable, s, true); + if (remapped == null) { - Object remapped = cache.remap(lookupTable, s, true); - if (remapped == null) - { - if (pd.getConceptURI() != null && SAMPLE_CONCEPT_URI.equals(pd.getConceptURI())) - errors.add(new PropertyValidationError(o + " not found in the current context.", pd.getName())); - else - errors.add(new PropertyValidationError("Failed to convert '" + pd.getName() + "': Could not translate value: " + o, pd.getName())); - } - else if (o != remapped) - { - o = remapped; - map.put(pd.getName(), remapped); - } + if (SAMPLE_CONCEPT_URI.equals(pd.getConceptURI())) + errors.add(new PropertyValidationError(o + " not found in the current context.", pd.getName())); + else + errors.add(new PropertyValidationError("Failed to convert '" + pd.getName() + "': Could not translate value: " + o, pd.getName())); } - catch (ConversionException e) + else if (o != remapped) { - errors.add(new PropertyValidationError(e.getMessage(), pd.getName())); + o = remapped; + map.put(pd.getName(), remapped); } }