|
|
|
@ -17,14 +17,26 @@ |
|
|
|
package org.springframework.jdbc.core.metadata; |
|
|
|
package org.springframework.jdbc.core.metadata; |
|
|
|
|
|
|
|
|
|
|
|
import java.sql.DatabaseMetaData; |
|
|
|
import java.sql.DatabaseMetaData; |
|
|
|
|
|
|
|
import java.sql.ResultSet; |
|
|
|
import java.sql.SQLException; |
|
|
|
import java.sql.SQLException; |
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
|
|
|
import java.util.Arrays; |
|
|
|
|
|
|
|
import java.util.List; |
|
|
|
|
|
|
|
import java.util.function.IntFunction; |
|
|
|
|
|
|
|
|
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
|
|
|
|
import org.mockito.InOrder; |
|
|
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThatIllegalStateException; |
|
|
|
import org.springframework.dao.InvalidDataAccessApiUsageException; |
|
|
|
|
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
|
|
|
|
import org.springframework.util.function.ThrowingBiFunction; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
|
|
|
import static org.mockito.BDDMockito.given; |
|
|
|
import static org.mockito.BDDMockito.given; |
|
|
|
|
|
|
|
import static org.mockito.Mockito.inOrder; |
|
|
|
import static org.mockito.Mockito.mock; |
|
|
|
import static org.mockito.Mockito.mock; |
|
|
|
import static org.mockito.Mockito.verify; |
|
|
|
import static org.mockito.Mockito.verifyNoMoreInteractions; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Tests for {@link GenericCallMetaDataProvider}. |
|
|
|
* Tests for {@link GenericCallMetaDataProvider}. |
|
|
|
@ -36,36 +48,230 @@ class GenericCallMetaDataProviderTests { |
|
|
|
private final DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class); |
|
|
|
private final DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class); |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void procedureNameWithPatternIsEscape() throws SQLException { |
|
|
|
void procedureNameWithNoMatch() throws SQLException { |
|
|
|
given(this.databaseMetaData.getSearchStringEscape()).willReturn("@"); |
|
|
|
|
|
|
|
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData); |
|
|
|
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData); |
|
|
|
given(this.databaseMetaData.getProcedures(null, null, "MY@_PROCEDURE")) |
|
|
|
|
|
|
|
.willThrow(new IllegalStateException("Expected")); |
|
|
|
ResultSet noProcedure = mockProcedures(); |
|
|
|
assertThatIllegalStateException().isThrownBy(() -> provider.initializeWithProcedureColumnMetaData( |
|
|
|
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE")) |
|
|
|
this.databaseMetaData, null, null, "my_procedure")); |
|
|
|
.willReturn(noProcedure); |
|
|
|
verify(this.databaseMetaData).getProcedures(null, null, "MY@_PROCEDURE"); |
|
|
|
ResultSet noFunction = mockProcedures(); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getFunctions(null, null, "MY_PROCEDURE")) |
|
|
|
|
|
|
|
.willReturn(noFunction); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) |
|
|
|
|
|
|
|
.isThrownBy(() -> provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure")) |
|
|
|
|
|
|
|
.withMessageContaining("'MY_PROCEDURE'"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
InOrder inOrder = inOrder(this.databaseMetaData); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getUserName(); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getFunctions(null, null, "MY_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getDatabaseProductName(); |
|
|
|
|
|
|
|
verifyNoMoreInteractions(this.databaseMetaData); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void schemaNameWithPatternIsEscape() throws SQLException { |
|
|
|
void procedureNameWithExactMatch() throws SQLException { |
|
|
|
given(this.databaseMetaData.getSearchStringEscape()).willReturn("@"); |
|
|
|
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ResultSet myProcedure = mockProcedures(new Procedure(null, null, "MY_PROCEDURE")); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE")) |
|
|
|
|
|
|
|
.willReturn(myProcedure); |
|
|
|
|
|
|
|
ResultSet myProcedureColumn = mockProcedureColumns("TEST", DatabaseMetaData.procedureColumnIn); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getProcedureColumns(null, null, "MY_PROCEDURE", null)) |
|
|
|
|
|
|
|
.willReturn(myProcedureColumn); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure"); |
|
|
|
|
|
|
|
assertThat(provider.getCallParameterMetaData()).singleElement().satisfies(callParameterMetaData -> { |
|
|
|
|
|
|
|
assertThat(callParameterMetaData.getParameterName()).isEqualTo("TEST"); |
|
|
|
|
|
|
|
assertThat(callParameterMetaData.getParameterType()).isEqualTo(DatabaseMetaData.procedureColumnIn); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
InOrder inOrder = inOrder(this.databaseMetaData); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getUserName(); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getProcedureColumns(null, null, "MY_PROCEDURE", null); |
|
|
|
|
|
|
|
verifyNoMoreInteractions(this.databaseMetaData); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
void procedureNameWithSeveralMatchesFallBackOnEscaped() throws SQLException { |
|
|
|
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData); |
|
|
|
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData); |
|
|
|
given(this.databaseMetaData.getProcedures(null, "MY@_SCHEMA", "TEST")) |
|
|
|
|
|
|
|
.willThrow(new IllegalStateException("Expected")); |
|
|
|
given(this.databaseMetaData.getSearchStringEscape()).willReturn("@"); |
|
|
|
assertThatIllegalStateException().isThrownBy(() -> provider.initializeWithProcedureColumnMetaData( |
|
|
|
ResultSet myProcedures = mockProcedures(new Procedure(null, null, "MY_PROCEDURE"), |
|
|
|
this.databaseMetaData, null, "my_schema", "test")); |
|
|
|
new Procedure(null, null, "MYBPROCEDURE")); |
|
|
|
verify(this.databaseMetaData).getProcedures(null, "MY@_SCHEMA", "TEST"); |
|
|
|
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE")) |
|
|
|
|
|
|
|
.willReturn(myProcedures); |
|
|
|
|
|
|
|
ResultSet myProcedureEscaped = mockProcedures(new Procedure(null, null, "MY@_PROCEDURE")); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getProcedures(null, null, "MY@_PROCEDURE")) |
|
|
|
|
|
|
|
.willReturn(myProcedureEscaped); |
|
|
|
|
|
|
|
ResultSet myProcedureColumn = mockProcedureColumns("TEST", DatabaseMetaData.procedureColumnIn); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getProcedureColumns(null, null, "MY@_PROCEDURE", null)) |
|
|
|
|
|
|
|
.willReturn(myProcedureColumn); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure"); |
|
|
|
|
|
|
|
assertThat(provider.getCallParameterMetaData()).singleElement().satisfies(callParameterMetaData -> { |
|
|
|
|
|
|
|
assertThat(callParameterMetaData.getParameterName()).isEqualTo("TEST"); |
|
|
|
|
|
|
|
assertThat(callParameterMetaData.getParameterType()).isEqualTo(DatabaseMetaData.procedureColumnIn); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
InOrder inOrder = inOrder(this.databaseMetaData); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getUserName(); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getSearchStringEscape(); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY@_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getProcedureColumns(null, null, "MY@_PROCEDURE", null); |
|
|
|
|
|
|
|
verifyNoMoreInteractions(this.databaseMetaData); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
void nameIsNotEscapedIfEscapeCharacterIsNotAvailable() throws SQLException { |
|
|
|
void procedureNameWithSeveralMatchesDoesNotFallBackOnEscapedIfEscapeCharacterIsNotAvailable() throws SQLException { |
|
|
|
given(this.databaseMetaData.getSearchStringEscape()).willReturn(null); |
|
|
|
given(this.databaseMetaData.getSearchStringEscape()).willReturn(null); |
|
|
|
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData); |
|
|
|
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData); |
|
|
|
given(this.databaseMetaData.getProcedures(null, "MY_SCHEMA", "MY_TEST")) |
|
|
|
|
|
|
|
.willThrow(new IllegalStateException("Expected")); |
|
|
|
ResultSet myProcedures = mockProcedures(new Procedure(null, null, "MY_PROCEDURE"), |
|
|
|
assertThatIllegalStateException().isThrownBy(() -> provider.initializeWithProcedureColumnMetaData( |
|
|
|
new Procedure(null, null, "MYBPROCEDURE")); |
|
|
|
this.databaseMetaData, null, "my_schema", "my_test")); |
|
|
|
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE")) |
|
|
|
verify(this.databaseMetaData).getProcedures(null, "MY_SCHEMA", "MY_TEST"); |
|
|
|
.willReturn(myProcedures); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class) |
|
|
|
|
|
|
|
.isThrownBy(() -> provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure")) |
|
|
|
|
|
|
|
.withMessageContainingAll("'MY_PROCEDURE'", "null.null.MY_PROCEDURE", "null.null.MYBPROCEDURE"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
InOrder inOrder = inOrder(this.databaseMetaData); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getUserName(); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getSearchStringEscape(); |
|
|
|
|
|
|
|
verifyNoMoreInteractions(this.databaseMetaData); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
void procedureNameWitNoMatchFallbackOnFunction() throws SQLException { |
|
|
|
|
|
|
|
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
given(this.databaseMetaData.getSearchStringEscape()).willReturn("@"); |
|
|
|
|
|
|
|
ResultSet noProcedure = mockProcedures(); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE")) |
|
|
|
|
|
|
|
.willReturn(noProcedure); |
|
|
|
|
|
|
|
ResultSet noProcedureWithEscaped = mockProcedures(); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getProcedures(null, null, "MY@_PROCEDURE")) |
|
|
|
|
|
|
|
.willReturn(noProcedureWithEscaped); |
|
|
|
|
|
|
|
ResultSet function = mockFunctions(new Procedure(null, null, "MY_PROCEDURE")); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getFunctions(null, null, "MY_PROCEDURE")) |
|
|
|
|
|
|
|
.willReturn(function); |
|
|
|
|
|
|
|
ResultSet myProcedureColumn = mockProcedureColumns("TEST", DatabaseMetaData.procedureColumnIn); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getFunctionColumns(null, null, "MY_PROCEDURE", null)) |
|
|
|
|
|
|
|
.willReturn(myProcedureColumn); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure"); |
|
|
|
|
|
|
|
assertThat(provider.getCallParameterMetaData()).singleElement().satisfies(callParameterMetaData -> { |
|
|
|
|
|
|
|
assertThat(callParameterMetaData.getParameterName()).isEqualTo("TEST"); |
|
|
|
|
|
|
|
assertThat(callParameterMetaData.getParameterType()).isEqualTo(DatabaseMetaData.procedureColumnIn); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
InOrder inOrder = inOrder(this.databaseMetaData); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getUserName(); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getFunctions(null, null, "MY_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getFunctionColumns(null, null, "MY_PROCEDURE", null); |
|
|
|
|
|
|
|
verifyNoMoreInteractions(this.databaseMetaData); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
void procedureNameWitNoMatchAndSeveralFunctionsFallbacksOnEscaped() throws SQLException { |
|
|
|
|
|
|
|
GenericCallMetaDataProvider provider = new GenericCallMetaDataProvider(this.databaseMetaData); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
given(this.databaseMetaData.getSearchStringEscape()).willReturn("@"); |
|
|
|
|
|
|
|
ResultSet noProcedure = mockProcedures(); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getProcedures(null, null, "MY_PROCEDURE")) |
|
|
|
|
|
|
|
.willReturn(noProcedure); |
|
|
|
|
|
|
|
ResultSet functions = mockFunctions(new Procedure(null, null, "MY_PROCEDURE"), |
|
|
|
|
|
|
|
new Procedure(null, null, "MYBPROCEDURE")); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getFunctions(null, null, "MY_PROCEDURE")) |
|
|
|
|
|
|
|
.willReturn(functions); |
|
|
|
|
|
|
|
ResultSet functionEscaped = mockFunctions(new Procedure(null, null, "MY@_PROCEDURE")); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getFunctions(null, null, "MY@_PROCEDURE")) |
|
|
|
|
|
|
|
.willReturn(functionEscaped); |
|
|
|
|
|
|
|
ResultSet myProcedureColumn = mockProcedureColumns("TEST", DatabaseMetaData.procedureColumnIn); |
|
|
|
|
|
|
|
given(this.databaseMetaData.getFunctionColumns(null, null, "MY@_PROCEDURE", null)) |
|
|
|
|
|
|
|
.willReturn(myProcedureColumn); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
provider.initializeWithProcedureColumnMetaData(this.databaseMetaData, null, null, "my_procedure"); |
|
|
|
|
|
|
|
assertThat(provider.getCallParameterMetaData()).singleElement().satisfies(callParameterMetaData -> { |
|
|
|
|
|
|
|
assertThat(callParameterMetaData.getParameterName()).isEqualTo("TEST"); |
|
|
|
|
|
|
|
assertThat(callParameterMetaData.getParameterType()).isEqualTo(DatabaseMetaData.procedureColumnIn); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
InOrder inOrder = inOrder(this.databaseMetaData); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getUserName(); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getProcedures(null, null, "MY_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getFunctions(null, null, "MY_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getSearchStringEscape(); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getFunctions(null, null, "MY@_PROCEDURE"); |
|
|
|
|
|
|
|
inOrder.verify(this.databaseMetaData).getFunctionColumns(null, null, "MY@_PROCEDURE", null); |
|
|
|
|
|
|
|
verifyNoMoreInteractions(this.databaseMetaData); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ResultSet mockProcedures(Procedure... procedures) { |
|
|
|
|
|
|
|
ResultSet resultSet = mock(ResultSet.class); |
|
|
|
|
|
|
|
List<Boolean> next = new ArrayList<>(); |
|
|
|
|
|
|
|
Arrays.stream(procedures).forEach(p -> next.add(true)); |
|
|
|
|
|
|
|
applyStrings(Arrays.stream(procedures).map(Procedure::catalog).toList(), (first, then) -> |
|
|
|
|
|
|
|
given(resultSet.getString("PROCEDURE_CAT")).willReturn(first, then)); |
|
|
|
|
|
|
|
applyStrings(Arrays.stream(procedures).map(Procedure::schema).toList(), (first, then) -> |
|
|
|
|
|
|
|
given(resultSet.getString("PROCEDURE_SCHEM")).willReturn(first, then)); |
|
|
|
|
|
|
|
applyStrings(Arrays.stream(procedures).map(Procedure::name).toList(), (first, then) -> |
|
|
|
|
|
|
|
given(resultSet.getString("PROCEDURE_NAME")).willReturn(first, then)); |
|
|
|
|
|
|
|
next.add(false); |
|
|
|
|
|
|
|
applyBooleans(next, (first, then) -> given(resultSet.next()).willReturn(first, then)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return resultSet; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ResultSet mockFunctions(Procedure... procedures) { |
|
|
|
|
|
|
|
ResultSet resultSet = mock(ResultSet.class); |
|
|
|
|
|
|
|
List<Boolean> next = new ArrayList<>(); |
|
|
|
|
|
|
|
Arrays.stream(procedures).forEach(p -> next.add(true)); |
|
|
|
|
|
|
|
applyStrings(Arrays.stream(procedures).map(Procedure::catalog).toList(), (first, then) -> |
|
|
|
|
|
|
|
given(resultSet.getString("FUNCTION_CAT")).willReturn(first, then)); |
|
|
|
|
|
|
|
applyStrings(Arrays.stream(procedures).map(Procedure::schema).toList(), (first, then) -> |
|
|
|
|
|
|
|
given(resultSet.getString("FUNCTION_SCHEM")).willReturn(first, then)); |
|
|
|
|
|
|
|
applyStrings(Arrays.stream(procedures).map(Procedure::name).toList(), (first, then) -> |
|
|
|
|
|
|
|
given(resultSet.getString("FUNCTION_NAME")).willReturn(first, then)); |
|
|
|
|
|
|
|
next.add(false); |
|
|
|
|
|
|
|
applyBooleans(next, (first, then) -> given(resultSet.next()).willReturn(first, then)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return resultSet; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ResultSet mockProcedureColumns(String columnName, int columnType) throws SQLException { |
|
|
|
|
|
|
|
ResultSet resultSet = mock(ResultSet.class); |
|
|
|
|
|
|
|
given(resultSet.next()).willReturn(true, false); |
|
|
|
|
|
|
|
given(resultSet.getString("COLUMN_NAME")).willReturn(columnName); |
|
|
|
|
|
|
|
given(resultSet.getInt("COLUMN_TYPE")).willReturn(columnType); |
|
|
|
|
|
|
|
return resultSet; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
record Procedure(@Nullable String catalog, @Nullable String schema, String name) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void applyBooleans(List<Boolean> content, ThrowingBiFunction<Boolean, Boolean[], Object> split) { |
|
|
|
|
|
|
|
apply(content, Boolean[]::new, split); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void applyStrings(List<String> content, ThrowingBiFunction<String, String[], Object> split) { |
|
|
|
|
|
|
|
apply(content, String[]::new, split); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private <T> void apply(List<T> content, IntFunction<T[]> generator, ThrowingBiFunction<T, T[], Object> split) { |
|
|
|
|
|
|
|
if (content.isEmpty()) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
T first = content.get(0); |
|
|
|
|
|
|
|
T[] array = content.subList(1, content.size()).toArray(generator); |
|
|
|
|
|
|
|
split.apply(first, array); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|