diff --git a/src/com/sap/piper/ConfigurationHelper.groovy b/src/com/sap/piper/ConfigurationHelper.groovy index 7d733adebb..7bbfaa5f71 100644 --- a/src/com/sap/piper/ConfigurationHelper.groovy +++ b/src/com/sap/piper/ConfigurationHelper.groovy @@ -127,9 +127,11 @@ class ConfigurationHelper implements Serializable { handleValidationFailures() MapUtils.traverse(config, { v -> (v instanceof GString) ? v.toString() : v }) if(config.verbose) step.echo "[${name}] Configuration: ${config}" - return config + return MapUtils.deepCopy(config) } + + /* private */ def getConfigPropertyNested(key) { return getConfigPropertyNested(config, key) } diff --git a/src/com/sap/piper/MapUtils.groovy b/src/com/sap/piper/MapUtils.groovy index 784a281e7a..d3cd329567 100644 --- a/src/com/sap/piper/MapUtils.groovy +++ b/src/com/sap/piper/MapUtils.groovy @@ -62,4 +62,45 @@ class MapUtils implements Serializable { } m.putAll(updates) } + + /* + * Provides a new map with the same content like the original map. + * Nested Collections and Maps are copied. Values with are not + * Collections/Maps are not copied/cloned. + * <paranoia>&/ltThe keys are also not copied/cloned, even if they are + * Maps or Collections;paranoia> + */ + static deepCopy(Map original) { + Map copy = [:] + for (def e : original.entrySet()) { + if(e.value == null) { + copy.put(e.key, e.value) + } else { + copy.put(e.key, deepCopy(e.value)) + } + } + copy + } + + /* private */ static deepCopy(Set original) { + Set copy = [] + for(def e : original) + copy << deepCopy(e) + copy + } + + /* private */ static deepCopy(List original) { + List copy = [] + for(def e : original) + copy << deepCopy(e) + copy + } + + /* + * In fact not a copy, but a catch all for everything not matching + * with the other signatures + */ + /* private */ static deepCopy(def original) { + original + } } diff --git a/test/groovy/com/sap/piper/MapUtilsTest.groovy b/test/groovy/com/sap/piper/MapUtilsTest.groovy index 61a06aaf8b..6c63ccd142 100644 --- a/test/groovy/com/sap/piper/MapUtilsTest.groovy +++ b/test/groovy/com/sap/piper/MapUtilsTest.groovy @@ -50,4 +50,35 @@ class MapUtilsTest { MapUtils.traverse(m, { s -> (s.startsWith('x')) ? "replaced" : s}) assert m == [a: 'replaced', m: [b: 'replaced', c: 'otherString']] } + + @Test + void testDeepCopy() { + + List l = ['a', 'b', 'c'] + + def original = [ + list: l, + set: (Set)['1', '2'], + nextLevel: [ + list: ['x', 'y'], + duplicate: l, + set: (Set)[9, 8, 7] + ] + ] + + def copy = MapUtils.deepCopy(original) + + assert ! copy.is(original) + assert ! copy.list.is(original.list) + assert ! copy.set.is(original.set) + assert ! copy.nextLevel.list.is(original.nextLevel.list) + assert ! copy.nextLevel.set.is(original.nextLevel.set) + assert ! copy.nextLevel.duplicate.is(original.nextLevel.duplicate) + + // Within the original identical list is used twice, but the + // assuption is that there are different lists in the copy. + assert ! copy.nextLevel.duplicate.is(copy.list) + + assert copy == original + } }