Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Include column name in exception when ColumnSource.cast fails #6078

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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