package org.sql2o;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
class ArrayParameters {
static class ArrayParameter implements Comparable<ArrayParameter> {
// the index of the parameter array
int parameterIndex;
// the number of parameters to put in the query placeholder
int parameterCount;
ArrayParameter(int parameterIndex, int parameterCount) {
this.parameterIndex = parameterIndex;
this.parameterCount = parameterCount;
}
@Override
public int compareTo(ArrayParameter o) {
return Integer.compare(parameterIndex, o.parameterIndex);
}
}
/**
* Update both the query and the parameter indexes to include the array parameters.
*/
static String updateQueryAndParametersIndexes(String parsedQuery,
Map<String, List<Integer>> parameterNamesToIndexes,
Map<String, Query.ParameterSetter> parameters,
boolean allowArrayParameters) {
List<ArrayParameter> arrayParametersSortedAsc = arrayParametersSortedAsc(parameterNamesToIndexes, parameters, allowArrayParameters);
if(arrayParametersSortedAsc.isEmpty()) {
return parsedQuery;
}
updateParameterNamesToIndexes(parameterNamesToIndexes, arrayParametersSortedAsc);
return updateQueryWithArrayParameters(parsedQuery, arrayParametersSortedAsc);
}
/**
* Update the indexes of each query parameter
*/
static Map<String, List<Integer>> updateParameterNamesToIndexes(Map<String, List<Integer>> parametersNameToIndex,
List<ArrayParameter> arrayParametersSortedAsc) {
for(Map.Entry<String, List<Integer>> parameterNameToIndexes : parametersNameToIndex.entrySet()) {
List<Integer> newParameterIndex = new ArrayList<>(parameterNameToIndexes.getValue().size());
for(Integer parameterIndex : parameterNameToIndexes.getValue()) {
newParameterIndex.add(computeNewIndex(parameterIndex, arrayParametersSortedAsc));
}
parameterNameToIndexes.setValue(newParameterIndex);
}
return parametersNameToIndex;
}
/**
* Compute the new index of a parameter given the index positions of the array parameters.
*/
static int computeNewIndex(int index, List<ArrayParameter> arrayParametersSortedAsc) {
int newIndex = index;
for(ArrayParameter arrayParameter : arrayParametersSortedAsc) {
if(index > arrayParameter.parameterIndex) {
newIndex = newIndex + arrayParameter.parameterCount - 1;
} else {
return newIndex;
}
}
return newIndex;
}
/**
* List all the array parameters that contains more that 1 parameters.
* Indeed, array parameter below 1 parameter will not change the text query nor the parameter indexes.
*/
private static List<ArrayParameter> arrayParametersSortedAsc(Map<String, List<Integer>> parameterNamesToIndexes,
Map<String, Query.ParameterSetter> parameters,
boolean allowArrayParameters) {
List<ArrayParameters.ArrayParameter> arrayParameters = new ArrayList<>();
for(Map.Entry<String, Query.ParameterSetter> parameter : parameters.entrySet()) {
if (parameter.getValue().parameterCount > 1) {
if (!allowArrayParameters) {
throw new Sql2oException("Array parameters are not allowed in batch mode");
}
for(int i : parameterNamesToIndexes.get(parameter.getKey())) {
arrayParameters.add(new ArrayParameters.ArrayParameter(i, parameter.getValue().parameterCount));
}
}
}
Collections.sort(arrayParameters);
return arrayParameters;
}
/**
* Change the query to replace ? at each arrayParametersSortedAsc.parameterIndex
* with ?,?,?.. multiple arrayParametersSortedAsc.parameterCount
*/
static String updateQueryWithArrayParameters(String parsedQuery, List<ArrayParameter> arrayParametersSortedAsc) {
if(arrayParametersSortedAsc.isEmpty()) {
return parsedQuery;
}
StringBuilder sb = new StringBuilder();
Iterator<ArrayParameter> parameterToReplaceIt = arrayParametersSortedAsc.iterator();
ArrayParameter nextParameterToReplace = parameterToReplaceIt.next();
// PreparedStatement index starts at 1
int currentIndex = 1;
for(char c : parsedQuery.toCharArray()) {
if(nextParameterToReplace != null && c == '?') {
if(currentIndex == nextParameterToReplace.parameterIndex) {
sb.append("?");
for(int i = 1; i < nextParameterToReplace.parameterCount; i++) {
sb.append(",?");
}
if(parameterToReplaceIt.hasNext()) {
nextParameterToReplace = parameterToReplaceIt.next();
} else {
nextParameterToReplace = null;
}
} else {
sb.append(c);
}
currentIndex++;
} else {
sb.append(c);
}
}
return sb.toString();
}
}