Fix checkstyle violations in sdc/jtosca
[sdc/sdc-tosca.git] / src / main / java / org / onap / sdc / toscaparser / api / functions / GetProperty.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.sdc.toscaparser.api.functions;
22
23 import org.onap.sdc.toscaparser.api.CapabilityAssignment;
24 import org.onap.sdc.toscaparser.api.NodeTemplate;
25 import org.onap.sdc.toscaparser.api.Property;
26 import org.onap.sdc.toscaparser.api.RelationshipTemplate;
27 import org.onap.sdc.toscaparser.api.RequirementAssignment;
28 import org.onap.sdc.toscaparser.api.TopologyTemplate;
29 import org.onap.sdc.toscaparser.api.common.JToscaValidationIssue;
30 import org.onap.sdc.toscaparser.api.elements.CapabilityTypeDef;
31 import org.onap.sdc.toscaparser.api.elements.EntityType;
32 import org.onap.sdc.toscaparser.api.elements.NodeType;
33 import org.onap.sdc.toscaparser.api.elements.PropertyDef;
34 import org.onap.sdc.toscaparser.api.elements.RelationshipType;
35 import org.onap.sdc.toscaparser.api.elements.StatefulEntityType;
36 import org.onap.sdc.toscaparser.api.utils.ThreadLocalsHolder;
37
38 import java.util.ArrayList;
39 import java.util.LinkedHashMap;
40
41 public class GetProperty extends Function {
42     // Get a property value of an entity defined in the same service template
43
44     // Arguments:
45
46     // * Node template name | SELF | HOST | SOURCE | TARGET.
47     // * Requirement or capability name (optional).
48     // * Property name.
49
50     // If requirement or capability name is specified, the behavior is as follows:
51     // The req or cap name is first looked up in the specified node template's
52     // requirements.
53     // If found, it would search for a matching capability
54     // of an other node template and get its property as specified in function
55     // arguments.
56     // Otherwise, the req or cap name would be looked up in the specified
57     // node template's capabilities and if found, it would return  the property of
58     // the capability as specified in function arguments.
59
60     // Examples:
61
62     // * { get_property: [ mysql_server, port ] }
63     // * { get_property: [ SELF, db_port ] }
64     // * { get_property: [ SELF, database_endpoint, port ] }
65     // * { get_property: [ SELF, database_endpoint, port, 1 ] }
66
67
68     public GetProperty(TopologyTemplate ttpl, Object context, String name, ArrayList<Object> args) {
69         super(ttpl, context, name, args);
70     }
71
72     @Override
73     void validate() {
74         if (args.size() < 2) {
75             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE167",
76                     "ValueError: Illegal arguments for function \"get_property\". Expected arguments: \"node-template-name\", \"req-or-cap\" (optional), \"property name.\""));
77             return;
78         }
79         if (args.size() == 2) {
80             Property foundProp = _findProperty((String) args.get(1));
81             if (foundProp == null) {
82                 return;
83             }
84             Object prop = foundProp.getValue();
85             if (prop instanceof Function) {
86                 getFunction(toscaTpl, context, prop, toscaTpl.getResolveGetInput());
87             }
88         } else if (args.size() >= 3) {
89             // do not use _find_property to avoid raise KeyError
90             // if the prop is not found
91             // First check if there is property with this name
92             NodeTemplate nodeTpl = _findNodeTemplate((String) args.get(0));
93             LinkedHashMap<String, Property> props;
94             if (nodeTpl != null) {
95                 props = nodeTpl.getProperties();
96             } else {
97                 props = new LinkedHashMap<>();
98             }
99             int index = 2;
100             Object propertyValue;
101             if (props.get(args.get(1)) != null) {
102                 propertyValue = ((Property) props.get(args.get(1))).getValue();
103             } else {
104                 index = 3;
105                 // then check the req or caps
106                 propertyValue = _findReqOrCapProperty((String) args.get(1), (String) args.get(2));
107             }
108
109             if (args.size() > index) {
110                 for (Object elem : args.subList(index, args.size() - 1)) {
111                     if (propertyValue instanceof ArrayList) {
112                         int intElem = (int) elem;
113                         propertyValue = _getIndexValue(propertyValue, intElem);
114                     } else {
115                         propertyValue = _getAttributeValue(propertyValue, (String) elem);
116                     }
117                 }
118             }
119         }
120     }
121
122     @SuppressWarnings("unchecked")
123     private Object _findReqOrCapProperty(String reqOrCap, String propertyName) {
124         NodeTemplate nodeTpl = _findNodeTemplate((String) args.get(0));
125         if (nodeTpl == null) {
126             return null;
127         }
128         // look for property in node template's requirements
129         for (RequirementAssignment req : nodeTpl.getRequirements().getAll()) {
130             String nodeName = req.getNodeTemplateName();
131             if (req.getName().equals(reqOrCap)) {
132                 NodeTemplate nodeTemplate = _findNodeTemplate(nodeName);
133                 return _getCapabilityProperty(nodeTemplate, req.getName(), propertyName, true);
134             }
135         }
136         // If requirement was not found, look in node template's capabilities
137         return _getCapabilityProperty(nodeTpl, reqOrCap, propertyName, true);
138     }
139
140     private Object _getCapabilityProperty(NodeTemplate nodeTemplate,
141                                           String capabilityName,
142                                           String propertyName,
143                                           boolean throwErrors) {
144
145         // Gets a node template capability property
146         Object property = null;
147         CapabilityAssignment cap = nodeTemplate.getCapabilities().getCapabilityByName(capabilityName);
148         if (cap != null) {
149             LinkedHashMap<String, Property> props = cap.getProperties();
150             if (props != null && props.get(propertyName) != null) {
151                 property = ((Property) props.get(propertyName)).getValue();
152             }
153             if (property == null && throwErrors) {
154                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE168", String.format(
155                         "KeyError: Property \"%s\" was not found in capability \"%s\" of node template \"%s\" referenced from node template \"%s\"",
156                         propertyName, capabilityName, nodeTemplate.getName(), ((NodeTemplate) context).getName())));
157             }
158             return property;
159         }
160         if (throwErrors) {
161             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE169", String.format(
162                     "KeyError: Requirement/CapabilityAssignment \"%s\" referenced from node template \"%s\" was not found in node template \"%s\"",
163                     capabilityName, ((NodeTemplate) context).getName(), nodeTemplate.getName())));
164         }
165
166         return null;
167     }
168
169     private Property _findProperty(String propertyName) {
170         NodeTemplate nodeTpl = _findNodeTemplate((String) args.get(0));
171         if (nodeTpl == null) {
172             return null;
173         }
174         LinkedHashMap<String, Property> props = nodeTpl.getProperties();
175         Property found = props.get(propertyName);
176         if (found == null) {
177             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE170", String.format(
178                     "KeyError: Property \"%s\" was not found in node template \"%s\"",
179                     propertyName, nodeTpl.getName())));
180         }
181         return found;
182     }
183
184     private NodeTemplate _findNodeTemplate(String nodeTemplateName) {
185         if (nodeTemplateName.equals(SELF)) {
186             return (NodeTemplate) context;
187         }
188         // enable the HOST value in the function
189         if (nodeTemplateName.equals(HOST)) {
190             NodeTemplate node = _findHostContainingProperty(null);
191             if (node == null) {
192                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE171", String.format(
193                         "KeyError: Property \"%s\" was not found in capability \"%s\" of node template \"%s\" referenced from node template \"%s\"",
194                         (String) args.get(2), (String) args.get(1), ((NodeTemplate) context).getName())));
195                 return null;
196             }
197             return node;
198         }
199         if (nodeTemplateName.equals(TARGET)) {
200             if (!(((RelationshipTemplate) context).getTypeDefinition() instanceof RelationshipType)) {
201                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE172",
202                         "KeyError: \"TARGET\" keyword can only be used in context to \"Relationships\" target node"));
203                 return null;
204             }
205             return ((RelationshipTemplate) context).getTarget();
206         }
207         if (nodeTemplateName.equals(SOURCE)) {
208             if (!(((RelationshipTemplate) context).getTypeDefinition() instanceof RelationshipType)) {
209                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE173",
210                         "KeyError: \"SOURCE\" keyword can only be used in context to \"Relationships\" target node"));
211                 return null;
212             }
213             return ((RelationshipTemplate) context).getSource();
214         }
215         if (toscaTpl.getNodeTemplates() == null) {
216             return null;
217         }
218         for (NodeTemplate nodeTemplate : toscaTpl.getNodeTemplates()) {
219             if (nodeTemplate.getName().equals(nodeTemplateName)) {
220                 return nodeTemplate;
221             }
222         }
223         ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE174", String.format(
224                 "KeyError: Node template \"%s\" was not found. Referenced from Node Template \"%s\"",
225                 nodeTemplateName, ((NodeTemplate) context).getName())));
226
227         return null;
228     }
229
230     @SuppressWarnings("rawtypes")
231     private Object _getIndexValue(Object value, int index) {
232         if (value instanceof ArrayList) {
233             if (index < ((ArrayList) value).size()) {
234                 return ((ArrayList) value).get(index);
235             } else {
236                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE175", String.format(
237                         "KeyError: Property \"%s\" found in capability \"%s\" referenced from node template \"%s\" must have an element with index %d",
238                         args.get(2), args.get(1), ((NodeTemplate) context).getName(), index)));
239
240             }
241         } else {
242             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE176", String.format(
243                     "KeyError: Property \"%s\" found in capability \"%s\" referenced from node template \"%s\" must be a list",
244                     args.get(2), args.get(1), ((NodeTemplate) context).getName())));
245         }
246         return null;
247     }
248
249     @SuppressWarnings("unchecked")
250     private Object _getAttributeValue(Object value, String attribute) {
251         if (value instanceof LinkedHashMap) {
252             Object ov = ((LinkedHashMap<String, Object>) value).get(attribute);
253             if (ov != null) {
254                 return ov;
255             } else {
256                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE177", String.format(
257                         "KeyError: Property \"%s\" found in capability \"%s\" referenced from node template \"%s\" must have an attribute named \"%s\"",
258                         args.get(2), args.get(1), ((NodeTemplate) context).getName(), attribute)));
259             }
260         } else {
261             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE178", String.format(
262                     "KeyError: Property \"%s\" found in capability \"%s\" referenced from node template \"%s\" must be a dict",
263                     args.get(2), args.get(1), ((NodeTemplate) context).getName())));
264         }
265         return null;
266     }
267
268     // Add this functions similar to get_attribute case
269     private NodeTemplate _findHostContainingProperty(String nodeTemplateName) {
270         if (nodeTemplateName == null) {
271             nodeTemplateName = SELF;
272         }
273         NodeTemplate nodeTemplate = _findNodeTemplate(nodeTemplateName);
274         LinkedHashMap<String, Object> hostedOnRel = (LinkedHashMap<String, Object>)
275                 EntityType.TOSCA_DEF.get(HOSTED_ON);
276         for (RequirementAssignment requirement : nodeTemplate.getRequirements().getAll()) {
277             String targetName = requirement.getNodeTemplateName();
278             NodeTemplate targetNode = _findNodeTemplate(targetName);
279             NodeType targetType = (NodeType) targetNode.getTypeDefinition();
280             for (CapabilityTypeDef capDef : targetType.getCapabilitiesObjects()) {
281                 if (capDef.inheritsFrom((ArrayList<String>) hostedOnRel.get("valid_target_types"))) {
282                     if (_propertyExistsInType(targetType)) {
283                         return targetNode;
284                     }
285                     // If requirement was not found, look in node
286                     // template's capabilities
287                     if (args.size() > 2 &&
288                             _getCapabilityProperty(targetNode, (String) args.get(1), (String) args.get(2), false) != null) {
289                         return targetNode;
290                     }
291
292                     return _findHostContainingProperty(targetName);
293                 }
294             }
295
296         }
297         return null;
298     }
299
300     private boolean _propertyExistsInType(StatefulEntityType typeDefinition) {
301         LinkedHashMap<String, PropertyDef> propsDef = typeDefinition.getPropertiesDef();
302         return propsDef.keySet().contains((String) args.get(1));
303     }
304
305     @Override
306     public Object result() {
307         Object propertyValue;
308         if (args.size() >= 3) {
309             // First check if there is property with this name
310             NodeTemplate nodeTpl = _findNodeTemplate((String) args.get(0));
311             LinkedHashMap<String, Property> props;
312             if (nodeTpl != null) {
313                 props = nodeTpl.getProperties();
314             } else {
315                 props = new LinkedHashMap<>();
316             }
317             int index = 2;
318             if (props.get(args.get(1)) != null) {
319                 propertyValue = ((Property) props.get(args.get(1))).getValue();
320             } else {
321                 index = 3;
322                 // then check the req or caps
323                 propertyValue = _findReqOrCapProperty((String) args.get(1), (String) args.get(2));
324             }
325
326             if (args.size() > index) {
327                 for (Object elem : args.subList(index, args.size() - 1)) {
328                     if (propertyValue instanceof ArrayList) {
329                         int intElem = (int) elem;
330                         propertyValue = _getIndexValue(propertyValue, intElem);
331                     } else {
332                         propertyValue = _getAttributeValue(propertyValue, (String) elem);
333                     }
334                 }
335             }
336         } else {
337             propertyValue = _findProperty((String) args.get(1)).getValue();
338         }
339         if (propertyValue instanceof Function) {
340             return ((Function) propertyValue).result();
341         }
342         return getFunction(toscaTpl, context, propertyValue, toscaTpl.getResolveGetInput());
343     }
344
345     public String getNodeTemplateName() {
346         return (String) args.get(0);
347     }
348
349     public String getPropertyName() {
350         if (args.size() > 2) {
351             return (String) args.get(2);
352         }
353         return (String) args.get(1);
354     }
355
356     public String getReqorCap() {
357         if (args.size() > 2) {
358             return (String) args.get(1);
359         }
360         return null;
361     }
362
363 }
364
365 /*python
366
367 class GetProperty(Function):
368 """Get a property value of an entity defined in the same service template.
369
370 Arguments:
371
372 * Node template name | SELF | HOST | SOURCE | TARGET.
373 * Requirement or capability name (optional).
374 * Property name.
375
376 If requirement or capability name is specified, the behavior is as follows:
377 The req or cap name is first looked up in the specified node template's
378 requirements.
379 If found, it would search for a matching capability
380 of an other node template and get its property as specified in function
381 arguments.
382 Otherwise, the req or cap name would be looked up in the specified
383 node template's capabilities and if found, it would return  the property of
384 the capability as specified in function arguments.
385
386 Examples:
387
388 * { get_property: [ mysql_server, port ] }
389 * { get_property: [ SELF, db_port ] }
390 * { get_property: [ SELF, database_endpoint, port ] }
391 * { get_property: [ SELF, database_endpoint, port, 1 ] }
392 """
393
394 def validate(self):
395     if len(self.args) < 2:
396         ValidationIssueCollector.appendException(
397             ValueError(_(
398                 'Expected arguments: "node-template-name", "req-or-cap" '
399                 '(optional), "property name".')))
400         return
401     if len(self.args) == 2:
402         found_prop = self._find_property(self.args[1])
403         if not found_prop:
404             return
405         prop = found_prop.value
406         if not isinstance(prop, Function):
407             get_function(self.tosca_tpl, self.context, prop)
408     elif len(self.args) >= 3:
409         # do not use _find_property to avoid raise KeyError
410         # if the prop is not found
411         # First check if there is property with this name
412         node_tpl = self._find_node_template(self.args[0])
413         props = node_tpl.get_properties() if node_tpl else []
414         index = 2
415         found = [props[self.args[1]]] if self.args[1] in props else []
416         if found:
417             property_value = found[0].value
418         else:
419             index = 3
420             # then check the req or caps
421             property_value = self._find_req_or_cap_property(self.args[1],
422                                                             self.args[2])
423         if len(self.args) > index:
424             for elem in self.args[index:]:
425                 if isinstance(property_value, list):
426                     int_elem = int(elem)
427                     property_value = self._get_index_value(property_value,
428                                                            int_elem)
429                 else:
430                     property_value = self._get_attribute_value(
431                         property_value,
432                         elem)
433
434 def _find_req_or_cap_property(self, req_or_cap, property_name):
435     node_tpl = self._find_node_template(self.args[0])
436     # Find property in node template's requirements
437     for r in node_tpl.requirements:
438         for req, node_name in r.items():
439             if req == req_or_cap:
440                 node_template = self._find_node_template(node_name)
441                 return self._get_capability_property(
442                     node_template,
443                     req,
444                     property_name)
445     # If requirement was not found, look in node template's capabilities
446     return self._get_capability_property(node_tpl,
447                                          req_or_cap,
448                                          property_name)
449
450 def _get_capability_property(self,
451                              node_template,
452                              capability_name,
453                              property_name):
454     """Gets a node template capability property."""
455     caps = node_template.get_capabilities()
456     if caps and capability_name in caps.keys():
457         cap = caps[capability_name]
458         property = None
459         props = cap.get_properties()
460         if props and property_name in props.keys():
461             property = props[property_name].value
462         if not property:
463             ValidationIssueCollector.appendException(
464                 KeyError(_('Property "%(prop)s" was not found in '
465                            'capability "%(cap)s" of node template '
466                            '"%(ntpl1)s" referenced from node template '
467                            '"%(ntpl2)s".') % {'prop': property_name,
468                                               'cap': capability_name,
469                                               'ntpl1': node_template.name,
470                                               'ntpl2': self.context.name}))
471         return property
472     msg = _('Requirement/CapabilityAssignment "{0}" referenced from node template '
473             '"{1}" was not found in node template "{2}".').format(
474                 capability_name,
475                 self.context.name,
476                 node_template.name)
477     ValidationIssueCollector.appendException(KeyError(msg))
478
479 def _find_property(self, property_name):
480     node_tpl = self._find_node_template(self.args[0])
481     if not node_tpl:
482         return
483     props = node_tpl.get_properties()
484     found = [props[property_name]] if property_name in props else []
485     if len(found) == 0:
486         ValidationIssueCollector.appendException(
487             KeyError(_('Property "%(prop)s" was not found in node '
488                        'template "%(ntpl)s".') %
489                      {'prop': property_name,
490                       'ntpl': node_tpl.name}))
491         return None
492     return found[0]
493
494 def _find_node_template(self, node_template_name):
495     if node_template_name == SELF:
496         return self.context
497     # enable the HOST value in the function
498     if node_template_name == HOST:
499         return self._find_host_containing_property()
500     if node_template_name == TARGET:
501         if not isinstance(self.context.type_definition, RelationshipType):
502             ValidationIssueCollector.appendException(
503                 KeyError(_('"TARGET" keyword can only be used in context'
504                            ' to "Relationships" target node')))
505             return
506         return self.context.target
507     if node_template_name == SOURCE:
508         if not isinstance(self.context.type_definition, RelationshipType):
509             ValidationIssueCollector.appendException(
510                 KeyError(_('"SOURCE" keyword can only be used in context'
511                            ' to "Relationships" source node')))
512             return
513         return self.context.source
514     if not hasattr(self.tosca_tpl, 'nodetemplates'):
515         return
516     for node_template in self.tosca_tpl.nodetemplates:
517         if node_template.name == node_template_name:
518             return node_template
519     ValidationIssueCollector.appendException(
520         KeyError(_(
521             'Node template "{0}" was not found.'
522             ).format(node_template_name)))
523
524 def _get_index_value(self, value, index):
525     if isinstance(value, list):
526         if index < len(value):
527             return value[index]
528         else:
529             ValidationIssueCollector.appendException(
530                 KeyError(_(
531                     "Property '{0}' found in capability '{1}'"
532                     " referenced from node template {2}"
533                     " must have an element with index {3}.").
534                     format(self.args[2],
535                            self.args[1],
536                            self.context.name,
537                            index)))
538     else:
539         ValidationIssueCollector.appendException(
540             KeyError(_(
541                 "Property '{0}' found in capability '{1}'"
542                 " referenced from node template {2}"
543                 " must be a list.").format(self.args[2],
544                                            self.args[1],
545                                            self.context.name)))
546
547 def _get_attribute_value(self, value, attibute):
548     if isinstance(value, dict):
549         if attibute in value:
550             return value[attibute]
551         else:
552             ValidationIssueCollector.appendException(
553                 KeyError(_(
554                     "Property '{0}' found in capability '{1}'"
555                     " referenced from node template {2}"
556                     " must have an attribute named {3}.").
557                     format(self.args[2],
558                            self.args[1],
559                            self.context.name,
560                            attibute)))
561     else:
562         ValidationIssueCollector.appendException(
563             KeyError(_(
564                 "Property '{0}' found in capability '{1}'"
565                 " referenced from node template {2}"
566                 " must be a dict.").format(self.args[2],
567                                            self.args[1],
568                                            self.context.name)))
569
570 # Add this functions similar to get_attribute case
571 def _find_host_containing_property(self, node_template_name=SELF):
572     node_template = self._find_node_template(node_template_name)
573     hosted_on_rel = EntityType.TOSCA_DEF[HOSTED_ON]
574     for r in node_template.requirements:
575         for requirement, target_name in r.items():
576             target_node = self._find_node_template(target_name)
577             target_type = target_node.type_definition
578             for capability in target_type.get_capabilities_objects():
579                 if capability.type in hosted_on_rel['valid_target_types']:
580                     if self._property_exists_in_type(target_type):
581                         return target_node
582                     return self._find_host_containing_property(
583                         target_name)
584     return None
585
586 def _property_exists_in_type(self, type_definition):
587     props_def = type_definition.get_properties_def()
588     found = [props_def[self.args[1]]] \
589         if self.args[1] in props_def else []
590     return len(found) == 1
591
592 def result(self):
593     if len(self.args) >= 3:
594         # First check if there is property with this name
595         node_tpl = self._find_node_template(self.args[0])
596         props = node_tpl.get_properties() if node_tpl else []
597         index = 2
598         found = [props[self.args[1]]] if self.args[1] in props else []
599         if found:
600             property_value = found[0].value
601         else:
602             index = 3
603             # then check the req or caps
604             property_value = self._find_req_or_cap_property(self.args[1],
605                                                             self.args[2])
606         if len(self.args) > index:
607             for elem in self.args[index:]:
608                 if isinstance(property_value, list):
609                     int_elem = int(elem)
610                     property_value = self._get_index_value(property_value,
611                                                            int_elem)
612                 else:
613                     property_value = self._get_attribute_value(
614                         property_value,
615                         elem)
616     else:
617         property_value = self._find_property(self.args[1]).value
618     if isinstance(property_value, Function):
619         return property_value.result()
620     return get_function(self.tosca_tpl,
621                         self.context,
622                         property_value)
623
624 @property
625 def node_template_name(self):
626     return self.args[0]
627
628 @property
629 def property_name(self):
630     if len(self.args) > 2:
631         return self.args[2]
632     return self.args[1]
633
634 @property
635 def req_or_cap(self):
636     if len(self.args) > 2:
637         return self.args[1]
638     return None
639 */