DCAE-D be initial commit
[sdc/dcae-d/dt-be-main.git] / dcaedt_validator / checker / src / main / java / org / onap / sdc / dcae / checker / Catalog.java
1 package org.onap.sdc.dcae.checker;
2
3 import java.util.Iterator;
4 import java.util.Collection;
5 import java.util.Comparator;
6 import java.util.Set;
7 import java.util.Map;
8 import java.util.List;
9 import java.util.EnumMap;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.LinkedHashMap;
13 import java.util.ArrayList;
14 import java.util.Collections;
15
16 import java.util.stream.Collectors;
17
18 import java.net.URI;
19
20 import com.google.common.base.Predicate;
21 import com.google.common.base.Function;
22 import com.google.common.collect.Iterators;
23 import com.google.common.collect.Table;
24 import com.google.common.collect.HashBasedTable;
25 import org.onap.sdc.common.onaplog.OnapLoggerDebug;
26 import org.onap.sdc.common.onaplog.OnapLoggerError;
27 import org.onap.sdc.common.onaplog.Enums.LogLevel;
28
29 /*
30  * Oddball: tracking inputs as data templates could be seen as rather
31  * odd but we see them as instances of data types, in the same way node 
32  * templates are instances of node types.
33  */
34 public class Catalog {
35
36         private static OnapLoggerError errLogger = OnapLoggerError.getInstance();
37         private static OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance();
38
39    /* Type hierarchies are stored as maps from a type name to its definition
40     * Not the best but easy to follow hierarchies towards their root ..
41     */
42    private EnumMap<Construct, Map<String,Map>> types =
43                             new EnumMap<Construct, Map<String,Map>>(Construct.class);
44          /* track templates: we track templates (tye instances) first per target then per contruct.
45         * This allows us to share the catalog among multiple templates sharign the same type set
46     */
47    private Map<Target, EnumMap<Construct, Map<String,Map>>> templates =
48                             new HashMap<Target, EnumMap<Construct, Map<String,Map>>>();
49
50         private Catalog parent;
51
52         public Catalog(Catalog theParent) {
53                 this.parent = theParent;
54                 /* there are no requirement types, they are the same as capability types */
55                 types.put(Construct.Data, new LinkedHashMap<String, Map>());
56                 types.put(Construct.Capability, new LinkedHashMap<String, Map>());
57                 types.put(Construct.Relationship, new LinkedHashMap<String, Map>());
58                 types.put(Construct.Artifact, new LinkedHashMap<String, Map>());
59                 types.put(Construct.Interface, new LinkedHashMap<String, Map>());
60                 types.put(Construct.Node, new LinkedHashMap<String, Map>());
61                 types.put(Construct.Group, new LinkedHashMap<String, Map>());
62                 types.put(Construct.Policy, new LinkedHashMap<String, Map>());
63      
64         }
65         
66         public Catalog() {
67                 this(null);
68         }
69
70         public boolean addType(Construct theConstruct, String theName, Map theDef) {
71                 if (hasType(theConstruct, theName)) {
72                         return false;
73                 }
74                 getConstructTypes(theConstruct).put(theName, theDef);
75                 return true;
76   }
77
78         public Map getTypeDefinition(Construct theConstruct, String theName) {
79                 Map<String, Map> constructTypes = getConstructTypes(theConstruct);
80                 Map typeDef = constructTypes.get(theName);
81                 if (typeDef == null && this.parent != null) {
82                         return this.parent.getTypeDefinition(theConstruct, theName);
83                 }
84                 return typeDef;
85         }
86
87   public boolean hasType(Construct theConstruct, String theName) {
88                 Map<String, Map> constructTypes = getConstructTypes(theConstruct);
89                 boolean res = constructTypes.containsKey(theName);
90                 if (!res && this.parent != null) {
91                         res = this.parent.hasType(theConstruct, theName);
92                 }
93                 return res;
94         }
95
96         protected Map<String, Map> getConstructTypes(Construct theConstruct) {
97                 Map<String, Map> constructTypes = this.types.get(theConstruct);
98                 if (null == constructTypes) {
99                         throw new RuntimeException("Something worse is cooking here!",
100                                                         new CatalogException("No types for construct " + theConstruct));
101                 }
102                 return constructTypes;
103         }
104
105         protected Iterator<Map.Entry<String,Map>> 
106                                                                                         typesIterator(Construct theConstruct) {
107                 List<Map.Entry<String,Map>> constructTypes =
108                                                                                                 new ArrayList<Map.Entry<String,Map>>(
109                                                                                                         this.types.get(theConstruct).entrySet());
110                 Collections.reverse(constructTypes);
111                 return (this.parent == null)
112                                                         ? constructTypes.iterator()
113                                                         : Iterators.concat(constructTypes.iterator(),
114                                                                                                                                  this.parent.typesIterator(theConstruct));
115         }
116
117    /* this will iterate through the type hierarchy for the given type, included.
118     */
119         public Iterator<Map.Entry<String,Map>>
120                                                                                 hierarchy(Construct theConstruct, final String theName) {
121                 return Iterators.filter(typesIterator(theConstruct),
122                              new Predicate<Map.Entry<String,Map>>() {
123                                Object next = theName;
124                                public boolean apply(Map.Entry<String,Map> theEntry) {
125                                  if (next != null && next.equals(theEntry.getKey())) {
126                                    next = theEntry.getValue().get("derived_from");
127                                    return true;
128                                  }
129                                  else
130                                    return false;
131                                }
132                              });
133    }
134    
135          public boolean isDerivedFrom(Construct theConstruct, String theType, String theBaseType) {
136
137                 Iterator<Map.Entry<String,Map>> hierachyIterator = 
138                                                                                                                                                                         hierarchy(theConstruct, theType);
139                 while (hierachyIterator.hasNext())      {
140                         Map.Entry<String,Map> typeDef = hierachyIterator.next();
141
142                         if (typeDef.getKey().equals(theBaseType)) {
143                                 return true;
144                         }
145                 }
146                 return  false;
147          }
148
149         /* We go over the type hierarchy and retain only an iterator over the
150          * elements of the given facet for each type in the hierarchy.
151          * We concatenate these iterators and filter out duplicates.
152          * TODO: cannot just filter out duplicates - a redefinition can refine the one in the base construct so we
153          * should merge them!
154          */
155         public Iterator<Map.Entry> facets(Construct theConstruct,
156                                                                                                                                                 final Facet theFacet,
157                                                                                                                                                 final String theName) {
158                 return
159                         Iterators.filter(       
160                                 Iterators.concat(
161                                         Iterators.transform(
162                                                 hierarchy(theConstruct, theName),
163                                                 new Function<Map.Entry<String,Map>, Iterator<Map.Entry>>() {
164                                                         public Iterator<Map.Entry> apply(Map.Entry<String,Map> theEntry) {
165                                                                 Map m = (Map)theEntry.getValue().get(theFacet.name());
166                                                                 return m == null
167                                                                                                         ? Collections.emptyIterator()
168                                                                                                         : m.entrySet().iterator();
169                                                         }
170                                                 }                                                       
171                                         )
172                                 ),
173         new Predicate<Map.Entry>() {
174                                         Set insts = new HashSet();
175                                         public boolean apply(Map.Entry theEntry) {
176                                                 return !insts.contains(theEntry.getKey());
177                                         }
178                                 }       
179                         );
180         }
181         
182         //no need to specify a construct, only nodes can have requirements
183         public Iterator<Map.Entry> requirements(final String theName) {
184                 return
185                         Iterators.concat(
186                                 Iterators.transform(
187                                         hierarchy(Construct.Node, theName),
188                                         new Function<Map.Entry<String,Map>, Iterator<Map.Entry>>() {
189                                                 public Iterator<Map.Entry> apply(Map.Entry<String,Map> theEntry) {
190                                                         List<Map> l = (List<Map>)theEntry.getValue().get("requirements");
191                                                         return l == null
192                                                                                                 ? Collections.emptyIterator()
193                                                                                                 : Iterators.concat(
194                                                                                                                 Iterators.transform(
195                                                                                                                         l.iterator(),
196                                                                                                                         new Function<Map, Iterator<Map.Entry>> () {
197                                                                                                                                 public Iterator<Map.Entry> apply(Map theEntry) {
198                                                                                                                                         return theEntry.entrySet().iterator();
199                                                                                                                                 }
200                                                                                                                         }
201                                                                                                                 )       
202                                                                                                         );
203                                                         }
204                                                 }                                                       
205                                         )
206                                 );
207         }
208
209         /* Example: find the definition of property 'port' of the node type 
210          * tosca.nodes.Database (properties being a facet of the node construct)
211          *
212          * Note: the definition of a facet is cumulative, i.e. more specialized
213          * definitions contribute (by overwriting) to the 
214          */
215         public Map getFacetDefinition(Construct theConstruct,
216                                                                                                                                 String theConstructTypeName,
217                                                                                                                                 Facet theFacet,
218                                                                                                                                 String theName) {
219                 Map def = null;
220                 Iterator<Map.Entry<String,Map>> ti = hierarchy(theConstruct, theConstructTypeName);
221                 while (ti.hasNext()) {
222                         //this is where requirements would yield a List ..
223                         Map<String,Map> fset = (Map<String,Map>)ti.next().getValue().get(theFacet.name());
224                         if (fset != null) {
225                                 def = def == null ? fset.get(theName)
226                                                                                                         : mergeDefinitions(def, fset.get(theName));
227                         }
228                 }
229                 return def;
230         }
231
232         public Map getRequirementDefinition(Construct theConstruct,
233                                                                                                                                                         String theConstructTypeName,
234                                                                                                                                                         String theName) {
235                 Iterator<Map.Entry<String,Map>> ti = hierarchy(theConstruct, theConstructTypeName);
236                 while (ti.hasNext()) {
237                         //this is where requirements yield a List ..
238                         List<Map> reqs = (List<Map>)ti.next().getValue().get("requirements");
239                         
240                         if(reqs!=null){
241                                 for (Map req: reqs) {
242                                         Map.Entry reqe = (Map.Entry)req.entrySet().iterator().next();
243                                         if (theName.equals(reqe.getKey())) {
244                                                 return (Map)reqe.getValue();
245                                         }
246                                 }
247                         }else{
248                                 debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Avoiding adding requirment block since it doesn't exists on the template....");
249                         }
250                 }
251                 return null;
252         }
253
254   /* */
255   private EnumMap<Construct,Map<String,Map>> getTemplates(Target theTarget) {
256         EnumMap<Construct, Map<String,Map>> targetTemplates = templates.get(theTarget);
257                 if (targetTemplates == null) {
258                         targetTemplates = new EnumMap<Construct,Map<String,Map>>(Construct.class);
259                         targetTemplates.put(Construct.Data, new LinkedHashMap<String, Map>());
260                         targetTemplates.put(Construct.Relationship, new LinkedHashMap<String, Map>());
261                         targetTemplates.put(Construct.Node, new LinkedHashMap<String, Map>());
262                         targetTemplates.put(Construct.Group, new LinkedHashMap<String, Map>());
263                         targetTemplates.put(Construct.Policy, new LinkedHashMap<String, Map>());
264
265                         templates.put(theTarget, targetTemplates);
266                 }
267                 return targetTemplates;
268         }
269
270         public Map<String,Map> getTargetTemplates(Target theTarget, Construct theConstruct) {
271                 return getTemplates(theTarget).get(theConstruct);
272         }
273
274         public void addTemplate(Target theTarget, Construct theConstruct, String theName, Map theDef) 
275                                                                                                                                                                                                         throws CatalogException {
276         Map<String, Map> constructTemplates = getTargetTemplates(theTarget, theConstruct);
277                 if (null == constructTemplates) {
278       throw new CatalogException("No such thing as " + theConstruct + " templates");
279                 }
280     if (constructTemplates.containsKey(theName)) {
281       throw new CatalogException(theConstruct + " template '" + theName + "' re-declaration");
282     }
283     constructTemplates.put(theName, theDef);
284   }
285
286   public boolean hasTemplate(Target theTarget, Construct theConstruct, String theName) {
287     Map<String, Map> constructTemplates = getTargetTemplates(theTarget, theConstruct);
288     return constructTemplates != null && 
289                 constructTemplates.containsKey(theName);
290   }
291
292   public Map getTemplate(Target theTarget, Construct theConstruct, String theName) {
293     Map<String, Map> constructTemplates = getTargetTemplates(theTarget, theConstruct);
294     if (constructTemplates != null)
295                         return constructTemplates.get(theName);
296                 else
297                         return null;
298   }
299
300         public static Map mergeDefinitions(Map theAggregate, Map theIncrement) {
301                 if (theIncrement == null)
302                         return theAggregate;
303
304                 for(Map.Entry e: (Set<Map.Entry>)theIncrement.entrySet()) {
305                         theAggregate.putIfAbsent(e.getKey(), e.getValue());
306                 }
307                 return theAggregate;
308         }
309
310   /* tracks imports, i.e.targets */
311         private LinkedHashMap<URI, Target> targets = 
312                                                                                                                 new LinkedHashMap<URI, Target>();
313         /* tracks dependencies between targets, i.e. the 'adjency' matrix defined by
314          * the 'import' relationship */
315         private Table<Target,Target,Boolean> imports = HashBasedTable.create();
316
317
318         /*
319    * theParent contains an 'include/import' statement pointing to the Target
320    */
321         public boolean addTarget(Target theTarget, Target theParent) {
322                 boolean cataloged = targets.containsKey(theTarget.getLocation());
323                 
324                 if(!cataloged) {
325                          targets.put(theTarget.getLocation(), theTarget);
326                 }
327
328                 if (theParent != null) {
329                         imports.put(theParent, theTarget, Boolean.TRUE);
330                 }
331
332                 return !cataloged;
333         }
334
335         public Target getTarget(URI theLocation) {
336                 return targets.get(theLocation);
337         }
338
339         public Collection<Target> targets() {
340                 return targets.values();
341         }
342
343         /* Targets that no other targets depend on */
344         public Collection<Target> topTargets() {
345                 return targets.values()
346                                                 .stream()
347                                                         .filter(t -> !imports.containsColumn(t))
348                                                         .collect(Collectors.toList());
349                                                         
350         }
351         
352         public String importString(Target theTarget) {
353                 return importString(theTarget, "  ");
354         }
355
356         private String importString(Target theTarget, String thePrefix) {
357                 StringBuilder sb = new StringBuilder("");
358                 Map<Target,Boolean> parents = imports.column(theTarget);
359                 if (parents != null) {
360                         for (Target p: parents.keySet()) {
361                                 sb.append(thePrefix)
362                                         .append("from ")
363                                   .append(p.getLocation())
364                                         .append("\n")
365                                         .append(importString(p, thePrefix + "  "));
366                         }
367                         //we only keep the positive relationships
368                 }
369                 return sb.toString();
370         }
371
372         /* */
373         private class TargetComparator implements Comparator<Target> {
374         
375                 /* @return 1 if there is a dependency path from TargetOne to TargetTwo, -1 otherwise  */
376                 public int compare(Target theTargetOne, Target theTargetTwo) {
377                         if (hasPath(theTargetTwo, theTargetOne))
378                                 return -1;
379
380                         if (hasPath(theTargetOne, theTargetTwo))
381                                 return 1;
382
383                         return 0;
384                 }
385
386                 public boolean hasPath(Target theStart, Target theEnd) {
387                         Map<Target,Boolean> deps = imports.row(theStart);
388                         if (deps.containsKey(theEnd))
389                                 return true;
390                         for (Target dep: deps.keySet()) {
391                                 if (hasPath(dep, theEnd))
392                                         return true;
393                         }
394                         return false;
395                 }
396         }
397
398         public Collection<Target> sortedTargets() {
399                 List keys = new ArrayList(this.targets.values());
400                 Collections.sort(keys, new TargetComparator());
401                 return keys;            
402         }
403
404         public static void main(String[] theArgs) throws Exception {
405
406                 Catalog cat = new Catalog();
407
408                 Target a = new Target("a", new URI("a")),
409                                 b = new Target("b", new URI("b")),
410                                 c = new Target("c", new URI("c")),
411                                 d = new Target("d", new URI("d"));
412
413                 cat.addTarget(a, null);
414                 cat.addTarget(b, null);
415                 cat.addTarget(c, null);
416                 cat.addTarget(d, null);
417
418                 cat.addTarget(b, c);
419                 cat.addTarget(a, c);
420                 cat.addTarget(c, d);
421                 cat.addTarget(a, b);
422
423                 for (Target t: cat.sortedTargets())
424                         debugLogger.log(LogLevel.DEBUG, Catalog.class.getName(), t.toString());
425
426                 Catalog root = new Catalog();
427                 root.addType(Construct.Node, "_a", Collections.emptyMap());
428                 root.addType(Construct.Node, "__a", Collections.singletonMap("derived_from", "_a"));
429                 root.addType(Construct.Node, "___a", Collections.singletonMap("derived_from", "_a"));
430
431                 Catalog base = new Catalog(root);
432                 base.addType(Construct.Node, "_b", Collections.singletonMap("derived_from", "__a"));
433                 base.addType(Construct.Node, "__b", Collections.singletonMap("derived_from", "_b"));
434                 base.addType(Construct.Node, "__b_", Collections.singletonMap("derived_from", "_a"));
435
436                 if (theArgs.length > 0) {
437                         Iterator<Map.Entry<String, Map>> ti = 
438                                                                                                                                         base.hierarchy(Construct.Node, theArgs[0]);
439                         while (ti.hasNext()) {
440                 debugLogger.log(LogLevel.DEBUG, Catalog.class.getName(), "> {}", ti.next().getKey());
441                         }
442                 }
443         }
444 }