Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
685f9ed
Multi choices
XingY Jan 8, 2026
322bd91
Multi value text choices
XingY Jan 12, 2026
672a750
fix empty array
XingY Jan 12, 2026
11f042c
crlf
XingY Jan 12, 2026
65538dc
support filters
XingY Jan 13, 2026
5a017c6
Merge remote-tracking branch 'origin/develop' into fb_mvtc
XingY Jan 13, 2026
9801a9c
clean
XingY Jan 13, 2026
afbfda0
fix
XingY Jan 14, 2026
c628cc8
order inputs
XingY Jan 14, 2026
3283986
Merge remote-tracking branch 'origin/develop' into fb_mvtc
XingY Jan 14, 2026
16150fc
order elements of a MultiChoice.Array
labkey-matthewb Jan 14, 2026
3013578
explicit null/isBlank() tests
labkey-matthewb Jan 14, 2026
74f54e4
bug fixes
XingY Jan 14, 2026
9e711cc
fix assay import
XingY Jan 15, 2026
21cc32b
fix assay import
XingY Jan 15, 2026
4aabc6b
code review changes
XingY Jan 15, 2026
defb3c1
show advanced settings
XingY Jan 15, 2026
0edb7ec
Fix study dataset multi value crud
XingY Jan 15, 2026
c34b984
code review changes
XingY Jan 15, 2026
6630ad0
Merge remote-tracking branch 'origin/develop' into fb_mvtc
XingY Jan 16, 2026
a96ac32
code review changes
XingY Jan 16, 2026
61693b3
Merge remote-tracking branch 'origin/develop' into fb_mvtc_empty
labkey-matthewb Jan 16, 2026
3059b18
SimpleConvert
labkey-matthewb Jan 16, 2026
421a026
SimpleConvert
labkey-matthewb Jan 16, 2026
b446305
JdbcType.isEmpty()
labkey-matthewb Jan 19, 2026
e3e491b
Merge remote-tracking branch 'origin/develop' into fb_mvtc_empty
labkey-matthewb Jan 19, 2026
e7b3df9
add to the right branch
labkey-matthewb Jan 20, 2026
b1a9acd
Merge remote-tracking branch 'origin/develop' into fb_mvtc_empty
labkey-matthewb Jan 20, 2026
6740d1a
merge from develop
XingY Jan 20, 2026
03c2f64
fix selectRows api
XingY Jan 20, 2026
b2c1fca
add compare type for array is null
XingY Jan 20, 2026
e4b9f51
fix check
labkey-matthewb Jan 21, 2026
910d2a0
fix comparator
XingY Jan 22, 2026
5212b4e
Merge remote-tracking branch 'origin/develop' into fb_mvtc_empty
labkey-matthewb Jan 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/src/messages/Validation.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ UniqueViolationError=The value of the {0} field conflicts with another value in
typeMismatch=This value could not be converted
typeMismatch.int=Please enter a valid integer value
typeMismatch.double=Please enter a valid floating point number
tyepMismatch.Date=Please enter a valid date
typeMismatch.Date=Please enter a valid date
requiredError=This field is required
uniqueConstraint=Value conflicts with existing data. Please enter a unique value.
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ else if (AbstractAssayProvider.PARTICIPANT_VISIT_RESOLVER_PROPERTY_NAME.equals(k
ColumnInfo pk = pks.get(0);
try
{
Object filterValue = ConvertUtils.convert(value, pk.getJavaClass());
Object filterValue = pk.convert(value);
SimpleFilter filter = new SimpleFilter(pk.getFieldKey(), filterValue);
Set<String> cols = new HashSet<>();
cols.add(lookupTable.getTitleColumn());
Expand Down
6 changes: 6 additions & 0 deletions api/src/org/labkey/api/data/AbstractWrappedColumnInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -848,4 +848,10 @@ public boolean isMultiValued()
{
return delegate.isMultiValued();
}

@Override @Transient
public final SimpleConvert getConvertFn()
{
return ColumnRenderProperties.getDefaultConvertFn(this);
}
}
7 changes: 7 additions & 0 deletions api/src/org/labkey/api/data/BaseColumnInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -2229,4 +2230,10 @@ public void setRemapMissingBehavior(SimpleTranslator.RemapMissingBehavior missin
{
_remapMissingBehavior = missingBehavior;
}

@Override @Transient
public final SimpleConvert getConvertFn()
{
return ColumnRenderProperties.getDefaultConvertFn(this);
}
}
12 changes: 7 additions & 5 deletions api/src/org/labkey/api/data/BeanViewForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,14 @@ else if (o instanceof Map)
}

@Override
protected Class<?> getTruePropType(String propName)
protected SimpleConvert getSimpleConvert(String propName)
{
var ret = _dynaClass.getTruePropType(propName);
if (null == ret)
ret = super.getTruePropType(propName);
return ret;
var type = _dynaClass.getTruePropType(propName);
if (null != type)
{
return ConvertHelper.getSimpleConvert(type);
}
return super.getSimpleConvert(propName);
}

// DynaBean
Expand Down
259 changes: 259 additions & 0 deletions api/src/org/labkey/api/data/ColumnInfoTests.jsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
<%@ page import="org.labkey.api.data.JdbcType" %>
<%@ page import="org.junit.Test" %>
<%@ page import="static org.junit.Assert.*" %>
<%@ page import="org.labkey.api.data.BaseColumnInfo" %>
<%@ page import="org.jetbrains.annotations.NotNull" %>
<%@ page import="java.math.BigDecimal" %>
<%@ page import="org.labkey.api.exp.PropertyType" %>
<%@ page import="org.labkey.api.ontology.Quantity" %>
<%@ page import="org.labkey.api.ontology.Unit" %>
<%@ page import="org.labkey.api.ontology.KindOfQuantity" %>
<%@ page import="org.labkey.api.data.MutableColumnInfo" %>
<%@ page import="org.labkey.api.data.WrappedColumnInfo" %>
<%@ page import="org.apache.commons.beanutils.ConversionException" %>
<%@ page import="org.labkey.api.data.ColumnInfo" %>
<%@ page import="org.labkey.api.data.dialect.SqlDialect" %>
<%@ page import="org.labkey.api.data.CoreSchema" %>
<%@ page import="java.nio.ByteBuffer" %>
<%@ page extends="org.labkey.api.jsp.JspTest.BVT" %>
<%--
This tests uses MockRequest to test some expected Headers and Meta tags for various types of requests.
--%>
<%!
void testConvert(ColumnInfo col, Object expected, Object val)
{
var result = col.convert(val);
assertNotNull(result);
assertEquals(col.getJdbcType().getJavaClass(), result.getClass());
assertEquals(expected, result);
}

void testConvertsToNull(ColumnInfo col, Object val)
{
var result = col.convert(val);
assertNull(result);
}

void testConversionException(ColumnInfo col, Object val)
{
try
{
col.convert(val);
fail();
}
catch (ConversionException x)
{
return;
}
}

void testConvert(JdbcType type, Object expected, Object val)
{
var col = new BaseColumnInfo("~", null, type);
testConvert(col.lock(), expected, val);
}

void testConvertsToNull(JdbcType type, Object val)
{
var col = new BaseColumnInfo("~", null, type);
testConvertsToNull(col.lock(), val);
}

void testConversionException(JdbcType type, Object val)
{
var col = new BaseColumnInfo("~", null, type);
testConversionException(col.lock(), val);
}

void testConvert(PropertyType pt, Object expected, @NotNull Object val)
{
var col = new BaseColumnInfo("~", null, pt.getJdbcType());
col.setPropertyType(pt);
testConvert(col.lock(), expected, val);
}

void testConvertsToNull(PropertyType pt, Object val)
{
var col = new BaseColumnInfo("~", null, pt.getJdbcType());
col.setPropertyType(pt);
testConvertsToNull(col.lock(), val);
}

void testConversionException(PropertyType pt, Object val)
{
var col = new BaseColumnInfo("~", null, pt.getJdbcType());
col.setPropertyType(pt);
testConversionException(col.lock(), val);
}

void testQuantity(Unit displayUnit, Quantity expected, Object value)
{
// UNDONE: setDisplayUnit is NYI???
var col = new BaseColumnInfo("~", null, JdbcType.DOUBLE)
{
@Override
public Unit getDisplayUnit()
{
return displayUnit;
}

@Override
public KindOfQuantity getKindOfQuantity()
{
return null==displayUnit ? null : displayUnit.getKindOfQuantity();
}
};
testConvert(col.lock(), expected, value);
}


/** This test is for the integrated ColumnInfo.convert() logic.
* <p></p>
* A lot of this testing is redunant with lower-level unit testing,
* however, his till servers as a basic conversion smoke test.
* <p></p>
* In particualr, the PropertyType conversions are pretty
* redundant with ConvertHelper.convert() and JdbcType.convert()
* (PropertyType predates JdbcType), but there are some differenes
Comment on lines +112 to +117
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are multiple spelling errors in the comment: "redunant" should be "redundant", "his till servers" should be "this still serves", "particualr" should be "particular", and "differenes" should be "differences".

Suggested change
* A lot of this testing is redunant with lower-level unit testing,
* however, his till servers as a basic conversion smoke test.
* <p></p>
* In particualr, the PropertyType conversions are pretty
* redundant with ConvertHelper.convert() and JdbcType.convert()
* (PropertyType predates JdbcType), but there are some differenes
* A lot of this testing is redundant with lower-level unit testing,
* however, this still serves as a basic conversion smoke test.
* <p></p>
* In particular, the PropertyType conversions are pretty
* redundant with ConvertHelper.convert() and JdbcType.convert()
* (PropertyType predates JdbcType), but there are some differences

Copilot uses AI. Check for mistakes.
* in implementation. We should try to reconcile these differences.
*/
// @Test
public void testColumnConvert() throws Exception
{
// see also ConvertHelper.testEmpty()

// w/o propertyType
for (JdbcType type : JdbcType.values())
{
switch (type)
{
case BIGINT ->
{
testConvert(type, Long.valueOf(5), Integer.valueOf(5));
testConvert(type, Long.valueOf(5), "5");
testConvert(type, Long.valueOf(5), Double.valueOf(5.00000));
testConversionException(type, Double.valueOf(5.00001));
testConvert(type, Long.valueOf(5), new BigDecimal("5.000"));
testConversionException(type, new BigDecimal("5.001"));
testConversionException(type, "5g");
testConvertsToNull(type, "");
testConvertsToNull(type, null);
}
case BINARY, LONGVARBINARY, VARBINARY ->
{
testConvert(type, ByteBuffer.wrap(new byte[] {0x00,0x00,0x00,0x05}), Long.valueOf(5));
}
case BOOLEAN -> {}
case CHAR,LONGVARCHAR,VARCHAR ->
{
testConvertsToNull(type, null);
// NOTE StandardDataIterator optionally trims, but convert() does not.
// see SimpleTranslator.createConvertColumn()
testConvert(type, " no trim ", " no trim ");
// JdbcType does not convert empty string to null, ColumnInfo.convert() and PropertyType.conver() do
assertEquals("", type.convert(""));
testConvertsToNull(type, "");
testConvertsToNull(type, null);
}
case DECIMAL -> {}
case DOUBLE -> {}
case INTEGER -> {}
case REAL -> {}
case SMALLINT, TINYINT -> {}
case DATE -> {}
case TIME -> {}
case TIMESTAMP -> {}
case GUID -> {}
case ARRAY, NULL, OTHER -> { /* ignore */ }
default -> fail("We missed a JdbcType: " + type.name());
}
}

// testArray()

// w/ propertyType
for (var type : PropertyType.values())
{
switch (type)
{
case BOOLEAN -> {}
case STRING ->
{
testConvertsToNull(type, null);
testConvertsToNull(type, "");
testConvert(type, " no trim ", " no trim ");
}
case MULTI_LINE ->
{
testConvertsToNull(type, null);
testConvertsToNull(type, "");
testConvert(type, " no trim ", " no trim ");
}
case MULTI_CHOICE -> {}
case RESOURCE -> {}
case INTEGER -> {}
case BIGINT ->
{
testConvert(type, Long.valueOf(5), Integer.valueOf(5));
testConvert(type, Long.valueOf(5), "5");
testConvert(type, Long.valueOf(5), new BigDecimal("5.001"));
testConvertsToNull(type, null);
testConvertsToNull(type, "");
testConversionException(type, "5g");
}
case BINARY -> {}
case FILE_LINK -> {}
case ATTACHMENT -> {}
case DATE_TIME -> {}
case DATE -> {}
case TIME -> {}
case DOUBLE -> {}
case FLOAT -> {}
case DECIMAL -> {}
case XML_TEXT -> {}
default -> fail("We missed a PropertyType: " + type.name());
}
}

// Quantity
Unit unit = Unit.kg;
testQuantity(unit, Quantity.of(5000,unit.getBase()), "5");
}


public void testLocked(MutableColumnInfo col)
{
col.setAlias("!");
col.lock();
try
{
col.setAlias("!");
fail("not locked?");
}
catch (IllegalStateException x)
{
// success
}
}

static class _ColumnInfo extends BaseColumnInfo
{
_ColumnInfo()
{
super("~", JdbcType.INTEGER);
}

@Override
public SqlDialect getSqlDialect()
{
return CoreSchema.getInstance().getSqlDialect();
}
}

// @Test
public void testLocked()
{
testLocked(new _ColumnInfo());
testLocked(WrappedColumnInfo.wrap(new _ColumnInfo()));
}
%>
Loading