Skip to content

Commit

Permalink
Reduce getter calls during fields mapping
Browse files Browse the repository at this point in the history
Related to orika-mapper#346

Signed-off-by: Oleksandr Porunov <[email protected]>
  • Loading branch information
porunov committed Apr 24, 2020
1 parent d01a6f8 commit ffcad68
Show file tree
Hide file tree
Showing 28 changed files with 655 additions and 214 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public MultiOccurrenceVariableRef(Type<?> type, String name) {
}

public String declareIterator() {
return declareIterator(getter());
}

public String declareIterator(String value) {
if (iteratorDeclared) {
throw new IllegalStateException("Iterator has already been declared");
}
Expand All @@ -76,7 +80,7 @@ public String declareIterator() {
} else if (isMap()) {
iterator = new EntrySetRef(this, name()).declareIterator();
} else {
iterator = "java.util.Iterator " + getIteratorName() + " = " + getter() + ".iterator()";
iterator = "java.util.Iterator " + getIteratorName() + " = " + value + ".iterator()";
}
iteratorDeclared = true;
return iterator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,20 @@ public void debugField(FieldMap fieldMap, String msg) {
logDetails.append(msg);
}
}

public String optionallyMakeDestinationVariableAndGetValue(StringBuilder out, VariableRef destination, boolean getDestinationOnMapping){

if(!getDestinationOnMapping){
return "null";
}

if(shouldCaptureFieldContext){
addVariable(out, destination);
return destination.getGlobalUniqueName();
}

return destination.asWrapper();
}

public String fieldTag(FieldMap fieldMap) {
return "\n\t Field(" + fieldMap.getSource() + ", " + fieldMap.getDestination() + ") : ";
Expand Down Expand Up @@ -354,7 +368,11 @@ public String newObjectFromMapper(Type<?> sourceType, Type<?> destinationType) {
* from a mapper
*/
public String newObjectFromMapper(VariableRef source, Type<?> destinationType) {
return newObjectFromMapper(source.type(), destinationType) + "(" + source.asWrapper() + ", mappingContext)";
return newObjectFromMapper(source, source.asWrapper(), destinationType);
}

public String newObjectFromMapper(VariableRef source, String sourceValue, Type<?> destinationType) {
return newObjectFromMapper(source.type(), destinationType) + "(" + sourceValue + ", mappingContext)";
}

/**
Expand All @@ -374,12 +392,16 @@ public String usedType(VariableRef r) {
* default value in cases of primitive types
*/
public String newObject(VariableRef source, Type<?> destinationType) {
return newObject(source, source.asWrapper(), destinationType);
}

public String newObject(VariableRef source, String sourceValue, Type<?> destinationType) {
if (destinationType.isPrimitive()) {
return VariableRef.getDefaultValue(destinationType.getRawType());
} else if (destinationType.isString()) {
return "null";
} else {
return newObjectFromMapper(source, destinationType);
return newObjectFromMapper(source, sourceValue, destinationType);
}
}

Expand Down Expand Up @@ -499,9 +521,13 @@ public static String join(List<?> list, String separator) {
* variable ref, which should be a Map type
*/
public static VariableRef entrySetRef(VariableRef s) {
return entrySetRef(s, s.toString());
}

public static VariableRef entrySetRef(VariableRef s, String value) {
@SuppressWarnings("unchecked")
Type<?> sourceEntryType = TypeFactory.valueOf(Set.class, MapEntry.entryType((Type<? extends Map<Object, Object>>) s.type()));
return new VariableRef(sourceEntryType, s + ".entrySet()");
return new VariableRef(sourceEntryType, value + ".entrySet()");
}

/**
Expand Down Expand Up @@ -751,18 +777,25 @@ public String mapFields(FieldMap fieldMap, VariableRef source, VariableRef desti
source.setConverter(converter);

boolean getDestinationOnMapping = AbstractSpecification.shouldGetDestinationOnMapping(fieldMap, this);
addVariable(out, source);
String sourceValue = source.getGlobalUniqueName();
String destinationValue = optionallyMakeDestinationVariableAndGetValue(out, destination, getDestinationOnMapping);

if (shouldCaptureFieldContext) {
beginCaptureFieldContext(out, fieldMap, source, destination, getDestinationOnMapping);
beginCaptureFieldContext(out, fieldMap, sourceValue, destinationValue);
}
StringBuilder filterClosing = new StringBuilder();
VariableRef[] filteredProperties = applyFilters(source, destination, out, filterClosing, getDestinationOnMapping);
source = filteredProperties[0];
VariableRef[] filteredProperties = applyFilters(source, destination, out, filterClosing, sourceValue, destinationValue);

if(filteredProperties[0] != source){
source = filteredProperties[0];
sourceValue = source.asWrapper();
}
destination = filteredProperties[1];

for (Specification spec : codeGenerationStrategy.getSpecifications()) {
if (spec.appliesTo(fieldMap)) {
String code = spec.generateMappingCode(fieldMap, source, destination, this);
String code = spec.generateMappingCode(fieldMap, source, sourceValue, destination, this);
if (code == null || "".equals(code)) {
throw new IllegalStateException("empty code returned for spec " + spec + ", sourceProperty = " + source
+ ", destinationProperty = " + destination);
Expand All @@ -781,10 +814,15 @@ public String mapFields(FieldMap fieldMap, VariableRef source, VariableRef desti
return out.toString();
}

private void beginCaptureFieldContext(StringBuilder out, FieldMap fieldMap, VariableRef source, VariableRef dest, boolean getDestinationValueOnMapping) {
private void beginCaptureFieldContext(StringBuilder out, FieldMap fieldMap, String sourceValue, String destinationValue) {
out.append(format("mappingContext.beginMappingField(\"%s\", %s, %s, \"%s\", %s, %s);\n" + "try{\n",
escapeQuotes(fieldMap.getSource().getExpression()), usedType(fieldMap.getAType()), source.asWrapper(),
escapeQuotes(fieldMap.getDestination().getExpression()), usedType(fieldMap.getBType()), (getDestinationValueOnMapping)?dest.asWrapper():"null"));
escapeQuotes(fieldMap.getSource().getExpression()), usedType(fieldMap.getAType()), sourceValue,
escapeQuotes(fieldMap.getDestination().getExpression()), usedType(fieldMap.getBType()), destinationValue));
}

public void addVariable(StringBuilder out, VariableRef variable){
String sourceVariableType = variable.isPrimitive()? variable.wrapperTypeName() : variable.typeName();
out.append(sourceVariableType).append(" ").append(variable.getGlobalUniqueName()).append(" = ").append(variable.asWrapper()).append(";\n");
}

private void endCaptureFieldContext(StringBuilder out) {
Expand All @@ -794,36 +832,42 @@ private void endCaptureFieldContext(StringBuilder out) {
private String escapeQuotes(String string) {
return string.replaceAll("(?<!\\\\)\"", "\\\\\"");
}

public VariableRef[] applyFilters(VariableRef sourceProperty, VariableRef destinationProperty, StringBuilder out, StringBuilder closing, boolean getDestinationOnMapping) {
return applyFilters(sourceProperty, destinationProperty, out, closing, sourceProperty.asWrapper(), (getDestinationOnMapping)?destinationProperty.asWrapper():"null");
}

public VariableRef[] applyFilters(VariableRef sourceProperty, VariableRef destinationProperty, StringBuilder out, StringBuilder closing,
String sourceValue, String destinationValue) {
/*
* TODO: need code which collects all of the applicable filters and adds
* them into an aggregate filter object
*/
Filter<Object, Object> filter = getFilter(sourceProperty, destinationProperty);
if (filter != null) {

if (destinationProperty.isNestedProperty()) {
out.append("if (");
out.append(format("(%s && %s.shouldMap(%s, \"%s\", %s, %s, \"%s\", %s, mappingContext))", destinationProperty.pathNotNull(),
usedFilter(filter), usedType(sourceProperty.type()), varPath(sourceProperty), sourceProperty.asWrapper(),
usedType(destinationProperty.type()), varPath(destinationProperty), (getDestinationOnMapping)?destinationProperty.asWrapper():"null"));
usedFilter(filter), usedType(sourceProperty.type()), varPath(sourceProperty), sourceValue,
usedType(destinationProperty.type()), varPath(destinationProperty), destinationValue));

out.append(" || ");

out.append(format("(%s && %s.shouldMap(%s, \"%s\", %s, %s, \"%s\", null, mappingContext))", destinationProperty.pathNull(),
usedFilter(filter), usedType(sourceProperty.type()), varPath(sourceProperty), sourceProperty.asWrapper(),
usedType(destinationProperty.type()), varPath(destinationProperty)));
usedFilter(filter), usedType(sourceProperty.type()), varPath(sourceProperty), sourceValue,
usedType(destinationProperty.type()), varPath(destinationProperty)));

out.append(") {");
} else {
out.append(format("if (%s.shouldMap(%s, \"%s\", %s, %s, \"%s\", %s, mappingContext)) {", usedFilter(filter),
usedType(sourceProperty.type()), varPath(sourceProperty), sourceProperty.asWrapper(),
usedType(destinationProperty.type()), varPath(destinationProperty), (getDestinationOnMapping)?destinationProperty.asWrapper():"null"));
usedType(sourceProperty.type()), varPath(sourceProperty), sourceValue,
usedType(destinationProperty.type()), varPath(destinationProperty), destinationValue));
}

sourceProperty = getSourceFilter(sourceProperty, destinationProperty, filter);
destinationProperty = getDestFilter(sourceProperty, destinationProperty, filter);

// need to set source property
closing.insert(0, "\n}\n");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,19 @@
public interface Specification extends BaseSpecification {

void setMapperFactory(MapperFactory mapperFactory);

/**
* Generates code for a boolean equality test between the two variable types,
* where are potentially unrelated.
*
*
* @param source
* @param destination
* @param code
* @return the code snippet which represents a true|false statement describing
* whether the two types should be considered 'equal'
*/
String generateEqualityTestCode(FieldMap fieldMap, VariableRef source, VariableRef destination, SourceCodeContext code);



/**
* Generates code to map the provided field map
*
Expand All @@ -55,5 +54,10 @@ public interface Specification extends BaseSpecification {
* @return the code snippet which represents mapping from the source to destination
* property
*/
String generateMappingCode(FieldMap fieldMap, VariableRef source, VariableRef destination, SourceCodeContext code);
default String generateMappingCode(FieldMap fieldMap, VariableRef source, VariableRef destination, SourceCodeContext code){
return generateMappingCode(fieldMap, source, source.toString(), destination, code);
}

String generateMappingCode(FieldMap fieldMap, VariableRef source, String sourceValue, VariableRef destination, SourceCodeContext code);

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import ma.glasnost.orika.Converter;
import ma.glasnost.orika.Filter;
Expand All @@ -52,6 +53,7 @@
*/
public class VariableRef {

private static final AtomicLong NEXT_GLOBAL_UNIQUE_ID = new AtomicLong(0L);
private static final Set<Class<?>> OPTIONAL_CLASSES = Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>());
static {
try {
Expand All @@ -65,7 +67,8 @@ public class VariableRef {
// No Guava Optional...
}
}


private final String globalUniqueName = "$"+Long.toHexString(NEXT_GLOBAL_UNIQUE_ID.getAndIncrement());
protected String name;
private Property property;
private VariableRef owner;
Expand Down Expand Up @@ -94,7 +97,18 @@ public VariableRef(Type<?> type, String name) {
this.nullPossible = !isPrimitive();
this.nullPathPossible = isNestedProperty();
}


public String getGlobalUniqueName() {
return globalUniqueName;
}

public String toRawValue(String value){
if (isPrimitive()) {
return "(" + "(" + rawType() + ")" + value + ")";
}
return value;
}

public void setConverter(Converter<?, ?> converter) {
this.converter = converter;
}
Expand Down Expand Up @@ -296,8 +310,12 @@ public String assignIfPossible(String value, Object... replacements) {
* @return
*/
public String assignIfPossible(VariableRef value) {
return assignIfPossible(value, value.toString());
}

public String assignIfPossible(VariableRef value, String castValue) {
if (setter() != null) {
return format(setter(), cast(value));
return format(setter(), cast(value, castValue));
} else {
return "";
}
Expand All @@ -311,8 +329,11 @@ public String assignIfPossible(VariableRef value) {
* @return
*/
public String assign(VariableRef value) {
return assign(value, value.toString());
}

public String assign(VariableRef value, String expr) {
if (setter() != null) {
String expr = value.toString();
if (value.type().isPrimitive() && type.isPrimitiveWrapper()) {
String wrapperClass = ClassUtil.getWrapperType(rawType()).getCanonicalName();
expr = format("(%s) %s.valueOf(%s)", wrapperClass, wrapperClass, expr);
Expand All @@ -336,7 +357,11 @@ public String assign(VariableRef value) {
}

public String cast(VariableRef ref) {
return cast(ref, type());
return cast(ref, ref.toString());
}

public String cast(VariableRef ref, String castValue) {
return cast(ref, type(), castValue);
}

/**
Expand Down Expand Up @@ -387,14 +412,17 @@ protected static String cast(String value, Type<?> type) {
* @return
*/
protected static String cast(VariableRef value, Type<?> type) {
String castValue = value.toString();
return cast(value, type, value.toString());
}

protected static String cast(VariableRef value, Type<?> type, String castValue) {
String typeName = type.getCanonicalName();

if (type.isPrimitive()) {
if (value.isWrapper()) {
castValue = format("%s.%sValue()", castValue, type);
} else if (Character.TYPE == type.getRawType() && value.type().isString()) {
castValue = format("%s.charAt(0)", value);
} else if (Character.TYPE == type.getRawType() && value.type().isString()) {
castValue = format("%s.charAt(0)", castValue);
} else if (!value.isPrimitive()) {
castValue = format("%s.valueOf(\"\"+%s).%sValue()", type.getWrapperType().getCanonicalName(), castValue, typeName);
}
Expand Down Expand Up @@ -530,10 +558,17 @@ private static String unwrap(String expression) {
}

private static String isNull(Property property, String name) {
if (property == null) {
return name + " == null";
}
return isNull(property, name, getGetter(property, name));
}

private static String isNull(Property property, String name, String value) {
if (property == null) {
return name + " == null";
} else {
String getterNull = getGetter(property, name) + " == null";
String getterNull = value + " == null";
if (property.isListElement()) {
return "(" + unwrap(getGetter(property, name)) + ".size() <= " + property.getName().replaceAll("[\\[\\]]", "") + " || "
+ getterNull + ")";
Expand Down Expand Up @@ -576,10 +611,14 @@ private String notNullIncludingPath() {

return path.toString();
}

public String notNull() {
return notNull(isNullPathPossible());
}

public String notNull(String value) {
return notNull(isNullPathPossible(), value);
}

public String notNull(boolean includePath) {
if (includePath) {
Expand All @@ -588,14 +627,30 @@ public String notNull(boolean includePath) {
return format("!(%s)", isNull(property, name));
}
}

public String notNull(boolean includePath, String value) {
if (includePath) {
return notNullIncludingPath();
} else {
return format("!(%s)", isNull(property, name, value));
}
}

public String ifNotNull() {
return ifNotNull(isNullPathPossible());
}


public String ifNotNull(String value) {
return ifNotNull(isNullPathPossible(), value);
}

public String ifNotNull(boolean includePath) {
return "if ( " + notNull(includePath) + ")";
}

public String ifNotNull(boolean includePath, String value) {
return "if ( " + notNull(includePath, value) + ")";
}

public String ifNull() {
return "if ( " + isNull() + ") ";
Expand Down
Loading

0 comments on commit ffcad68

Please sign in to comment.