[jOOQ/jOOQ#6598] Implementation for nested immutable POJOs

This commit is contained in:
Lukas Eder 2020-03-26 11:19:36 +01:00
parent 4d119f3442
commit 9112a4ff20

View File

@ -72,9 +72,11 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -288,6 +290,12 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
*/
private RecordMapper<R, E> delegate;
/**
* A set of field name prefixes that may defined the behaviour of nested
* record mappers.
*/
private transient Set<String> prefixes;
/**
* Create a new <code>DefaultRecordMapper</code>.
* <p>
@ -447,7 +455,7 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
Class<?>[] parameterTypes = constructor.getParameterTypes();
// Match the first constructor by parameter length
if (parameterTypes.length == fields.length) {
if (parameterTypes.length == prefixes().size()) {
// [#4627] use parameter names from byte code if available
@ -875,24 +883,93 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
*/
private class ImmutablePOJOMapper implements RecordMapper<R, E> {
private final Constructor<E> constructor;
private final Class<?>[] parameterTypes;
private final Constructor<E> constructor;
private final Class<?>[] parameterTypes;
private final boolean nested;
private final int[] nonNestedIndexLookup;
private final List<Integer>[] nestedIndexLookup;
private final List<Field<?>>[] nestedMappedFields;
private final RecordMapper<Record, Object>[] nestedMappers;
public ImmutablePOJOMapper(Constructor<E> constructor, Class<?>[] parameterTypes) {
this.constructor = accessible(constructor);
this.parameterTypes = parameterTypes;
this.nestedMappedFields = new List[prefixes().size()];
this.nestedMappers = new RecordMapper[prefixes().size()];
this.nestedIndexLookup = new List[prefixes().size()];
this.nonNestedIndexLookup = new int[prefixes().size()];
int i = -1;
boolean hasNestedFields = false;
prefixLoop:
for (String prefix : prefixes()) {
++i;
for (int j = 0; j < fields.length; j++) {
if (fields[j].getName().equals(prefix)) {
nonNestedIndexLookup[i] = j;
continue prefixLoop;
}
else if (fields[j].getName().startsWith(prefix + ".")) {
hasNestedFields = true;
if (nestedMappedFields[i] == null)
nestedMappedFields[i] = new ArrayList<>();
if (nestedIndexLookup[i] == null)
nestedIndexLookup[i] = new ArrayList<>();
nestedMappedFields[i].add(field(
name(fields[j].getName().substring(prefix.length() + 1)),
fields[j].getDataType()
));
nestedIndexLookup[i].add(j);
}
}
if (nestedMappedFields[i] != null) {
nestedMappers[i] = configuration
.recordMapperProvider()
.provide((RecordType) new Fields<>(nestedMappedFields[i]), parameterTypes[i]);
}
}
this.nested = hasNestedFields;
}
@Override
public final E map(R record) {
try {
Object[] converted = Convert.convert(record.intoArray(), parameterTypes);
return constructor.newInstance(converted);
return constructor.newInstance(nested
? mapNested(record)
: Convert.convert(record.intoArray(), parameterTypes)
);
}
catch (Exception e) {
throw new MappingException("An error ocurred when mapping record to " + type, e);
}
}
private final Object[] mapNested(R record) {
Object[] converted = new Object[prefixes().size()];
for (int i = 0; i < converted.length; i++) {
if (nestedMappers[i] == null) {
converted[i] = record.get(nonNestedIndexLookup[i], parameterTypes[i]);
}
else {
RecordImplN rec = new RecordImplN(nestedMappedFields[i]);
for (int index = 0; index < nestedIndexLookup[i].size(); index++)
rec.set(index, record.get(nestedIndexLookup[i].get(index)));
converted[i] = nestedMappers[i].map(rec);
}
}
return converted;
}
}
/**
@ -992,4 +1069,18 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
return attachable;
}
private final Set<String> prefixes() {
if (prefixes == null) {
prefixes = new LinkedHashSet<>();
for (Field<?> field : fields) {
String name = field.getName();
int dot = name.indexOf('.');
prefixes.add(dot > -1 ? name.substring(0, dot) : name);
}
}
return prefixes;
}
}