From 084e3f95009300db1fc635148d55168c8c96c697 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Tue, 2 Apr 2024 19:32:07 +0200 Subject: [PATCH] Lazily create builders during transformations --- src/mdo/transformer.vm | 84 ++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/src/mdo/transformer.vm b/src/mdo/transformer.vm index ac6cd05f0b..04b6b707b8 100644 --- a/src/mdo/transformer.vm +++ b/src/mdo/transformer.vm @@ -41,6 +41,7 @@ import java.util.Properties; import java.util.Objects; import java.util.function.BinaryOperator; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.maven.api.annotations.Generated; @@ -82,21 +83,25 @@ public class ${className} { if (target == null) { return null; } - ${class.name}.Builder builder = ${class.name}.newBuilder(target); + Supplier<${class.name}.Builder> creator = () -> ${class.name}.newBuilder(target); + ${class.name}.Builder builder = null; #foreach ( $field in $allFields ) - transform${field.modelClass.name}_${Helper.capitalise($field.name)}(builder, target); + builder = (${class.name}.Builder) transform${field.modelClass.name}_${Helper.capitalise($field.name)}(creator, builder, target); #end - return builder.build(); + return builder != null ? builder.build() : target; } #foreach ( $field in $allFields ) #set ( $capField = ${Helper.capitalise($field.name)} ) - protected void transform${class.name}_${capField}(${class.name}.Builder builder, ${class.name} target) { + protected ${class.name}.Builder transform${class.name}_${capField}(Supplier creator, ${class.name}.Builder builder, ${class.name} target) { #if ( $field.type == "String" ) - String newVal = transform(target.get${capField}()); - builder.${field.name}(newVal != target.get${capField}() ? newVal : null); + String oldVal = target.get${capField}(); + String newVal = transform(oldVal); + return newVal != oldVal ? (builder != null ? builder : creator.get()).${field.name}(newVal) : builder; #elseif ( $field.type == "java.util.List" && $field.to == "String" && $field.multiplicity == "*" ) - builder.${field.name}(transform(target.get${capField}(), this::transform)); + List oldVal = target.get${capField}(); + List newVal = transform(oldVal, this::transform); + return newVal != oldVal ? (builder != null ? builder : creator.get()).${field.name}(newVal) : builder; #elseif ( $field.type == "java.util.Properties" && $field.to == "String" && $field.multiplicity == "*" ) Map props = target.get${capField}(); Map newProps = null; @@ -106,21 +111,28 @@ public class ${className} { if (newProps == null) { newProps = new HashMap<>(); newProps.putAll(props); + builder = builder != null ? builder : creator.get(); builder.${field.name}(newProps); } newProps.put(entry.getKey(), newVal); } } + return builder; #elseif ( $field.to && $field.multiplicity == "1" ) - ${field.to} newVal = transform${field.to}(target.get${capField}()); - builder.${field.name}(newVal != target.get${capField}() ? newVal : null); + ${field.to} oldVal = target.get${capField}(); + ${field.to} newVal = transform${field.to}(oldVal); + return newVal != oldVal ? (builder != null ? builder : creator.get()).${field.name}(newVal) : builder; #elseif ( $field.to && $field.multiplicity == "*" ) - builder.${field.name}(transform(target.get${capField}(), this::transform${field.to})); + List<${field.to}> oldVal = target.get${capField}(); + List<${field.to}> newVal = transform(oldVal, this::transform${field.to}); + return newVal != oldVal ? (builder != null ? builder : creator.get()).${field.name}(newVal) : builder; #elseif ( $field.type == "DOM" ) - XmlNode newVal = transform(target.get${capField}()); - builder.${field.name}(newVal != target.get${capField}() ? newVal : null); + XmlNode oldVal = target.get${capField}(); + XmlNode newVal = transform(oldVal); + return newVal != oldVal ? (builder != null ? builder : creator.get()).${field.name}(newVal) : builder; #elseif ( $field.type == "boolean" || $field.type == "int" || $field.type == "java.nio.file.Path" ) // nothing to do, the transformer only handles strings + return builder; #else // TODO: type=${field.type} to=${field.to} multiplicity=${field.multiplicity} #end @@ -130,11 +142,12 @@ public class ${className} { #end #end protected List transform(List list, Function transformer) { - List newList = null; + List newList = list; for (int i = 0; i < list.size(); i++) { - T newVal = transformer.apply(list.get(i)); - if (newVal != list.get(i)) { - if (newList == null) { + T oldVal = list.get(i); + T newVal = transformer.apply(oldVal); + if (newVal != oldVal) { + if (newList == list) { newList = new ArrayList<>(list); } newList.set(i, newVal); @@ -143,17 +156,34 @@ public class ${className} { return newList; } + protected Map transform(Map map, Function transformer) { + Map newMap = map; + for (String key : map.keySet()) { + T oldVal = map.get(key); + T newVal = transformer.apply(oldVal); + if (newVal != oldVal) { + if (newMap == map) { + newMap = new HashMap<>(map); + } + newMap.put(key, newVal); + } + } + return newMap; + } + protected XmlNode transform(XmlNode node) { - return node != null ? new XmlNodeImpl( - node.getPrefix(), - node.getNamespaceUri(), - node.getName(), - transform(node.getValue()), - node.getAttributes().entrySet() - .stream().collect(Collectors.toMap(e -> e.getKey(), e -> transform(e.getValue()))), - node.getChildren().stream() - .map(this::transform).collect(Collectors.toList()), - node.getInputLocation() - ) : null; + if (node != null) { + String oldValue = node.getValue(); + String newValue = transform(oldValue); + Map oldAttrs = node.getAttributes(); + Map newAttrs = transform(oldAttrs, this::transform); + List oldChildren = node.getChildren(); + List newChildren = transform(oldChildren, this::transform); + if (oldValue != newValue || oldAttrs != newAttrs || oldChildren != newChildren) { + return new XmlNodeImpl(node.getPrefix(), node.getNamespaceUri(), node.getName(), + newValue, newAttrs, newChildren, node.getInputLocation()); + } + } + return node; } }