Fix PropertyConvertor initialization (use spring)
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / tosca / CapabilityRequirementConverter.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 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.openecomp.sdc.be.tosca;
22
23 import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
24 import static org.apache.commons.lang3.StringUtils.isBlank;
25 import static org.apache.commons.lang3.StringUtils.isNoneBlank;
26
27 import com.google.common.collect.Iterables;
28 import com.google.common.collect.Maps;
29 import fj.data.Either;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Optional;
37 import java.util.function.Function;
38 import java.util.stream.Collectors;
39 import org.apache.commons.collections.CollectionUtils;
40 import org.apache.commons.collections.MapUtils;
41 import org.apache.commons.lang3.StringUtils;
42 import org.apache.commons.lang3.tuple.ImmutablePair;
43 import org.openecomp.sdc.be.datatypes.elements.CapabilityDataDefinition;
44 import org.openecomp.sdc.be.datatypes.elements.RequirementDataDefinition;
45 import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
46 import org.openecomp.sdc.be.model.CapabilityDefinition;
47 import org.openecomp.sdc.be.model.Component;
48 import org.openecomp.sdc.be.model.ComponentInstance;
49 import org.openecomp.sdc.be.model.ComponentInstanceProperty;
50 import org.openecomp.sdc.be.model.ComponentParametersView;
51 import org.openecomp.sdc.be.model.DataTypeDefinition;
52 import org.openecomp.sdc.be.model.GroupDefinition;
53 import org.openecomp.sdc.be.model.PropertyDefinition;
54 import org.openecomp.sdc.be.model.RequirementDefinition;
55 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
56 import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter;
57 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
58 import org.openecomp.sdc.be.model.utils.ComponentUtilities;
59 import org.openecomp.sdc.be.tosca.ToscaUtils.SubstitutionEntry;
60 import org.openecomp.sdc.be.tosca.model.SubstitutionMapping;
61 import org.openecomp.sdc.be.tosca.model.ToscaCapability;
62 import org.openecomp.sdc.be.tosca.model.ToscaNodeTemplate;
63 import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
64 import org.openecomp.sdc.be.tosca.model.ToscaProperty;
65 import org.openecomp.sdc.be.tosca.model.ToscaRequirement;
66 import org.openecomp.sdc.be.tosca.model.ToscaTemplateCapability;
67 import org.openecomp.sdc.common.log.wrappers.Logger;
68 import org.springframework.beans.factory.annotation.Autowired;
69 import org.springframework.context.annotation.Scope;
70
71 /**
72  * Allows to convert requirements\capabilities of a component to requirements\capabilities of a substitution mappings section of a tosca template
73  *
74  */
75 @org.springframework.stereotype.Component("capabilty-requirement-convertor")
76 @Scope(value = "singleton")
77 public class CapabilityRequirementConverter {
78
79     private static final String NO_CAPABILITIES = "No Capabilities for node type";
80     private static final String NO_REQUIREMENTS = "No Requirements for node type";
81     private static CapabilityRequirementConverter instance;
82     private static final Logger logger = Logger.getLogger(CapabilityRequirementConverter.class);
83     private static final String PATH_DELIMITER = ".";
84     private static final String FAILED_TO_FIND_CI_IN_PATH ="Failed to find ci in the path is {} component {}";
85
86     @Autowired
87     private ToscaOperationFacade toscaOperationFacade;
88     @Autowired
89     private PropertyConvertor propertyConvertor;
90
91     public CapabilityRequirementConverter() {}
92
93     public static synchronized CapabilityRequirementConverter getInstance() {
94         if (instance == null) {
95             instance = new CapabilityRequirementConverter();
96         }
97         return instance;
98     }
99
100     public String buildCapabilityNameForComponentInstance( Map<String,Component> componentCache , ComponentInstance componentInstance, CapabilityDefinition c) {
101         String prefix = buildCapReqNamePrefix(componentInstance.getNormalizedName());
102         if(ComponentUtilities.isNotUpdatedCapReqName(prefix, c.getName(), c.getPreviousName())){
103             return buildSubstitutedName(componentCache, c.getName(), c.getPreviousName(), c.getPath(), c.getOwnerId(), componentInstance)
104                     .left()
105                     .orValue(c.getName());
106         }
107         return c.getPreviousName();
108     }
109
110     public String buildRequirementNameForComponentInstance(Map<String,Component> componentCache,
111                                                            ComponentInstance componentInstance,
112                                                            RequirementDefinition r) {
113         String prefix = buildCapReqNamePrefix(componentInstance.getNormalizedName());
114         if (ComponentUtilities.isNotUpdatedCapReqName(prefix, r.getName(), r.getPreviousName())) {
115             return buildSubstitutedName(componentCache, r.getName(), r.getPreviousName(), r.getPath(), r.getOwnerId(),
116                     componentInstance).left()
117                     .orValue(r.getName());
118         }
119         return r.getPreviousName();
120     }
121
122     private String buildCapReqNamePrefix(String normalizedName) {
123         return normalizedName + PATH_DELIMITER;
124     }
125
126     /**
127      * Allows to convert capabilities of a component to capabilities of a substitution mappings section of a tosca template
128      * @param componentInstance
129      * @param dataTypes
130      * @param nodeTemplate
131      * @return
132      */
133     public Either<ToscaNodeTemplate, ToscaError> convertComponentInstanceCapabilities(ComponentInstance componentInstance, Map<String, DataTypeDefinition> dataTypes, ToscaNodeTemplate nodeTemplate) {
134
135         Map<String, List<CapabilityDefinition>> capabilitiesInst = componentInstance.getCapabilities();
136         Map<String,Component> componentCache = new HashMap<>();
137         if (capabilitiesInst != null && !capabilitiesInst.isEmpty()) {
138             Map<String, ToscaTemplateCapability> capabilities = new HashMap<>();
139             capabilitiesInst.entrySet().forEach( e -> {
140                 List<CapabilityDefinition> capList = e.getValue();
141                 if ( capList != null && !capList.isEmpty() ) {
142                     capList.stream()
143                             .forEach( c -> convertOverridenProperties( componentInstance, dataTypes, capabilities, c ,
144                                 buildCapabilityNameForComponentInstance( componentCache , componentInstance , c )));
145                 }
146             });
147             if (MapUtils.isNotEmpty(capabilities)) {
148                 nodeTemplate.setCapabilities(capabilities);
149             }
150         }
151         return Either.left(nodeTemplate);
152     }
153
154     private void convertOverridenProperties(ComponentInstance componentInstance, Map<String, DataTypeDefinition> dataTypes, Map<String, ToscaTemplateCapability> capabilties, CapabilityDefinition c , String capabilityName) {
155         if (isNotEmpty(c.getProperties())) {
156             c.getProperties()
157             .stream()
158             .filter(p -> p.getValue() != null || p.getDefaultValue() != null)
159             .forEach(p -> convertOverriddenProperty(componentInstance, dataTypes, capabilties , p ,capabilityName));
160         }
161     }
162
163     private void convertOverriddenProperty(ComponentInstance componentInstance, Map<String, DataTypeDefinition> dataTypes, Map<String, ToscaTemplateCapability> capabilties, ComponentInstanceProperty p , String capabilityName) {
164         if (logger.isDebugEnabled()) {
165             logger.debug("Exist d property {} for capability {} with value {}", p.getName(), capabilityName, p.getValue());
166         }
167         ToscaTemplateCapability toscaTemplateCapability = capabilties.computeIfAbsent( capabilityName , key -> new ToscaTemplateCapability() );
168
169         Map<String, Object> toscaCapProp = toscaTemplateCapability.getProperties();
170         if (toscaCapProp == null) {
171             toscaCapProp = new HashMap<>();
172         }
173         Object convertedValue = convertInstanceProperty(dataTypes, componentInstance, p);
174         toscaCapProp.put(p.getName(), convertedValue);
175         toscaTemplateCapability.setProperties(toscaCapProp);
176     }
177
178     private Object convertInstanceProperty(Map<String, DataTypeDefinition> dataTypes, ComponentInstance componentInstance, ComponentInstanceProperty prop) {
179         logger.debug("Convert property {} for instance {}", prop.getName(), componentInstance.getUniqueId());
180         String propertyType = prop.getType();
181         String innerType = null;
182         if (prop.getSchema() != null && prop.getSchema().getProperty() != null) {
183             innerType = prop.getSchema().getProperty().getType();
184         }
185         String propValue = prop.getValue() == null ? prop.getDefaultValue() : prop.getValue();
186         return propertyConvertor.convertToToscaObject(prop, propValue, dataTypes, false);
187     }
188     /**
189      * Allows to convert requirements of a node type to tosca template requirements representation
190      * @param component
191      * @param nodeType
192      * @return
193      */
194     public Either<ToscaNodeType, ToscaError> convertRequirements(Map<String, Component> componentsCache, Component component, ToscaNodeType nodeType) {
195         List<Map<String, ToscaRequirement>> toscaRequirements = convertRequirementsAsList(componentsCache, component);
196         if (!toscaRequirements.isEmpty()) {
197             nodeType.setRequirements(toscaRequirements);
198         }
199         logger.debug("Finish convert Requirements for node type");
200
201         return Either.left(nodeType);
202     }
203
204     /**
205      * Allows to convert component requirements to the tosca template substitution mappings requirements
206      * @param componentsCache
207      * @param component
208      * @param substitutionMappings
209      * @return
210      */
211     public Either<SubstitutionMapping, ToscaError> convertSubstitutionMappingRequirements(Map<String,Component> componentsCache, Component component, SubstitutionMapping substitutionMappings) {
212         Either<SubstitutionMapping, ToscaError> result = Either.left(substitutionMappings);
213         Either<Map<String, String[]>, ToscaError> toscaRequirementsRes = convertSubstitutionMappingRequirementsAsMap(componentsCache, component);
214         if(toscaRequirementsRes.isRight()){
215             result = Either.right(toscaRequirementsRes.right().value());
216             logger.debug("Failed convert requirements for the component {}. ", component.getName());
217         } else if (MapUtils.isNotEmpty(toscaRequirementsRes.left().value())) {
218             substitutionMappings.setRequirements(toscaRequirementsRes.left().value());
219             result = Either.left(substitutionMappings);
220             logger.debug("Finish convert requirements for the component {}. ", component.getName());
221         }
222         return result;
223     }
224
225     /**
226      * Allows to convert requirements of a server proxy node type to tosca template requirements
227      * @param instanceProxy
228      * @return converted tosca template requirements
229      */
230     List<Map<String, ToscaRequirement>> convertProxyRequirements(Map<String, Component> componentCache,
231                                                                  ComponentInstance instanceProxy) {
232         Map<String, List<RequirementDefinition>> requirements = instanceProxy.getRequirements();
233         List<Map<String, ToscaRequirement>> toscaRequirements = new ArrayList<>();
234         if (requirements != null) {
235             requirements.entrySet().stream()
236                     .flatMap(e -> e.getValue().stream())
237                     .forEach(req -> {
238                 ImmutablePair<String, ToscaRequirement> pair = convertProxyRequirement(
239                         buildRequirementNameForComponentInstance(componentCache, instanceProxy, req), req);
240                 Map<String, ToscaRequirement> requirement = new HashMap<>();
241                 requirement.put(pair.left, pair.right);
242                 toscaRequirements.add(requirement);
243             });
244         } else {
245             logger.debug(NO_REQUIREMENTS);
246         }
247
248         return toscaRequirements;
249     }
250
251     private ImmutablePair<String, ToscaRequirement> convertProxyRequirement(String requirementName,
252                                                                             RequirementDefinition r) {
253         ToscaRequirement toscaRequirement = createToscaRequirement(r);
254         return new ImmutablePair<>(requirementName, toscaRequirement);
255     }
256
257     private List<Map<String, ToscaRequirement>> convertRequirementsAsList(Map<String, Component> componentsCache, Component component) {
258         Map<String, List<RequirementDefinition>> requirements = component.getRequirements();
259         List<Map<String, ToscaRequirement>> toscaRequirements = new ArrayList<>();
260         if (requirements != null) {
261             for (Map.Entry<String, List<RequirementDefinition>> entry : requirements.entrySet()) {
262                 entry.getValue().stream().filter(r -> filter(component, r.getOwnerId())).forEach(r -> {
263                     ImmutablePair<String, ToscaRequirement> pair = convertRequirement(componentsCache, component, ModelConverter.isAtomicComponent(component), r);
264                     Map<String, ToscaRequirement> requirement = new HashMap<>();
265
266                     requirement.put(pair.left, pair.right);
267                     toscaRequirements.add(requirement);
268                 });
269                 logger.debug("Finish convert Requirements for node type");
270             }
271         } else {
272             logger.debug(NO_REQUIREMENTS);
273         }
274         return toscaRequirements;
275     }
276
277     private boolean filter(Component component, String ownerId) {
278         return !ModelConverter.isAtomicComponent(component) || isNodeTypeOwner(component, ownerId) || (ModelConverter.isAtomicComponent(component) && ownerId == null);
279     }
280
281     private boolean isNodeTypeOwner(Component component, String ownerId) {
282         return ModelConverter.isAtomicComponent(component) && component.getUniqueId().equals(ownerId);
283     }
284
285     private String dropLast( String path, String delimiter ) {
286         if (isBlank(path) || isBlank(delimiter)){
287             return path;
288         }
289         return path.substring(0, path.lastIndexOf(delimiter));
290     }
291
292     private Either<Map<String, String[]>, ToscaError> convertSubstitutionMappingRequirementsAsMap(Map<String, Component> componentsCache, Component component) {
293         Map<String, List<RequirementDefinition>> requirements = component.getRequirements();
294         Either<Map<String, String[]>, ToscaError> result;
295         if (requirements != null) {
296             result = buildAddSubstitutionMappingsRequirements(componentsCache, component, requirements);
297         } else {
298             result = Either.left(Maps.newHashMap());
299             logger.debug("No requirements for substitution mappings section of a tosca template of the component {}. ", component.getName());
300         }
301         return result;
302     }
303
304     private Either<Map<String, String[]>, ToscaError> buildAddSubstitutionMappingsRequirements(Map<String, Component> componentsCache, Component component, Map<String, List<RequirementDefinition>> requirements) {
305
306         Map<String, String[]> toscaRequirements = new HashMap<>();
307         Either<Map<String, String[]>, ToscaError> result = null;
308         for (Map.Entry<String, List<RequirementDefinition>> entry : requirements.entrySet()) {
309             Optional<RequirementDefinition> failedToAddRequirement = entry.getValue()
310                     .stream()
311                     .filter(r->!addEntry(componentsCache, toscaRequirements, component, new SubstitutionEntry(r.getName(), r.getParentName(), ""), r.getPreviousName(), r.getOwnerId(), r.getPath()))
312                     .findAny();
313             if(failedToAddRequirement.isPresent()){
314                 logger.debug("Failed to convert requirement {} for substitution mappings section of a tosca template of the component {}. ",
315                         failedToAddRequirement.get().getName(), component.getName());
316                 result = Either.right(ToscaError.NODE_TYPE_REQUIREMENT_ERROR);
317             }
318             logger.debug("Finish convert requirements for the component {}. ", component.getName());
319         }
320         if(result == null){
321             result = Either.left(toscaRequirements);
322         }
323         return result;
324     }
325
326     private Either<Map<String, String[]>, ToscaError> buildAddSubstitutionMappingsCapabilities(Map<String, Component> componentsCache, Component component, Map<String, List<CapabilityDefinition>> capabilities) {
327
328         Map<String, String[]> toscaCapabilities = new HashMap<>();
329         Either<Map<String, String[]>, ToscaError> result = null;
330         for (Map.Entry<String, List<CapabilityDefinition>> entry : capabilities.entrySet()) {
331             Optional<CapabilityDefinition> failedToAddRequirement = entry.getValue()
332                     .stream()
333                     .filter(c->!addEntry(componentsCache, toscaCapabilities, component, new SubstitutionEntry(c.getName(), c.getParentName(), ""), c.getPreviousName(), c.getOwnerId(), c.getPath()))
334                     .findAny();
335             if(failedToAddRequirement.isPresent()){
336                 logger.debug("Failed to convert capability {} for substitution mappings section of a tosca template of the component {}. ",
337                         failedToAddRequirement.get().getName(), component.getName());
338                 result = Either.right(ToscaError.NODE_TYPE_CAPABILITY_ERROR);
339             }
340             logger.debug("Finish convert capabilities for the component {}. ", component.getName());
341         }
342         if(result == null){
343             result = Either.left(toscaCapabilities);
344         }
345         return result;
346     }
347
348     private boolean addEntry(Map<String, Component> componentsCache, Map<String, String[]> capReqMap, Component component, SubstitutionEntry entry, String previousName, String ownerId, List<String> path){
349     
350         if(shouldBuildSubstitutionName(component, path) && !buildSubstitutedNamePerInstance(componentsCache, component, entry.getFullName(), previousName, path, ownerId, entry)){
351             return false;
352         }
353         logger.debug("The requirement/capability {} belongs to the component {} ", entry.getFullName(), component.getUniqueId());
354         if (StringUtils.isNotEmpty(entry.getSourceName())) {
355             addEntry(capReqMap, component, path, entry);
356         }
357         logger.debug("Finish convert the requirement/capability {} for the component {}. ", entry.getFullName(), component.getName());
358         return true;
359
360     }
361
362     private boolean shouldBuildSubstitutionName(Component component, List<String> path) {
363         return ToscaUtils.isNotComplexVfc(component) && isNotEmpty(path) && path.iterator().hasNext();
364     }
365
366     private boolean buildSubstitutedNamePerInstance(Map<String, Component> componentsCache, Component component, String name, String previousName, List<String> path, String ownerId, SubstitutionEntry entry) {
367         String fullName;
368         String sourceName;
369         String prefix;
370         if(CollectionUtils.isNotEmpty(component.getGroups())) {
371             Optional<GroupDefinition> groupOpt = component.getGroups().stream().filter(g -> g.getUniqueId().equals(ownerId)).findFirst();
372             if (groupOpt.isPresent()) {
373                 prefix = buildCapReqNamePrefix(groupOpt.get().getNormalizedName());
374                 if(ComponentUtilities.isNotUpdatedCapReqName(prefix, name, previousName)){
375                     sourceName = name;
376                     fullName = prefix + sourceName;
377                 } else {
378                     sourceName = previousName;
379                     fullName = name;
380                 }
381                 entry.setFullName(fullName);
382                 entry.setSourceName(sourceName);
383                 entry.setOwner(groupOpt.get().getNormalizedName());
384                 return true;
385             }
386         }
387
388         Optional<ComponentInstance> ci =
389                 component.safeGetComponentInstances().stream().filter(c->c.getUniqueId().equals(Iterables.getLast(path))).findFirst();
390         if(!ci.isPresent()){
391             logger.debug(FAILED_TO_FIND_CI_IN_PATH, path, component.getUniqueId());
392
393             Collections.reverse(path);
394
395             logger.debug("try to reverse path {} component {}", path, component.getUniqueId());
396             ci = component.safeGetComponentInstances().stream().filter(c->c.getUniqueId().equals(Iterables.getLast(path))).findFirst();
397         }
398         if(ci.isPresent()){
399             prefix = buildCapReqNamePrefix(ci.get().getNormalizedName());
400             if(ComponentUtilities.isNotUpdatedCapReqName(prefix, name, previousName)){
401                 Either<String, Boolean> buildSubstitutedName = buildSubstitutedName(componentsCache, name, previousName, path, ownerId, ci.get());
402                 if(buildSubstitutedName.isRight()){
403                     logger.debug("Failed buildSubstitutedName name {}  path {} component {}", name, path, component.getUniqueId());
404                     return false;
405                 }
406                 sourceName = buildSubstitutedName.left().value();
407                 fullName = prefix + sourceName;
408             } else {
409                 sourceName = previousName;
410                 fullName = name;
411             }
412             entry.setFullName(fullName);
413             entry.setSourceName(sourceName);
414         } else {
415             logger.debug(FAILED_TO_FIND_CI_IN_PATH, path, component.getUniqueId());
416             return false;
417         }
418         return true;
419     }
420
421     private void addEntry(Map<String, String[]> toscaRequirements, Component component, List<String> capPath, SubstitutionEntry entry) {
422         Optional<ComponentInstance> findFirst = component.safeGetComponentInstances().stream().filter(ci -> ci.getUniqueId().equals(Iterables.getLast(capPath))).findFirst();
423         findFirst.ifPresent(componentInstance -> entry.setOwner(componentInstance.getName()));
424         if (StringUtils.isNotEmpty(entry.getOwner()) && StringUtils.isNotEmpty(entry.getSourceName())) {
425             toscaRequirements.put(entry.getFullName(), new String[] { entry.getOwner(), entry.getSourceName() });
426         }
427     }
428
429     public Either<String, Boolean> buildSubstitutedName(Map<String, Component> componentsCache, String name, String previousName, List<String> path, String ownerId, ComponentInstance instance) {
430         if(StringUtils.isNotEmpty(previousName)){
431             return Either.left(name);
432         }
433         Either<Component, Boolean> getOriginRes = getOriginComponent(componentsCache, instance);
434         if(getOriginRes.isRight()){
435             logger.debug("Failed to build substituted name for the capability/requirement {}. Failed to get an origin component with uniqueId {}", name, instance.getComponentUid());
436             return Either.right(false);
437         }
438         List<String> reducedPath = ownerId !=null ? getReducedPathByOwner(path , ownerId ) : getReducedPath(path) ;
439         logger.debug("reducedPath for ownerId {}, reducedPath {} ", ownerId, reducedPath);
440         reducedPath.remove(reducedPath.size() - 1);
441         return buildSubstitutedName(componentsCache, getOriginRes.left().value(), reducedPath, name, previousName);
442     }
443
444     private String buildReqNamePerOwnerByPath(Map<String, Component> componentsCache, Component component, RequirementDefinition r) {
445         return buildCapReqNamePerOwnerByPath(componentsCache, component, r.getName(), r.getPreviousName(), r.getPath());
446     }
447
448     private ImmutablePair<String, ToscaRequirement> convertRequirement(Map<String, Component> componentsCache, Component component, boolean isNodeType, RequirementDefinition r) {
449         String name = r.getName();
450         if (!isNodeType && ToscaUtils.isNotComplexVfc(component)) {
451             name = buildReqNamePerOwnerByPath(componentsCache, component, r);
452         }
453         logger.debug("the requirement {} belongs to resource {} ", name, component.getUniqueId());
454         ToscaRequirement toscaRequirement = createToscaRequirement(r);
455         return new ImmutablePair<>(name, toscaRequirement);
456     }
457
458     private ToscaRequirement createToscaRequirement(RequirementDefinition r) {
459         ToscaRequirement toscaRequirement = new ToscaRequirement();
460
461         List<Object> occurrences = new ArrayList<>();
462         occurrences.add(Integer.valueOf(r.getMinOccurrences()));
463         if (r.getMaxOccurrences().equals(RequirementDataDefinition.MAX_OCCURRENCES)) {
464             occurrences.add(r.getMaxOccurrences());
465         } else {
466             occurrences.add(Integer.valueOf(r.getMaxOccurrences()));
467         }
468         toscaRequirement.setOccurrences(occurrences);
469         toscaRequirement.setNode(r.getNode());
470         toscaRequirement.setCapability(r.getCapability());
471         toscaRequirement.setRelationship(r.getRelationship());
472         return toscaRequirement;
473     }
474
475     /**
476      * Allows to convert capabilities of a node type to tosca template capabilities
477      * @param component
478      * @param dataTypes
479      * @return
480      */
481     public Map<String, ToscaCapability> convertCapabilities(Map<String, Component> componentsCache, Component component, Map<String, DataTypeDefinition> dataTypes) {
482         Map<String, List<CapabilityDefinition>> capabilities = component.getCapabilities();
483         Map<String, ToscaCapability> toscaCapabilities = new HashMap<>();
484         if (capabilities != null) {
485             boolean isNodeType = ModelConverter.isAtomicComponent(component);
486             for (Map.Entry<String, List<CapabilityDefinition>> entry : capabilities.entrySet()) {
487                 entry.getValue().stream().filter(c -> filter(component, c.getOwnerId())).forEach(c -> convertCapability(componentsCache, component, toscaCapabilities, isNodeType, c, dataTypes , c.getName()));
488             }
489         } else {
490             logger.debug(NO_CAPABILITIES);
491         }
492
493         return toscaCapabilities;
494     }
495
496     /**
497      * Allows to convert capabilities of a server proxy node type to tosca template capabilities
498      * @param instanceProxy
499      * @param dataTypes
500      * @return
501      */
502     public Map<String, ToscaCapability> convertProxyCapabilities(Map<String, Component> componentCache,
503                                                                  ComponentInstance instanceProxy,
504                                                                  Map<String, DataTypeDefinition> dataTypes) {
505         Map<String, List<CapabilityDefinition>> capabilities = instanceProxy.getCapabilities();
506         Map<String, ToscaCapability> toscaCapabilities = new HashMap<>();
507         if (capabilities != null) {
508             for (Map.Entry<String, List<CapabilityDefinition>> entry : capabilities.entrySet()) {
509                 entry.getValue()
510                         .stream()
511                         .forEach(c -> convertProxyCapability(toscaCapabilities, c, dataTypes ,
512                                 buildCapabilityNameForComponentInstance( componentCache , instanceProxy , c )));
513             }
514         } else {
515             logger.debug(NO_CAPABILITIES);
516         }
517
518         return toscaCapabilities;
519     }
520
521     /**
522      * Allows to convert component capabilities to the tosca template substitution mappings capabilities
523      * @param componentsCache
524      * @param component
525      * @return
526      */
527     public Either<Map<String, String[]>, ToscaError> convertSubstitutionMappingCapabilities(Map<String, Component> componentsCache, Component component) {
528         Map<String, List<CapabilityDefinition>> capabilities = component.getCapabilities();
529         Either<Map<String, String[]>, ToscaError> res;
530         if (capabilities != null) {
531             res = buildAddSubstitutionMappingsCapabilities(componentsCache, component, capabilities);
532         } else {
533             res = Either.left(Maps.newHashMap());
534             logger.debug(NO_CAPABILITIES);
535         }
536         return res;
537     }
538
539     private String buildCapNamePerOwnerByPath(Map<String, Component> componentsCache, CapabilityDefinition c, Component component) {
540         return buildCapReqNamePerOwnerByPath(componentsCache, component, c.getName(), c.getPreviousName(), c.getPath());
541     }
542
543     private void convertProxyCapability(Map<String, ToscaCapability> toscaCapabilities, CapabilityDefinition c,
544                                         Map<String, DataTypeDefinition> dataTypes, String capabilityName) {
545         createToscaCapability(toscaCapabilities, c, dataTypes, capabilityName);
546     }
547
548     private void convertCapability(Map<String, Component> componentsCache, Component component, Map<String, ToscaCapability> toscaCapabilities, boolean isNodeType, CapabilityDefinition c, Map<String, DataTypeDefinition> dataTypes , String capabilityName) {
549         String name = isNoneBlank(capabilityName) ? capabilityName : c.getName();
550         if (!isNodeType && ToscaUtils.isNotComplexVfc(component)) {
551             name = buildCapNamePerOwnerByPath(componentsCache, c, component);
552         }
553         logger.debug("The capability {} belongs to resource {} ", name, component.getUniqueId());
554         createToscaCapability(toscaCapabilities, c, dataTypes, name);
555     }
556
557     private void createToscaCapability(Map<String, ToscaCapability> toscaCapabilities, CapabilityDefinition c,
558                                        Map<String, DataTypeDefinition> dataTypes, String name) {
559         ToscaCapability toscaCapability = new ToscaCapability();
560         toscaCapability.setDescription(c.getDescription());
561         toscaCapability.setType(c.getType());
562
563         List<Object> occurrences = new ArrayList<>();
564         occurrences.add(Integer.valueOf(c.getMinOccurrences()));
565         if (c.getMaxOccurrences().equals(CapabilityDataDefinition.MAX_OCCURRENCES)) {
566             occurrences.add(c.getMaxOccurrences());
567         } else {
568             occurrences.add(Integer.valueOf(c.getMaxOccurrences()));
569         }
570         toscaCapability.setOccurrences(occurrences);
571
572         toscaCapability.setValid_source_types(c.getValidSourceTypes());
573         List<ComponentInstanceProperty> properties = c.getProperties();
574         if (isNotEmpty(properties)) {
575             Map<String, ToscaProperty> toscaProperties = new HashMap<>();
576             for (PropertyDefinition property : properties) {
577                 ToscaProperty toscaProperty = propertyConvertor.convertProperty(dataTypes, property, PropertyConvertor.PropertyType.CAPABILITY);
578                 toscaProperties.put(property.getName(), toscaProperty);
579             }
580             toscaCapability.setProperties(toscaProperties);
581         }
582         toscaCapabilities.put(name, toscaCapability);
583     }
584
585     private String buildCapReqNamePerOwnerByPath(Map<String, Component> componentsCache, Component component, String name, String previousName, List<String> path) {
586         if (CollectionUtils.isEmpty(path)) {
587             return name;
588         }
589         String ownerId = path.get(path.size() - 1);
590         String prefix;
591         if(CollectionUtils.isNotEmpty(component.getGroups())) {
592             Optional<GroupDefinition> groupOpt = component.getGroups().stream().filter(g -> g.getUniqueId().equals(ownerId)).findFirst();
593             if (groupOpt.isPresent()) {
594                 prefix = buildCapReqNamePrefix(groupOpt.get().getNormalizedName());
595                 if(ComponentUtilities.isNotUpdatedCapReqName(prefix, name, previousName)){
596                     return prefix + name;
597                 }
598                 return name;
599             }
600         }
601         Optional<ComponentInstance> ci = component.safeGetComponentInstances().stream().filter(c->c.getUniqueId().equals(Iterables.getLast(path))).findFirst();
602         if(!ci.isPresent()){
603             logger.debug(FAILED_TO_FIND_CI_IN_PATH, path, component.getUniqueId());
604
605             Collections.reverse(path);
606
607             logger.debug("try to reverse path {} component {}", path, component.getUniqueId());
608             ci = component.safeGetComponentInstances().stream().filter(c->c.getUniqueId().equals(Iterables.getLast(path))).findFirst();
609         }
610         if(ci.isPresent()){
611             prefix = buildCapReqNamePrefix(ci.get().getNormalizedName());
612             if(ComponentUtilities.isNotUpdatedCapReqName(prefix, name, previousName)){
613                 Either<String, Boolean> buildSubstitutedName = buildSubstitutedName(componentsCache, name, previousName, path, ownerId, ci.get());
614                 if(buildSubstitutedName.isRight()){
615                     logger.debug("Failed buildSubstitutedName name {}  path {} component {}", name, path, component.getUniqueId());
616                 }
617                 return prefix + buildSubstitutedName.left().value();
618             }
619             return name;
620         }
621         return StringUtils.EMPTY;
622     }
623     /**
624      * Allows to build substituted name of capability\requirement of the origin component instance according to the path
625      * @param componentsCache
626      * @param originComponent
627      * @param path
628      * @param name
629      * @param previousName
630      * @return
631      */
632     public Either<String, Boolean> buildSubstitutedName(Map<String, Component> componentsCache, Component originComponent, List<String> path, String name, String previousName) {
633         if(StringUtils.isNotEmpty(previousName)){
634             return Either.left(name);
635         }
636         StringBuilder substitutedName = new StringBuilder();
637         boolean nameBuiltSuccessfully = true;
638         if(isNotEmpty(path) && ToscaUtils.isNotComplexVfc(originComponent)){
639             List<String> reducedPath = getReducedPath(path);
640             Collections.reverse(reducedPath);
641             nameBuiltSuccessfully = appendNameRecursively(componentsCache, originComponent, reducedPath.iterator(), substitutedName);
642         }
643         return nameBuiltSuccessfully ? Either.left(substitutedName.append(name).toString()) : Either.right(nameBuiltSuccessfully);
644     }
645
646     protected List<String> getReducedPathByOwner(List<String> path , String ownerId) {
647         logger.debug("ownerId {}, path {} ", ownerId, path);
648         if ( CollectionUtils.isEmpty(path) ){
649             logger.debug("cannot perform reduce by owner, path to component is empty");
650             return path;
651         }
652         if ( isBlank(ownerId) ){
653             logger.debug("cannot perform reduce by owner, component owner is empty");
654             return path;
655         }
656         //reduce by owner
657         Map map = path.stream().collect( Collectors.toMap( it -> dropLast(it,PATH_DELIMITER) , Function.identity() , ( a , b ) ->  a.endsWith(ownerId) ? a : b ));
658         //reduce list&duplicates and preserve order
659         return path.stream().distinct().filter(it -> map.values().contains(it) ).collect(Collectors.toList());
660     }
661
662     private List<String> getReducedPath(List<String> path) {
663         return path.stream().distinct().collect(Collectors.toList());
664     }
665
666     private boolean appendNameRecursively(Map<String, Component> componentsCache, Component originComponent, Iterator<String> instanceIdIter, StringBuilder substitutedName) {
667         if(isNotEmpty(originComponent.getComponentInstances()) && instanceIdIter.hasNext() && ToscaUtils.isNotComplexVfc(originComponent)){
668             String ownerId = instanceIdIter.next();
669             Optional<ComponentInstance> instanceOpt = originComponent.getComponentInstances().stream().filter(i -> i.getUniqueId().equals(ownerId)).findFirst();
670             if(instanceOpt.isPresent()){
671                 substitutedName.append(instanceOpt.get().getNormalizedName()).append(PATH_DELIMITER);
672                 Either<Component, Boolean> getOriginRes = getOriginComponent(componentsCache, instanceOpt.get());
673                 if(getOriginRes.isRight()){
674                     return false;
675                 }
676                 appendNameRecursively(componentsCache, getOriginRes.left().value(), instanceIdIter, substitutedName);
677             } else if(CollectionUtils.isNotEmpty(originComponent.getGroups())){
678                 Optional<GroupDefinition> groupOpt = originComponent.getGroups().stream().filter(g -> g.getUniqueId().equals(ownerId)).findFirst();
679                 if(!groupOpt.isPresent()){
680                     logger.debug("Failed to find an capability owner with uniqueId {} on a component with uniqueId {}", ownerId, originComponent.getUniqueId());
681                     return false;
682                 }
683                 substitutedName.append(groupOpt.get().getNormalizedName()).append(PATH_DELIMITER);
684             } else {
685                 logger.debug("Failed to find an capability owner with uniqueId {} on a component with uniqueId {}", ownerId, originComponent.getUniqueId());
686                 return false;
687             }
688         }
689         return true;
690     }
691
692     Either<Component, Boolean> getOriginComponent(Map<String, Component> componentsCache, ComponentInstance instance) {
693         Either<Component, Boolean> result;
694         Either<Component, StorageOperationStatus> getOriginRes;
695         if(componentsCache.containsKey(instance.getActualComponentUid())){
696             result = Either.left(componentsCache.get(instance.getActualComponentUid()));
697         } else {
698             ComponentParametersView filter = getFilter(instance);
699             getOriginRes = toscaOperationFacade.getToscaElement(instance.getActualComponentUid(), filter);
700             if(getOriginRes.isRight()){
701                 logger.debug("Failed to get an origin component with uniqueId {}", instance.getActualComponentUid());
702                 result = Either.right(false);
703             } else {
704                 result = Either.left(getOriginRes.left().value());
705                 componentsCache.put(getOriginRes.left().value().getUniqueId(), getOriginRes.left().value());
706             }
707         }
708         return result;
709     }
710
711     private ComponentParametersView getFilter(ComponentInstance instance) {
712         ComponentParametersView filter = new ComponentParametersView(true);
713         filter.setIgnoreComponentInstances(false);
714         if(instance.getIsProxy()){
715             filter.setIgnoreCapabilities(false);
716             filter.setIgnoreRequirements(false);
717             filter.setIgnoreCategories(false);
718         }
719         if(instance.getOriginType() == OriginTypeEnum.VF){
720             filter.setIgnoreGroups(false);
721         }
722         return filter;
723     }
724
725 }