Skip to content

Instantly share code, notes, and snippets.

@richardstephens
Created January 23, 2024 13:51
Show Gist options
  • Save richardstephens/fd6e8a59906431f7f16755cc98fe7363 to your computer and use it in GitHub Desktop.
Save richardstephens/fd6e8a59906431f7f16755cc98fe7363 to your computer and use it in GitHub Desktop.
Convert a string of output for an SQL query into an array of POJOs
package cloud.gallium.lib.testutils;
import lombok.SneakyThrows;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class RowStringsToClasses {
// This method converts a string of output from a database query
// into a list of objects of the given class.
//
// Expected input format:
// a | b | c
// ---+---+---
// 1 | 2 | 3
//
@SneakyThrows
public static <T> List<T> mockRows(String in, Class<T> clazz) {
var lines = in.lines().filter(l -> l.trim().length() > 0).toList();
var header =
Arrays.stream(lines.get(0).split("\\|"))
.map(String::trim)
.map(s -> toCamelCase(s))
.toList();
var rows = lines.subList(2, lines.size());
Constructor<T> constructorToUse = null;
for (var constructor : (Constructor<T>[]) clazz.getConstructors()) {
if (constructorToUse == null
|| constructor.getParameterCount() > constructorToUse.getParameterCount()) {
constructorToUse = constructor;
}
}
Map<String, Class> typeToUse = new HashMap<>();
Map<String, Integer> indexToUse = new HashMap<>();
var parameters = constructorToUse.getParameters();
for (var name : header) {
boolean found = false;
for (int idx = 0; idx < parameters.length; idx++) {
var p = parameters[idx];
if (p.getName().equals(name)) {
typeToUse.put(name, p.getType());
indexToUse.put(name, idx);
}
found = true;
}
if (!found) {
throw new IllegalArgumentException("No parameter found for " + name);
}
}
List<T> result = new ArrayList<>();
for (var row : rows) {
var rowParts = Arrays.stream(row.split("\\|")).map(String::trim).toList();
var args = new Object[constructorToUse.getParameterCount()];
for (int idx = 0; idx < header.size(); idx++) {
var name = header.get(idx);
var valueStr = rowParts.get(idx);
var type = typeToUse.get(name);
var index = indexToUse.get(name);
Object value = parseValueStr(valueStr, type);
args[index] = value;
}
result.add(constructorToUse.newInstance(args));
}
return result;
}
private static String toCamelCase(String s) {
String[] parts = s.split("_");
StringBuilder camelCaseString = new StringBuilder(parts[0].toLowerCase());
for (int i = 1; i < parts.length; i++) {
camelCaseString.append(parts[i].substring(0, 1).toUpperCase());
camelCaseString.append(parts[i].substring(1).toLowerCase());
}
return camelCaseString.toString();
}
private static Object parseValueStr(String valueStr, Class type) {
if (type == String.class) {
return valueStr;
} else if (type == UUID.class) {
return UUID.fromString(valueStr);
} else if (type == Integer.class) {
return Integer.parseInt(valueStr);
} else if (type == Long.class) {
return Long.parseLong(valueStr);
} else if (type == Boolean.class) {
if (valueStr.equals("t")) {
return true;
} else if (valueStr.equals("f")) {
return false;
} else {
throw new IllegalArgumentException("Unknown boolean value " + valueStr);
}
} else if (type == Double.class) {
return Double.parseDouble(valueStr);
} else if (type == Float.class) {
return Float.parseFloat(valueStr);
} else {
throw new IllegalArgumentException("Unknown type " + type);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment