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