DCAE-D be initial commit
[sdc/dcae-d/dt-be-main.git] / dcaedt_catalog / commons / src / main / java / org / onap / sdc / dcae / catalog / commons / Recycler.java
1 package org.onap.sdc.dcae.catalog.commons;
2
3 import java.io.Reader;
4 import java.io.IOException;
5
6 import java.util.List;
7 import java.util.Map;
8 import java.util.HashMap;
9 import java.util.AbstractMap;
10 import java.util.Arrays;
11 import java.util.Iterator;
12 import java.util.Collections;
13 import java.util.Spliterators;
14 import java.util.stream.Stream;
15 import java.util.stream.StreamSupport;
16
17 import org.apache.commons.jxpath.Pointer;
18 import org.apache.commons.jxpath.JXPathContext;
19
20 import com.fasterxml.jackson.databind.ObjectMapper;
21 import com.fasterxml.jackson.core.type.TypeReference;
22
23 import org.onap.sdc.common.onaplog.OnapLoggerDebug;
24 import org.onap.sdc.common.onaplog.Enums.LogLevel;
25 import org.yaml.snakeyaml.Yaml;
26
27
28 /**
29  * Practically a copy of the Validator's service Recycler, minus the Spring framework aspects + picking up the
30  * description of every node
31  */
32 public class Recycler {
33
34     private static final String PROPERTIES = "properties";
35     private static final String VALUE = "value";
36     private static final String ASSIGNMENT = "assignment";
37     private static final String CAPABILITY = "capability";
38     private static final String RELATIONSHIP = "relationship";
39         private static final String NAME = "name";
40         private static OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance();
41     private List<Map>    imports;
42     private List<String> metas;
43
44     public Recycler() {
45         withImports();
46         withMetas(null);
47     }
48
49     public Recycler withImports(String... theImports) {
50         debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Setting imports to {}", theImports);
51         ListBuilder importsBuilder = new ListBuilder();
52         for (int i = 0; i < theImports.length; i++) {
53             importsBuilder.add(new MapBuilder()
54                     .put("i" + i, theImports[i])
55                     .build());
56         }
57         this.imports = importsBuilder.build();
58         return this;
59     }
60
61     private List imports() {
62         ListBuilder importsBuilder = new ListBuilder();
63         for (Map e: this.imports) {
64             importsBuilder.add(new MapBuilder()
65                     .putAll(e)
66                     .build());
67         }
68         return importsBuilder.build();
69     }
70
71     public Recycler withMetas(String... theMetas) {
72         this.metas = (theMetas == null) ? Collections.emptyList() : Arrays.asList(theMetas);
73         return this;
74     }
75
76     public Object recycle(final Reader theSource) throws Exception {
77         return this.recycle(new ObjectMapper().readValue(theSource, (Class)HashMap.class));
78     }
79     
80     public Object recycle(final Object theDump) {
81   
82         final JXPathContext jxroot = JXPathContext.newContext(theDump);
83         jxroot.setLenient(true);
84
85         final Map<String, Object> nodeTemplates =
86             (Map<String, Object>)new MapBuilder()
87                 .putAll(
88                         StreamSupport
89                             .stream(
90                                 Spliterators.spliteratorUnknownSize((Iterator<Pointer>)jxroot.iteratePointers("/nodes"), 16), false)
91                             .map(p -> {
92                                         JXPathContext jxnode = jxroot.getRelativeContext(p);
93                                         return new AbstractMap.SimpleEntry<String,Object>(
94                                             (String)jxnode.getValue(NAME) + "_" + (String)jxnode.getValue("nid"),
95                                             new MapBuilder()
96                                                     .put("type", jxnode.getValue("type/name"))
97                                                     .put("description", jxnode.getValue("description"))
98                                                     .putOpt("metadata", nodeMetadata(jxnode))
99                                                     .putOpt(PROPERTIES, nodeProperties(jxnode))
100                                                     .putOpt("requirements", nodeRequirements(jxnode))
101                                                     .putOpt("capabilities", nodeCapabilities(jxnode))
102                                                     .build());
103                             })::iterator)
104                 .buildOpt();
105
106             return new MapBuilder()
107                                     .put("tosca_definitions_version", "tosca_simple_yaml_1_0_0")
108                                     .put("imports", imports())
109                                     .put("topology_template", new MapBuilder()
110                                                                                             .putOpt("node_templates", nodeTemplates)
111                                                                                             .build())
112                                     .build();
113     }
114
115     /* */
116     private Object nodeProperties(JXPathContext theNodeContext) {
117         return
118             new MapBuilder()
119                 .putAll(
120                     StreamSupport.stream(
121                         Spliterators.spliteratorUnknownSize((Iterator<Map>)theNodeContext.iterate(PROPERTIES), 16), false)
122                                                     .map(m -> new AbstractMap.SimpleEntry(m.get(NAME), this.nodeProperty(m)))
123                                                     .filter(e -> e.getValue() != null)
124                             ::iterator)
125                 .buildOpt();
126     }
127     
128     /* */
129     private Object nodeProperty(final Map theSpec) {
130         Object value = theSpec.get(VALUE);
131         if (value == null) {
132             value = theSpec.get("default");
133             if (value == null) {
134                 /*final*/ Map assign = (Map)theSpec.get(ASSIGNMENT);
135                 if (assign != null) {
136                     value = assign.get(VALUE);
137                 }
138             }
139         }
140         String type = (String)theSpec.get("type");
141         if (value != null && type != null) {
142                         value = getValueByType(value, type);
143                 }
144         return value;
145     }
146
147         private Object getValueByType(Object value, String type) {
148         Object returnValue = null;
149                 try {
150             if ("map".equals(type) && !(value instanceof Map)) {
151                                 returnValue = new ObjectMapper().readValue(value.toString(), new TypeReference<Map>(){});
152             }
153             else if ("list".equals(type) && !(value instanceof List)) {
154                                 returnValue = new ObjectMapper().readValue(value.toString(), new TypeReference<List>(){});
155             }
156             else if ("integer".equals(type) && (value instanceof String)) {
157                                 returnValue = Integer.valueOf((String)value);
158             }
159             else if ("float".equals(type) && (value instanceof String)) {
160                                 returnValue = Double.valueOf((String)value); //double because that's how the yaml parser would encode it
161             }
162         }
163         catch (NumberFormatException nfx) {
164             debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Failed to process String representation {} of numeric data: {}", value, nfx);
165         }
166         catch (IOException iox) {
167             debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Failed to process {} representation of a collection: {}", value.getClass().getName(), iox);
168         }
169                 return returnValue;
170         }
171
172         /* */
173     private List nodeRequirements(JXPathContext theNodeContext) {
174         return
175             new ListBuilder()
176                 .addAll(
177                     StreamSupport.stream(
178                         Spliterators.spliteratorUnknownSize((Iterator<Map>)theNodeContext.iterate("requirements"), 16), false)
179                                                     .flatMap(m -> this.nodeRequirement(m, theNodeContext).stream())
180                     //nicer that the ListBuilder buy cannot handle the empty lists, i.e. it will generate empty requirement lists
181                     //                                                          .collect(Collectors.toList())
182                                                     .toArray())
183                 .buildOpt();
184     }
185
186     /*
187      * @param theSpec the requirement entry that appears within the node specification
188      * @param theNodeContext .. Should I pass the root context instead of assuming that the nodes context has it as parent?
189      * @return a List as one requirement (definition) could end up being instantiated multiple times
190      */
191     private List nodeRequirement(final Map theSpec, JXPathContext theNodeContext/*Iterator theTargets*/) {
192
193         final ListBuilder value = new ListBuilder();
194
195         final Map target = (Map)theSpec.get("target");
196         final Map capability = (Map)theSpec.get(CAPABILITY);
197         final Map relationship = (Map)theSpec.get(RELATIONSHIP);
198
199         //this are actual assignments
200         for (Iterator i = theNodeContext.getParentContext().iterate("/relations[@n2='" + theNodeContext.getValue("nid") + "']/meta[@p2='" + theSpec.get(NAME) +"']"); i.hasNext(); ) {
201
202            String targetNodeName = (String)((Map)i.next()).get("n1");
203
204            //make sure target exists
205            Map targetNode = (Map)theNodeContext.getParentContext().getValue("/nodes[@nid='" + targetNodeName + "']");
206            if (null == targetNode) {
207                 debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Relation points to non-existing node {}", targetNodeName);
208                 continue; //this risks of producing a partial template ..
209            }
210
211            value.add(new MapBuilder().put(theSpec.get(NAME), new MapBuilder()
212                        .putOpt("node", targetNode.get(NAME) + "_" + targetNode.get("nid"))
213                        .putOpt(CAPABILITY, capability == null ? null : capability.get(NAME))
214                        .putOpt(RELATIONSHIP, relationship == null ? null : relationship.get("type"))
215                        .build()).build());
216         }
217                 addTemporary(theSpec, theNodeContext, value, capability, relationship);
218
219                 if (value.isEmpty()) {
220            value.add(new MapBuilder().put(theSpec.get(NAME), new MapBuilder()
221                        .putOpt("node", target == null ? null : target.get(NAME) + "_" + target.get("nid"))
222                        .putOpt(CAPABILITY, capability == null ? null : capability.get(NAME))
223                        .putOpt(RELATIONSHIP, relationship == null ? null : relationship.get("type"))
224                        .build()).build());
225         }
226
227         return value.build();
228     }
229
230         private void addTemporary(Map theSpec, JXPathContext theNodeContext, ListBuilder value, Map capability, Map relationship) {
231                 //temporary
232                 for (Iterator i = theNodeContext.getParentContext().iterate("/relations[@n1='" + theNodeContext.getValue("nid") + "']/meta[@p1='" + theSpec.get(NAME) +"']"); i.hasNext(); ) {
233
234            String targetNodeName = (String)((Map)i.next()).get("n2");
235
236            Map targetNode = (Map)theNodeContext.getParentContext().getValue("/nodes[@nid='" + targetNodeName + "']");
237            //make sure target exists
238            if (null == targetNode) {
239                 debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Relation points to non-existing node {}", targetNode);
240                 continue; //this risks of producing a partial template ..
241            }
242
243            value.add(new MapBuilder().put(theSpec.get(NAME), new MapBuilder()
244                        .putOpt("node", targetNode.get(NAME) + "_" + targetNode.get("nid"))
245                        .putOpt(CAPABILITY, capability == null ? null : capability.get(NAME))
246                        .putOpt(RELATIONSHIP, relationship == null ? null : relationship.get("type"))
247                        .build()).build());
248         }
249                 //end temporary
250         }
251
252         /* */
253     private Map nodeCapabilities(JXPathContext theNodeContext) {
254         return
255             new MapBuilder()
256                 .putAll(
257                     StreamSupport.stream(
258                         Spliterators.spliteratorUnknownSize((Iterator<Map>)theNodeContext.iterate("capabilities"), 16), false)
259                                              .map(m -> this.nodeCapability(m))
260                                              .filter(c -> c != null)
261                                              ::iterator)
262                 .buildOpt();
263     }
264
265     /**
266      * this handles a capability assignment which only includes properties and attributes so unless there
267      * are any properties/attributes assignments we might not generate anything
268      */
269     private Map.Entry nodeCapability(final Map theSpec) {
270         List<Map> properties = (List<Map>) theSpec.get(PROPERTIES);
271         if (properties == null || properties.isEmpty()) {
272             return null;
273         }
274
275         return new AbstractMap.SimpleEntry(theSpec.get(NAME),
276                 new MapBuilder()
277                         .put(PROPERTIES,
278                                 new MapBuilder().putAll(properties.stream()
279                                         .filter(p -> p.containsKey(ASSIGNMENT) ||
280                                                 p.containsKey(VALUE))
281                                         .map(p -> new AbstractMap.SimpleEntry(
282                                                 p.get(NAME),
283                                                 p.containsKey(ASSIGNMENT) ?
284                                             ((Map) p.get(ASSIGNMENT)).get(VALUE)
285                                                         : p.get(VALUE))
286                                                 )
287                                         ::iterator)
288                                 .build())
289                         .build());
290     }
291
292
293     /* */
294     private Object nodeMetadata(JXPathContext theNodeContext) {
295         return
296             new MapBuilder()
297                 .putAll(
298                         this.metas
299                             .stream()
300                             .flatMap(m -> {
301                                                 Object v = theNodeContext.getValue(m);
302                                                 if (v == null) {
303                                                     return Stream.empty();
304                                                 }
305                                                 if (v instanceof Map) {
306                                                     return ((Map) v).entrySet()
307                                                             .stream()
308                                                             .map(e -> new AbstractMap.SimpleEntry<String, Object>
309                                                                     (((Map.Entry) e).getKey().toString(),
310                                                                             ((Map.Entry) e).getValue().toString()));
311                                                 }
312                                                 return Stream.of(new AbstractMap.SimpleEntry<String,Object>(m, v.toString()));
313                                             })
314                         ::iterator)
315                 .buildOpt();
316     }
317
318
319     public static String toString(Object theVal) {
320         return new Yaml().dump(theVal);
321     }
322   
323
324     public static void main(String[] theArgs) throws Exception {
325         debugLogger.log(LogLevel.DEBUG, Recycler.class.getName(),
326                 Recycler.toString(
327                 new Recycler().recycle(new java.io.FileReader(theArgs[0]))));
328     }
329 }