Skip to content

Commit

Permalink
fix: Include column name in exception when ColumnSource.cast fails (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
rbasralian authored Sep 19, 2024
1 parent 5a6bdc1 commit 45ebf06
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,34 @@ default void exportAllTo(final Object @NotNull [] dest, @NotNull final T tuple)
*/
@FinalDefault
default <TYPE> ColumnSource<TYPE> cast(Class<? extends TYPE> clazz) {
return cast(clazz, (String) null);
}

/**
* Returns this {@code ColumnSource}, parameterized by {@code <TYPE>}, if the data type of this column (as given by
* {@link #getType()}) can be cast to {@code clazz}. This is analogous to casting the objects provided by this
* column source to {@code clazz}.
* <p>
* For example, the following code will throw an exception if the "MyString" column does not actually contain
* {@code String} data:
*
* <pre>
* ColumnSource&lt;String&gt; colSource = table.getColumnSource("MyString").cast(String.class, "MyString")
* </pre>
* <p>
* Due to the nature of type erasure, the JVM will still insert an additional cast to {@code TYPE} when elements are
* retrieved from the column source, such as with {@code String myStr = colSource.get(0)}.
*
* @param clazz The target type.
* @param <TYPE> The target type, as a type parameter. Intended to be inferred from {@code clazz}.
* @param colName An optional column name, which will be included in exception messages.
* @return A {@code ColumnSource} parameterized by {@code TYPE}.
*/
@FinalDefault
default <TYPE> ColumnSource<TYPE> cast(Class<? extends TYPE> clazz, @Nullable String colName) {
Require.neqNull(clazz, "clazz");
TypeHelper.checkCastTo("ColumnSource", getType(), clazz);
final String castCheckPrefix = colName == null ? "ColumnSource" : "ColumnSource[" + colName + ']';
TypeHelper.checkCastTo(castCheckPrefix, getType(), clazz);
// noinspection unchecked
return (ColumnSource<TYPE>) this;
}
Expand Down Expand Up @@ -208,8 +234,39 @@ default <TYPE> ColumnSource<TYPE> cast(Class<? extends TYPE> clazz) {
*/
@FinalDefault
default <TYPE> ColumnSource<TYPE> cast(Class<? extends TYPE> clazz, @Nullable Class<?> componentType) {
return cast(clazz, componentType, null);
}

/**
* Returns this {@code ColumnSource}, parameterized by {@code <TYPE>}, if the data type of this column (as given by
* {@link #getType()}) can be cast to {@code clazz}. This is analogous to casting the objects provided by this
* column source to {@code clazz}. Additionally, this checks that the component type of this column (as given by
* {@link #getComponentType()}) can be cast to {@code componentType} (both must be present and castable, or both
* must be {@code null}).
*
* <p>
* For example, the following code will throw an exception if the "MyString" column does not actually contain
* {@code String} data:
*
* <pre>
* ColumnSource&lt;String&gt; colSource = table.getColumnSource("MyString").cast(String.class, null, "MyString")
* </pre>
* <p>
* Due to the nature of type erasure, the JVM will still insert an additional cast to {@code TYPE} when elements are
* retrieved from the column source, such as with {@code String myStr = colSource.get(0)}.
*
* @param clazz The target type.
* @param componentType The target component type, may be {@code null}.
* @param colName An optional column name, which will be included in exception messages.
* @param <TYPE> The target type, as a type parameter. Intended to be inferred from {@code clazz}.
* @return A {@code ColumnSource} parameterized by {@code TYPE}.
*/
@FinalDefault
default <TYPE> ColumnSource<TYPE> cast(Class<? extends TYPE> clazz, @Nullable Class<?> componentType,
@Nullable String colName) {
Require.neqNull(clazz, "clazz");
TypeHelper.checkCastTo("ColumnSource", getType(), getComponentType(), clazz, componentType);
final String castCheckPrefix = colName == null ? "ColumnSource" : "ColumnSource[" + colName + ']';
TypeHelper.checkCastTo(castCheckPrefix, getType(), getComponentType(), clazz, componentType);
// noinspection unchecked
return (ColumnSource<TYPE>) this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ default <T> ColumnSource<T> getColumnSource(String sourceName, Class<? extends T
@SuppressWarnings("rawtypes")
ColumnSource rawColumnSource = getColumnSource(sourceName);
// noinspection unchecked
return rawColumnSource.cast(clazz);
return rawColumnSource.cast(clazz, sourceName);
}

@Override
Expand All @@ -109,7 +109,7 @@ default <T> ColumnSource<T> getColumnSource(String sourceName, Class<? extends T
@SuppressWarnings("rawtypes")
ColumnSource rawColumnSource = getColumnSource(sourceName);
// noinspection unchecked
return rawColumnSource.cast(clazz, componentType);
return rawColumnSource.cast(clazz, componentType, sourceName);
}

// -----------------------------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3848,6 +3848,79 @@ public void testMultipleUpdateGraphs() {
assertEquals(g2, g2.sharedLock().computeLocked(() -> merge(s1, s2, r2).getUpdateGraph()));
}

public void testColumnSourceCast() {
// Create a test table with a String column and an array column
final Table testTable = TableTools.newTable(
TableTools.stringCol("MyTestStrCol", "A", "B", "C"),
TableTools.col("MyTestArrCol", new String[] {"A0", "A1"}, new String[] {"B0", "B1"},
new String[] {"C0", "C1"}));

/* Test getting column sources with checked types */

// Test getColumnSource for MyTestStrCol
ColumnSource<CharSequence> stringColSource = testTable.getColumnSource("MyTestStrCol", CharSequence.class);
assertNotNull(stringColSource);
assertEquals(String.class, stringColSource.getType()); // actual type is still String

// Test getColumnSource for MyTestStrCol with wrong type and verify exception message
ClassCastException colTypeException = Assert.assertThrows(ClassCastException.class, () -> {
ColumnSource<Integer> intColSource = testTable.getColumnSource("MyTestStrCol", Integer.class);
});
assertEquals("Cannot convert ColumnSource[MyTestStrCol] of type java.lang.String to type java.lang.Integer",
colTypeException.getMessage());

// Test getColumnSource for MyTestArrCol
ColumnSource<CharSequence[]> arrColSource =
testTable.getColumnSource("MyTestArrCol", CharSequence[].class, CharSequence.class);
assertNotNull(arrColSource);
assertEquals(String[].class, arrColSource.getType());
assertEquals(String.class, arrColSource.getComponentType());

// Test getColumnSource for MyTestArrCol with a wrong component type and verify exception message
ClassCastException wrongComponentException = Assert.assertThrows(ClassCastException.class, () -> {
ColumnSource<CharSequence[]> wrongComponentTypeSource =
testTable.getColumnSource("MyTestArrCol", CharSequence[].class, Integer.class);
});
assertEquals(
"Cannot convert ColumnSource[MyTestArrCol] componentType of type java.lang.String to java.lang.Integer (for [Ljava.lang.String; / [Ljava.lang.CharSequence;)",
wrongComponentException.getMessage());


/* Verify exception messages of underlying ColumnSource.cast method, with and without column name specified */

ColumnSource<?> rawStrColSource = testTable.getColumnSource("MyTestStrCol");
// cast() without component type, with column name specified
ClassCastException castExceptionNoCompWithColName = Assert.assertThrows(ClassCastException.class, () -> {
rawStrColSource.cast(Boolean.class, "MyTestStrCol");
});
assertEquals("Cannot convert ColumnSource[MyTestStrCol] of type java.lang.String to type java.lang.Boolean",
castExceptionNoCompWithColName.getMessage());

// cast() without component type and no column name specified
ClassCastException castExceptionNoCompNoColName = Assert.assertThrows(ClassCastException.class, () -> {
rawStrColSource.cast(Boolean.class);
});
assertEquals("Cannot convert ColumnSource of type java.lang.String to type java.lang.Boolean",
castExceptionNoCompNoColName.getMessage());

ColumnSource<Object> rawArrColSource = testTable.getColumnSource("MyTestArrCol");
// cast() with component type and column name specified
ClassCastException castExceptionWithCompAndColName = Assert.assertThrows(ClassCastException.class, () -> {
rawArrColSource.cast(Object[].class, Integer.class, "MyTestArrCol");
});
assertEquals(
"Cannot convert ColumnSource[MyTestArrCol] componentType of type java.lang.String to java.lang.Integer (for [Ljava.lang.String; / [Ljava.lang.Object;)",
castExceptionWithCompAndColName.getMessage());

// cast() with component type and no column name specified
ClassCastException castExceptionWithCompNoColName = Assert.assertThrows(ClassCastException.class, () -> {
rawArrColSource.cast(Object[].class, Integer.class);
});
assertEquals(
"Cannot convert ColumnSource componentType of type java.lang.String to java.lang.Integer (for [Ljava.lang.String; / [Ljava.lang.Object;)",
castExceptionWithCompNoColName.getMessage());
}

private static final class DummyUpdateGraph implements UpdateGraph {

private final String name;
Expand Down

0 comments on commit 45ebf06

Please sign in to comment.