initial revision.
[pipelinelib] / src / org / wamblee / jenkins / pipelinelib / MyYaml.groovy
1 package org.wamblee.jenkins.pipelinelib
2
3 @Grab('org.yaml:snakeyaml:1.25')
4
5 import org.yaml.snakeyaml.Yaml
6 import org.yaml.snakeyaml.constructor.SafeConstructor
7
8 /*
9   Original reference:
10   https://github.com/OndraZizka/yaml-merge/blob/master/src/main/java/org/cobbzilla/util/yml/YmlMerger.java
11  */
12
13 class MyYaml {
14   private final Yaml parser
15
16   MyYaml() {
17     parser = new Yaml(new SafeConstructor())
18   }
19
20   String merge(List<String> yamls) {
21     Map<String, Object> mergedResult = new LinkedHashMap<String, Object>();
22     for (yaml in yamls) {
23       final Map<String, Object> yamlToMerge = parser.load(yaml)
24       // Merge into results map.
25       mergeStructures(mergedResult, yamlToMerge)
26     }
27     return parser.dump(mergedResult)
28   }
29
30   private static Object addToMergedResult(Map<String, Object> mergedResult, String key, Object yamlValue) {
31     return mergedResult.put(key, yamlValue)
32   }
33
34   private static IllegalArgumentException unknownValueType(String key, Object yamlValue) {
35     final String msg = "Cannot mergeYamlFiles element of unknown type: " + key + ": " + yamlValue.getClass().getName()
36     return new IllegalArgumentException(msg)
37   }
38
39   private void mergeLists(Map<String, Object> mergedResult, String key, Object yamlValue) {
40     if (!(yamlValue instanceof List && mergedResult.get(key) instanceof List)) {
41       throw new IllegalArgumentException("Cannot mergeYamlFiles a list with a non-list: " + key)
42     }
43
44     List<Object> originalList = (List<Object>) mergedResult.get(key)
45
46     // original implementation
47     // originalList.addAll((List<Object>) yamlValue)
48
49     // my implementation
50     // below is non-standard approach as I assume a key:value mapping called (name->value) to identify a Map
51     List<Object> yamlList = (List<Object>) yamlValue
52     Map<String, Object> originalCache = new LinkedHashMap<>()
53     String name
54     for (ori in originalList) {
55       if (ori instanceof Map) {
56         name = ori.get('name')
57         if (name) {
58           originalCache.put(name, ori)
59         }
60       }
61     }
62
63     def merged
64     for (item in yamlList) {
65       merged = false
66       if (item instanceof Map) {
67         name = item.get('name')
68         if (name && originalCache.containsKey(name)) {
69           mergeStructures((Map<String, Object>) originalCache.get(name), (Map<String, Object>) item)
70           merged = true
71         }
72       }
73       if (!merged) {
74         originalList.add(item)
75       }
76     }
77   }
78
79   private void mergeStructures(Map<String, Object> targetTree, Map<String, Object> sourceTree) {
80     if (sourceTree == null) return
81
82     for (String key : sourceTree.keySet()) {
83
84       Object yamlValue = sourceTree.get(key)
85       if (yamlValue == null) {
86         addToMergedResult(targetTree, key, yamlValue)
87         continue
88       }
89
90       Object existingValue = targetTree.get(key);
91       if (existingValue != null) {
92         if (yamlValue instanceof Map) {
93           if (existingValue instanceof Map) {
94             mergeStructures((Map<String, Object>) existingValue, (Map<String, Object>) yamlValue);
95           } else if (existingValue instanceof String) {
96             throw new IllegalArgumentException("Cannot mergeYamlFiles complex element into a simple element: " + key)
97           } else {
98             throw unknownValueType(key, yamlValue)
99           }
100         } else if (yamlValue instanceof List) {
101           mergeLists(targetTree, key, yamlValue)
102
103         } else if (yamlValue instanceof String
104             || yamlValue instanceof Boolean
105             || yamlValue instanceof Double
106             || yamlValue instanceof Integer) {
107
108           addToMergedResult(targetTree, key, yamlValue)
109
110         } else {
111           throw unknownValueType(key, yamlValue)
112         }
113
114       } else {
115         if (yamlValue instanceof Map
116             || yamlValue instanceof List
117             || yamlValue instanceof String
118             || yamlValue instanceof Boolean
119             || yamlValue instanceof Integer
120             || yamlValue instanceof Double) {
121
122           addToMergedResult(targetTree, key, yamlValue)
123         } else {
124           throw unknownValueType(key, yamlValue)
125         }
126       }
127     }
128   }
129 }
130