1 package org.onap.sdc.dcae.checker;
3 import java.util.Iterator;
4 import java.util.Collection;
5 import java.util.Comparator;
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;
16 import java.util.stream.Collectors;
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;
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.
34 public class Catalog {
36 private static OnapLoggerError errLogger = OnapLoggerError.getInstance();
37 private static OnapLoggerDebug debugLogger = OnapLoggerDebug.getInstance();
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 ..
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
47 private Map<Target, EnumMap<Construct, Map<String,Map>>> templates =
48 new HashMap<Target, EnumMap<Construct, Map<String,Map>>>();
50 private Catalog parent;
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>());
70 public boolean addType(Construct theConstruct, String theName, Map theDef) {
71 if (hasType(theConstruct, theName)) {
74 getConstructTypes(theConstruct).put(theName, theDef);
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);
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);
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));
102 return constructTypes;
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));
117 /* this will iterate through the type hierarchy for the given type, included.
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");
135 public boolean isDerivedFrom(Construct theConstruct, String theType, String theBaseType) {
137 Iterator<Map.Entry<String,Map>> hierachyIterator =
138 hierarchy(theConstruct, theType);
139 while (hierachyIterator.hasNext()) {
140 Map.Entry<String,Map> typeDef = hierachyIterator.next();
142 if (typeDef.getKey().equals(theBaseType)) {
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
155 public Iterator<Map.Entry> facets(Construct theConstruct,
156 final Facet theFacet,
157 final String theName) {
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());
167 ? Collections.emptyIterator()
168 : m.entrySet().iterator();
173 new Predicate<Map.Entry>() {
174 Set insts = new HashSet();
175 public boolean apply(Map.Entry theEntry) {
176 return !insts.contains(theEntry.getKey());
182 //no need to specify a construct, only nodes can have requirements
183 public Iterator<Map.Entry> requirements(final String theName) {
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");
192 ? Collections.emptyIterator()
196 new Function<Map, Iterator<Map.Entry>> () {
197 public Iterator<Map.Entry> apply(Map theEntry) {
198 return theEntry.entrySet().iterator();
209 /* Example: find the definition of property 'port' of the node type
210 * tosca.nodes.Database (properties being a facet of the node construct)
212 * Note: the definition of a facet is cumulative, i.e. more specialized
213 * definitions contribute (by overwriting) to the
215 public Map getFacetDefinition(Construct theConstruct,
216 String theConstructTypeName,
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());
225 def = def == null ? fset.get(theName)
226 : mergeDefinitions(def, fset.get(theName));
232 public Map getRequirementDefinition(Construct theConstruct,
233 String theConstructTypeName,
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");
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();
248 debugLogger.log(LogLevel.DEBUG, this.getClass().getName(), "Avoiding adding requirment block since it doesn't exists on the template....");
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>());
265 templates.put(theTarget, targetTemplates);
267 return targetTemplates;
270 public Map<String,Map> getTargetTemplates(Target theTarget, Construct theConstruct) {
271 return getTemplates(theTarget).get(theConstruct);
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");
280 if (constructTemplates.containsKey(theName)) {
281 throw new CatalogException(theConstruct + " template '" + theName + "' re-declaration");
283 constructTemplates.put(theName, theDef);
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);
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);
300 public static Map mergeDefinitions(Map theAggregate, Map theIncrement) {
301 if (theIncrement == null)
304 for(Map.Entry e: (Set<Map.Entry>)theIncrement.entrySet()) {
305 theAggregate.putIfAbsent(e.getKey(), e.getValue());
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();
319 * theParent contains an 'include/import' statement pointing to the Target
321 public boolean addTarget(Target theTarget, Target theParent) {
322 boolean cataloged = targets.containsKey(theTarget.getLocation());
325 targets.put(theTarget.getLocation(), theTarget);
328 if (theParent != null) {
329 imports.put(theParent, theTarget, Boolean.TRUE);
335 public Target getTarget(URI theLocation) {
336 return targets.get(theLocation);
339 public Collection<Target> targets() {
340 return targets.values();
343 /* Targets that no other targets depend on */
344 public Collection<Target> topTargets() {
345 return targets.values()
347 .filter(t -> !imports.containsColumn(t))
348 .collect(Collectors.toList());
352 public String importString(Target theTarget) {
353 return importString(theTarget, " ");
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()) {
363 .append(p.getLocation())
365 .append(importString(p, thePrefix + " "));
367 //we only keep the positive relationships
369 return sb.toString();
373 private class TargetComparator implements Comparator<Target> {
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))
380 if (hasPath(theTargetOne, theTargetTwo))
386 public boolean hasPath(Target theStart, Target theEnd) {
387 Map<Target,Boolean> deps = imports.row(theStart);
388 if (deps.containsKey(theEnd))
390 for (Target dep: deps.keySet()) {
391 if (hasPath(dep, theEnd))
398 public Collection<Target> sortedTargets() {
399 List keys = new ArrayList(this.targets.values());
400 Collections.sort(keys, new TargetComparator());
404 public static void main(String[] theArgs) throws Exception {
406 Catalog cat = new Catalog();
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"));
413 cat.addTarget(a, null);
414 cat.addTarget(b, null);
415 cat.addTarget(c, null);
416 cat.addTarget(d, null);
423 for (Target t: cat.sortedTargets())
424 debugLogger.log(LogLevel.DEBUG, Catalog.class.getName(), t.toString());
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"));
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"));
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());