Added oparent to sdc main
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / jsonjanusgraph / utils / CapabilityRequirementNameResolver.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.openecomp.sdc.be.model.jsonjanusgraph.utils;
22
23 import org.apache.commons.collections.CollectionUtils;
24 import org.apache.commons.collections.MapUtils;
25 import org.apache.commons.lang3.StringUtils;
26 import org.openecomp.sdc.be.config.BeEcompErrorManager;
27 import org.openecomp.sdc.be.datatypes.elements.*;
28 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
29 import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.TopologyTemplate;
30 import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.ToscaElement;
31 import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.ToscaElementTypeEnum;
32 import org.openecomp.sdc.be.model.utils.ComponentUtilities;
33 import org.openecomp.sdc.common.log.wrappers.Logger;
34 import java.util.*;
35 import java.util.function.Function;
36 import java.util.stream.Collectors;
37 import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
38 import static org.apache.commons.lang3.StringUtils.isBlank;
39
40 public class CapabilityRequirementNameResolver {
41
42     private static final Logger log = Logger.getLogger(CapabilityRequirementNameResolver.class);
43     private static final String PATH_DELIMITER = ".";
44
45     private CapabilityRequirementNameResolver() {
46     }
47
48     public static void updateNamesOfCalculatedCapabilitiesRequirements(TopologyTemplate toscaElement, String ownerId, String ownerName, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
49         Map<String, ToscaElement> componentCacheToRepair = new HashMap<>();
50         log.debug("#updateNamesOfCalculatedCapabilitiesRequirements");
51         updateCalculatedCapabilitiesNames(componentCacheToRepair, toscaElement, ownerId, ownerName, originGetter);
52         updateCalculatedRequirementsNames(componentCacheToRepair, toscaElement, ownerId, ownerName, originGetter);
53         updateCalculatedCapabilitiesPropertiesKeys(toscaElement, ownerId);
54     }
55
56     private static void updateCalculatedCapabilitiesPropertiesKeys(TopologyTemplate toscaElement, String ownerId) {
57         if (calCapPropertiesExist(toscaElement, ownerId)) {
58             MapCapabilityProperty newProps = new MapCapabilityProperty();
59             toscaElement.getCalculatedCapabilitiesProperties().get(ownerId)
60                     .getMapToscaDataDefinition()
61                     .forEach((k, v) -> updateAndAddCalculatedCapabilitiesProperties(k, v, toscaElement.getCalculatedCapabilities().get(ownerId), newProps));
62             if (MapUtils.isNotEmpty(newProps.getMapToscaDataDefinition())) {
63                 toscaElement.getCalculatedCapabilitiesProperties().put(ownerId, newProps);
64             }
65         }
66     }
67
68     private static boolean calCapPropertiesExist(TopologyTemplate toscaElement, String ownerId) {
69         return toscaElement.getCalculatedCapabilitiesProperties() != null
70                 && toscaElement.getCalculatedCapabilitiesProperties().get(ownerId) != null
71                 && MapUtils.isNotEmpty(toscaElement.getCalculatedCapabilitiesProperties().get(ownerId).getMapToscaDataDefinition())
72                 && capabilitiesExist(toscaElement, ownerId);
73     }
74
75     private static void updateCalculatedRequirementsNames(Map<String, ToscaElement> componentCacheToRepair, TopologyTemplate toscaElement, String ownerId, String ownerName, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
76         if (requirementsExist(toscaElement, ownerId)) {
77             String prefix = ownerName + PATH_DELIMITER;
78             repairReqNames(componentCacheToRepair, toscaElement, ownerId, originGetter);
79             toscaElement.getCalculatedRequirements().get(ownerId)
80                     .getMapToscaDataDefinition().values().stream()
81                     .flatMap(l -> l.getListToscaDataDefinition().stream())
82                     .forEach(r -> {
83                         if (isRequiredToRepair(r.getName())) {
84                             BeEcompErrorManager.getInstance()
85                                     .logBeComponentMissingError("The empty name of the requirement was found. Id: " + r.getUniqueId() + ", ownerId: " + ownerId + ", ownerName: " + ownerName,
86                                             toscaElement.getComponentType().getValue(), toscaElement.getName());
87                         }
88                         if (ComponentUtilities.isNotUpdatedCapReqName(prefix, r.getName(), r.getPreviousName())) {
89                             if (StringUtils.isNotEmpty(r.getPreviousName())) {
90                                 r.setParentName(r.getPreviousName());
91                             }
92                             r.setPreviousName(r.getName());
93                         }
94                         r.setName(prefix + r.getPreviousName());
95                     });
96         }
97     }
98
99     private static boolean requirementsExist(TopologyTemplate toscaElement, String ownerId) {
100         return toscaElement.getCalculatedRequirements() != null
101                 && toscaElement.getCalculatedRequirements().get(ownerId) != null
102                 && MapUtils.isNotEmpty(toscaElement.getCalculatedRequirements().get(ownerId).getMapToscaDataDefinition());
103     }
104
105     private static void updateCalculatedCapabilitiesNames(Map<String, ToscaElement> componentCacheToRepair, TopologyTemplate toscaElement, String ownerId, String ownerName, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
106         if (capabilitiesExist(toscaElement, ownerId)) {
107             String prefix = ownerName + PATH_DELIMITER;
108             repairCapNames(componentCacheToRepair, toscaElement, ownerId, originGetter);
109             toscaElement.getCalculatedCapabilities().get(ownerId)
110                     .getMapToscaDataDefinition().values().stream()
111                     .flatMap(l -> l.getListToscaDataDefinition().stream())
112                     .forEach(c -> {
113                         if (isRequiredToRepair(c.getName())) {
114                             BeEcompErrorManager.getInstance()
115                                     .logBeComponentMissingError("The empty name of the capability was found. Id: " + c.getUniqueId() + ", ownerId: " + ownerId + ", ownerName: " + ownerName,
116                                             toscaElement.getComponentType().getValue(), toscaElement.getName());
117                         }
118                         if (ComponentUtilities.isNotUpdatedCapReqName(prefix, c.getName(), c.getPreviousName())) {
119                             if (StringUtils.isNotEmpty(c.getPreviousName())) {
120                                 c.setParentName(c.getPreviousName());
121                             }
122                             c.setPreviousName(c.getName());
123                         }
124                         c.setName(prefix + c.getPreviousName());
125                     });
126         }
127     }
128
129     private static boolean capabilitiesExist(TopologyTemplate toscaElement, String ownerId) {
130         return toscaElement.getCalculatedCapabilities() != null
131                 && toscaElement.getCalculatedCapabilities().get(ownerId) != null
132                 && MapUtils.isNotEmpty(toscaElement.getCalculatedCapabilities().get(ownerId).getMapToscaDataDefinition());
133     }
134
135     private static void repairCapNames(Map<String, ToscaElement> componentCacheToRepair, TopologyTemplate toscaElement, String ownerId, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
136         log.debug("#repairCapNames");
137         boolean emptyNameFound = toscaElement.getCalculatedCapabilities() != null
138                 && toscaElement.getCalculatedCapabilities().get(ownerId) != null
139                 && toscaElement.getCalculatedCapabilities().get(ownerId).getMapToscaDataDefinition() != null
140                 && toscaElement.getCalculatedCapabilities().get(ownerId).getMapToscaDataDefinition().values()
141                 .stream()
142                 .filter(Objects::nonNull)
143                 .flatMap(l -> l.getListToscaDataDefinition().stream())
144                 .filter(Objects::nonNull)
145                 .anyMatch(c -> isRequiredToRepair(c.getName()));
146
147         ComponentInstanceDataDefinition instance = toscaElement.getComponentInstances() != null ?
148                 toscaElement.getComponentInstances().get(ownerId) : null;
149         if (instance != null && emptyNameFound) {
150             log.debug("#repairCapNames - Going to repair the name of the capability for the owner {}. ", ownerId);
151             toscaElement.getCalculatedCapabilities().get(ownerId)
152                     .getMapToscaDataDefinition().values()
153                     .stream()
154                     .flatMap(l -> l.getListToscaDataDefinition().stream())
155                     .forEach(c -> repairCapName(componentCacheToRepair, instance, c, originGetter));
156         }
157     }
158
159     private static void repairReqNames(Map<String, ToscaElement> componentCacheToRepair, TopologyTemplate toscaElement, String ownerId, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
160         log.debug("#repairReqNames");
161         boolean emptyNameFound = toscaElement.getCalculatedRequirements() != null
162                 && toscaElement.getCalculatedRequirements().get(ownerId) != null
163                 && toscaElement.getCalculatedRequirements().get(ownerId).getMapToscaDataDefinition() != null
164                 && toscaElement.getCalculatedRequirements().get(ownerId).getMapToscaDataDefinition().values()
165                 .stream()
166                 .filter(Objects::nonNull)
167                 .flatMap(l -> l.getListToscaDataDefinition().stream())
168                 .filter(Objects::nonNull)
169                 .anyMatch(r -> isRequiredToRepair(r.getName()));
170
171         ComponentInstanceDataDefinition instance = toscaElement.getComponentInstances() != null ?
172                 toscaElement.getComponentInstances().get(ownerId) : null;
173         if (instance != null && emptyNameFound) {
174             log.debug("#repairReqNames - Going to repair the name of the requirement for the owner {}. ", ownerId);
175             toscaElement.getCalculatedRequirements().get(ownerId)
176                     .getMapToscaDataDefinition().values()
177                     .stream()
178                     .flatMap(l -> l.getListToscaDataDefinition().stream())
179                     .forEach(r -> repairReqName(componentCacheToRepair, instance, r, originGetter));
180         }
181     }
182
183     private static void repairCapName(Map<String, ToscaElement> componentCacheToRepair, ComponentInstanceDataDefinition instance, CapabilityDataDefinition capability, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
184         if (isRequiredToRepair(capability.getName())) {
185             log.debug("#repairTopologyTemplateCapName - Going to build the name for the capability: ", capability.getUniqueId());
186             buildSetCapName(componentCacheToRepair, capability, instance, originGetter);
187         }
188     }
189
190     private static boolean isRequiredToRepair(String name) {
191         boolean isRequiredToRepair = StringUtils.isEmpty(name) || name.endsWith(".null") || name.contains(".null.");
192         if (isRequiredToRepair) {
193             log.debug("#isRequiredToRepair - The name {} should be repaired. ", name);
194         } else {
195             log.debug("#isRequiredToRepair - The name {} should not be repaired. ", name);
196         }
197         return isRequiredToRepair;
198     }
199
200     private static void repairReqName(Map<String, ToscaElement> componentCacheToRepair, ComponentInstanceDataDefinition instance, RequirementDataDefinition requirement, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
201         if (isRequiredToRepair(requirement.getName())) {
202             log.debug("#repairTopologyTemplateCapName - Going to build the name for the requirement: ", requirement.getUniqueId());
203             buildSetReqName(componentCacheToRepair, requirement, instance, originGetter);
204         }
205     }
206
207     private static void updateAndAddCalculatedCapabilitiesProperties(String stringKey, MapPropertiesDataDefinition properties, MapListCapabilityDataDefinition calculatedCapabilities, MapCapabilityProperty newProps) {
208         String[] key = stringKey.split(ModelConverter.CAP_PROP_DELIM);
209         String capType = key[key.length - 2];
210         String capName = key[key.length - 1];
211         Optional<CapabilityDataDefinition> foundCapOpt = calculatedCapabilities.getMapToscaDataDefinition().get(capType)
212                 .getListToscaDataDefinition().stream()
213                 .filter(c -> StringUtils.isNotEmpty(c.getPreviousName()) && c.getPreviousName().equals(capName))
214                 .findFirst();
215         foundCapOpt.ifPresent(capabilityDataDefinition -> key[key.length - 1] = capabilityDataDefinition.getName());
216         newProps.put(buildCaLCapPropKey(key), properties);
217     }
218
219     public static void revertNamesOfCalculatedCapabilitiesRequirements(TopologyTemplate toscaElement, String ownerId, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
220         Map<String, ToscaElement> componentCacheToRepair = new HashMap<>();
221         log.debug("#revertNamesOfCalculatedCapabilitiesRequirements");
222         revertCalculatedCapabilitiesPropertiesKeys(componentCacheToRepair, toscaElement, ownerId, originGetter);
223         revertCalculatedCapabilitiesNames(toscaElement, ownerId);
224         revertCalculatedRequirementsNames(componentCacheToRepair, toscaElement, ownerId, originGetter);
225     }
226
227     private static void revertCalculatedCapabilitiesPropertiesKeys(Map<String, ToscaElement> componentCacheToRepair, TopologyTemplate toscaElement, String ownerId, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
228         repairCapNames(componentCacheToRepair, toscaElement, ownerId, originGetter);
229         if (calCapPropertiesExist(toscaElement, ownerId)) {
230             MapCapabilityProperty newProps = new MapCapabilityProperty();
231             toscaElement.getCalculatedCapabilitiesProperties().get(ownerId)
232                     .getMapToscaDataDefinition()
233                     .forEach((k, v) -> revertAndAddCalculatedCapabilitiesProperties(k, v, toscaElement.getCalculatedCapabilities().get(ownerId), newProps));
234             if (MapUtils.isNotEmpty(newProps.getMapToscaDataDefinition())) {
235                 toscaElement.getCalculatedCapabilitiesProperties().put(ownerId, newProps);
236             }
237         }
238     }
239
240     private static void revertCalculatedRequirementsNames(Map<String, ToscaElement> componentCacheToRepair, TopologyTemplate toscaElement, String ownerId, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
241         repairReqNames(componentCacheToRepair, toscaElement, ownerId, originGetter);
242         if (requirementsExist(toscaElement, ownerId)) {
243             toscaElement.getCalculatedRequirements().get(ownerId)
244                     .getMapToscaDataDefinition().values().stream()
245                     .flatMap(l -> l.getListToscaDataDefinition().stream())
246                     .forEach(CapabilityRequirementNameResolver::revertReqNames);
247         }
248     }
249
250     private static void revertReqNames(RequirementDataDefinition requirement) {
251         if (StringUtils.isNotEmpty(requirement.getPreviousName())) {
252             requirement.setName(requirement.getPreviousName());
253             requirement.setPreviousName(requirement.getParentName());
254         }
255     }
256
257     private static void revertCalculatedCapabilitiesNames(TopologyTemplate toscaElement, String ownerId) {
258         if (capabilitiesExist(toscaElement, ownerId)) {
259             toscaElement.getCalculatedCapabilities().get(ownerId)
260                     .getMapToscaDataDefinition().values().stream()
261                     .flatMap(l -> l.getListToscaDataDefinition().stream())
262                     .forEach(CapabilityRequirementNameResolver::revertCapNames);
263         }
264     }
265
266     private static void revertCapNames(CapabilityDataDefinition capability) {
267         if (StringUtils.isNotEmpty(capability.getPreviousName())) {
268             capability.setName(capability.getPreviousName());
269             capability.setPreviousName(capability.getParentName());
270         }
271     }
272
273     private static void revertAndAddCalculatedCapabilitiesProperties(String stringKey, MapPropertiesDataDefinition properties, MapListCapabilityDataDefinition calculatedCapabilities, MapCapabilityProperty newProps) {
274         String[] key = stringKey.split(ModelConverter.CAP_PROP_DELIM);
275         String capType = key[key.length - 2];
276         String capName = key[key.length - 1];
277         Optional<CapabilityDataDefinition> foundCapOpt = calculatedCapabilities.getMapToscaDataDefinition().get(capType)
278                 .getListToscaDataDefinition().stream()
279                 .filter(c -> c.getName().equals(capName) && StringUtils.isNotEmpty(c.getPreviousName()))
280                 .findFirst();
281         foundCapOpt.ifPresent(capabilityDataDefinition -> key[key.length - 1] = capabilityDataDefinition.getPreviousName());
282         newProps.put(buildCaLCapPropKey(key), properties);
283     }
284
285     private static String buildCaLCapPropKey(String[] keyArray) {
286         StringBuilder key = new StringBuilder();
287         for (int i = 0; i < keyArray.length; ++i) {
288             key.append(keyArray[i]);
289             if (i < keyArray.length - 1) {
290                 key.append(ModelConverter.CAP_PROP_DELIM);
291             }
292         }
293         return key.toString();
294     }
295
296     private static void buildSetCapName(Map<String, ToscaElement> componentsCache, CapabilityDataDefinition capability, ComponentInstanceDataDefinition instance, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
297         List<String> reducedPath = capability.getOwnerId() != null ? getReducedPathByOwner(capability.getPath(), capability.getOwnerId()) : getReducedPath(capability.getPath());
298         log.debug("reducedPath for ownerId {}, reducedPath {} ", capability.getOwnerId(), reducedPath);
299         reducedPath.remove(reducedPath.size() - 1);
300         ToscaElement originComponent = getOriginComponent(componentsCache, instance, originGetter);
301         String name = isRequiredToRepair(capability.getParentName()) ?
302                 extractNameFromUniqueId(capability.getUniqueId()) : capability.getParentName();
303         StringBuilder repairedName = buildSubstitutedName(componentsCache, originComponent, reducedPath, originGetter);
304         log.debug("#buildSetCapName - The name for the capability was built: {}", repairedName);
305
306         capability.setName(repairedName.append(name).toString());
307         if (isRequiredToRepair(capability.getPreviousName())) {
308             capability.setPreviousName(capability.getName().substring(capability.getName().indexOf(PATH_DELIMITER) + 1));
309         }
310         if (isRequiredToRepair(capability.getParentName())) {
311             capability.setParentName(name);
312         }
313     }
314
315     private static void buildSetReqName(Map<String, ToscaElement> componentsCache, RequirementDataDefinition requirement, ComponentInstanceDataDefinition instance, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
316         List<String> reducedPath = requirement.getOwnerId() != null ? getReducedPathByOwner(requirement.getPath(), requirement.getOwnerId()) : getReducedPath(requirement.getPath());
317         log.debug("reducedPath for ownerId {}, reducedPath {} ", requirement.getOwnerId(), reducedPath);
318         reducedPath.remove(reducedPath.size() - 1);
319         ToscaElement originComponent = getOriginComponent(componentsCache, instance, originGetter);
320         String name = isRequiredToRepair(requirement.getParentName()) ?
321                 extractNameFromUniqueId(requirement.getUniqueId()) : requirement.getParentName();
322
323         StringBuilder repairedName = buildSubstitutedName(componentsCache, originComponent, reducedPath, originGetter);
324         log.debug("#buildSetReqName - The name for the capability was built: ", repairedName);
325         requirement.setName(repairedName.append(name).toString());
326         if (isRequiredToRepair(requirement.getPreviousName())) {
327             requirement.setPreviousName(requirement.getName().substring(requirement.getName().indexOf(PATH_DELIMITER) + 1));
328         }
329         if (isRequiredToRepair(requirement.getParentName())) {
330             requirement.setParentName(name);
331         }
332     }
333
334     private static String extractNameFromUniqueId(String uniqueId) {
335         String[] uid = uniqueId.split("\\.");
336         return uid[uid.length - 1];
337     }
338
339     private static StringBuilder buildSubstitutedName(Map<String, ToscaElement> componentsCache, ToscaElement originComponent, List<String> path, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
340         StringBuilder substitutedName = new StringBuilder();
341         log.debug("#buildSubstitutedName");
342         if (isNotEmpty(path) && isTopologyTemplateNotCvfc(originComponent)) {
343             log.debug("#buildSubstitutedName");
344             List<String> reducedPath = getReducedPath(path);
345             Collections.reverse(reducedPath);
346             appendNameRecursively(componentsCache, originComponent, reducedPath.iterator(), substitutedName, originGetter);
347         }
348         return substitutedName;
349     }
350
351     private static boolean isTopologyTemplateNotCvfc(ToscaElement originComponent) {
352         return originComponent.getToscaType() == ToscaElementTypeEnum.TOPOLOGY_TEMPLATE && originComponent.getResourceType() != ResourceTypeEnum.CVFC;
353     }
354
355     private static ToscaElement getOriginComponent(Map<String, ToscaElement> componentsCache, ComponentInstanceDataDefinition instance, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
356         if (componentsCache.containsKey(getActualComponentUid(instance))) {
357             return componentsCache.get(getActualComponentUid(instance));
358         }
359         ToscaElement origin = originGetter.apply(instance);
360         componentsCache.put(origin.getUniqueId(), origin);
361         return origin;
362     }
363
364     public static String getActualComponentUid(ComponentInstanceDataDefinition instance) {
365         return instance.getIsProxy() ? instance.getSourceModelUid() : instance.getComponentUid();
366     }
367
368     private static List<String> getReducedPath(List<String> path) {
369         return path.stream().distinct().collect(Collectors.toList());
370     }
371
372     private static void appendNameRecursively(Map<String, ToscaElement> componentsCache, ToscaElement originComponent, Iterator<String> instanceIdIter, StringBuilder substitutedName, Function<ComponentInstanceDataDefinition, ToscaElement> originGetter) {
373         log.debug("#appendNameRecursively");
374         if (isTopologyTemplateNotCvfc(originComponent)
375                 && MapUtils.isNotEmpty(((TopologyTemplate) originComponent).getComponentInstances()) && instanceIdIter.hasNext()) {
376
377             String ownerId = instanceIdIter.next();
378             Optional<ComponentInstanceDataDefinition> instanceOpt = ((TopologyTemplate) originComponent).getComponentInstances().values().stream().filter(i -> i.getUniqueId().equals(ownerId)).findFirst();
379             if (instanceOpt.isPresent()) {
380                 substitutedName.append(instanceOpt.get().getNormalizedName()).append(PATH_DELIMITER);
381                 ToscaElement getOriginRes = getOriginComponent(componentsCache, instanceOpt.get(), originGetter);
382                 appendNameRecursively(componentsCache, getOriginRes, instanceIdIter, substitutedName, originGetter);
383             } else if (MapUtils.isNotEmpty(((TopologyTemplate) originComponent).getGroups())) {
384                 Optional<GroupDataDefinition> groupOpt = ((TopologyTemplate) originComponent).getGroups().values().stream().filter(g -> g.getUniqueId().equals(ownerId)).findFirst();
385                 groupOpt.ifPresent(groupDataDefinition -> substitutedName.append(groupDataDefinition.getName()).append(PATH_DELIMITER));
386             } else {
387                 log.debug("Failed to find an capability owner with uniqueId {} on a component with uniqueId {}", ownerId, originComponent.getUniqueId());
388             }
389         }
390     }
391
392     private static List<String> getReducedPathByOwner(List<String> path, String ownerId) {
393         log.debug("ownerId {}, path {} ", ownerId, path);
394         if (CollectionUtils.isEmpty(path)) {
395             log.debug("cannot perform reduce by owner, path to component is empty");
396             return path;
397         }
398         if (isBlank(ownerId)) {
399             log.debug("cannot perform reduce by owner, component owner is empty");
400             return path;
401         }
402         //reduce by owner
403         Map map = path.stream().collect(Collectors.toMap(it -> dropLast(it, PATH_DELIMITER), Function.identity(), (a, b) -> a.endsWith(ownerId) ? a : b));
404         //reduce list&duplicates and preserve order
405         return path.stream().distinct().filter(it -> map.values().contains(it)).collect(Collectors.toList());
406     }
407
408     private static String dropLast(String path, String delimiter) {
409         if (isBlank(path) || isBlank(delimiter)) {
410             return path;
411         }
412         return path.substring(0, path.lastIndexOf(delimiter));
413     }
414 }