Support Inputs during Import Service
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / CompositionBusinessLogic.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  * Modifications copyright (c) 2019 Nokia
20  * ================================================================================
21  */
22 package org.openecomp.sdc.be.components.impl;
23
24 import java.security.SecureRandom;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Optional;
32 import java.util.Set;
33 import java.util.stream.Collectors;
34 import org.apache.commons.lang3.StringUtils;
35 import org.apache.commons.lang3.tuple.ImmutablePair;
36 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
37 import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
38 import org.openecomp.sdc.be.model.ComponentInstance;
39 import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
40 import org.openecomp.sdc.be.model.Resource;
41 import org.openecomp.sdc.be.model.Service;
42 import org.openecomp.sdc.common.log.wrappers.Logger;
43 import org.springframework.beans.factory.annotation.Autowired;
44 import org.springframework.stereotype.Component;
45
46 /**
47  * This class holds the logic of arranging resource instance on the canvas for imported VF
48  *
49  * @author mshitrit
50  */
51 @Component("compositionBusinessLogic")
52 public class CompositionBusinessLogic {
53
54     private static final Logger log = Logger.getLogger(CompositionBusinessLogic.class.getName());
55     private static final int VFC_CANVAS_ELEMENT_SIZE = 50;
56     private static final int CP_CANVAS_ELEMENT_SIZE = 21;
57     private static final int CANVAS_WIDTH = 1000;
58     private static final int CANVAS_HEIGHT = 700;
59     private static final int SPACE_BETWEEN_ELEMENTS = VFC_CANVAS_ELEMENT_SIZE * 4;
60     private static final double CP_RADIUS_FACTOR = 0.4;
61     private final ComponentInstanceBusinessLogic componentInstanceBusinessLogic;
62
63     @Autowired
64     public CompositionBusinessLogic(ComponentInstanceBusinessLogic componentInstanceBusinessLogic) {
65         this.componentInstanceBusinessLogic = componentInstanceBusinessLogic;
66     }
67
68     protected void setPositionsForComponentInstances(Resource resource, String userId) {
69         boolean isNotAllPositionsCalculated = resource.getComponentInstances() == null || resource.getComponentInstances().stream()
70             .anyMatch(p -> (p.getPosX() == null || p.getPosX().isEmpty()) || (p.getPosY() == null || p.getPosY().isEmpty()));
71         if (isNotAllPositionsCalculated && resource.getComponentInstances() != null) {
72             // Arrange Icons In Spiral Pattern
73             Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstanceLocations = buildSpiralPatternPositioningForComponentInstances(
74                 resource);
75             // Set Relative Locations According to Canvas Size
76             componentInstanceLocations.entrySet().forEach(this::setRelativePosition);
77             // Update in DB
78             componentInstanceBusinessLogic.updateComponentInstance(ComponentTypeEnum.RESOURCE_PARAM_NAME, resource, resource.getUniqueId(), userId,
79                 resource.getComponentInstances(), false);
80         }
81     }
82
83     ;
84
85     protected void setPositionsForComponentInstances(Service service, String userId) {
86         boolean isNotAllPositionsCalculated = service.getComponentInstances() == null || service.getComponentInstances().stream()
87             .anyMatch(p -> (p.getPosX() == null || p.getPosX().isEmpty()) || (p.getPosY() == null || p.getPosY().isEmpty()));
88         if (isNotAllPositionsCalculated && service.getComponentInstances() != null) {
89             // Arrange Icons In Spiral Pattern
90             Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstanceLocations = buildSpiralPatternPositioningForComponentInstances(
91                 service);
92             // Set Relative Locations According to Canvas Size
93             componentInstanceLocations.entrySet().forEach(this::setRelativePosition);
94             // Update in DB
95             componentInstanceBusinessLogic.updateComponentInstance(ComponentTypeEnum.SERVICE_PARAM_NAME, service, service.getUniqueId(), userId,
96                 service.getComponentInstances(), false);
97         }
98     }
99
100     private void setRelativePosition(Entry<ImmutablePair<Double, Double>, ComponentInstance> entry) {
101         int xCenter = CANVAS_WIDTH / 2;
102         int yCenter = CANVAS_HEIGHT / 2;
103         ImmutablePair<Double, Double> matrixPosition = entry.getKey();
104         ComponentInstance componentInstance = entry.getValue();
105         componentInstance.setPosX(calculateCompositionPosition(xCenter, matrixPosition.getLeft(), componentInstance));
106         componentInstance.setPosY(calculateCompositionPosition(yCenter, matrixPosition.getRight(), componentInstance));
107     }
108
109     private String calculateCompositionPosition(int center, double relativePosition, ComponentInstance componentInstance) {
110         final double topLeftCanvasPosition = center + relativePosition * CompositionBusinessLogic.SPACE_BETWEEN_ELEMENTS;
111         double offsetedCanvasPosition;
112         switch (componentInstance.getOriginType()) {
113             case CP:
114                 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.CP_CANVAS_ELEMENT_SIZE / 2.0;
115                 break;
116             case VL:
117                 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.CP_CANVAS_ELEMENT_SIZE / 2.0;
118                 break;
119             case VF:
120                 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2.0;
121                 break;
122             case VFC:
123                 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2.0;
124                 break;
125             case VFCMT:
126                 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2.0;
127                 break;
128             default:
129                 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2.0;
130                 break;
131         }
132         return String.valueOf(offsetedCanvasPosition);
133     }
134
135     protected Map<ImmutablePair<Double, Double>, ComponentInstance> buildSpiralPatternPositioningForComponentInstances(
136         org.openecomp.sdc.be.model.Component component) {
137         Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstanceLocations = new HashMap<>();
138         List<ComponentInstance> componentInstances = new ArrayList<>();
139         if (component instanceof Resource) {
140             Resource resource = (Resource) component;
141             List<ComponentInstance> componentInstanceList = resource.getComponentInstances();
142             componentInstances.addAll(componentInstanceList);
143         } else if (component instanceof Service) {
144             Service service = (Service) component;
145             List<ComponentInstance> componentInstanceList = service.getComponentInstances();
146             componentInstances.addAll(componentInstanceList);
147         }
148         Map<ComponentInstance, List<ComponentInstance>> connectededCps = getCpsConnectedToVFC(componentInstances, component);
149         // Remove all cp that are connected from the list
150         componentInstances.removeAll(connectededCps.values().stream().flatMap(Collection::stream).collect(Collectors.toList()));
151         buildSpiralPatternForMajorComponents(componentInstanceLocations, componentInstances);
152         buildCirclePatternForCps(componentInstanceLocations, connectededCps);
153         return componentInstanceLocations;
154     }
155
156     protected void buildCirclePatternForCps(Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstLocations,
157                                             Map<ComponentInstance, List<ComponentInstance>> connectedCps) {
158         for (Entry<ComponentInstance, List<ComponentInstance>> vfcCpList : connectedCps.entrySet()) {
159             componentInstLocations.entrySet().stream().filter(p -> p.getValue().getUniqueId().equals(vfcCpList.getKey().getUniqueId())).findAny()
160                 .ifPresent(vfcOfTheCps -> buildCirclePatternForOneGroupOfCps(vfcOfTheCps.getKey(), vfcCpList.getValue(), componentInstLocations));
161         }
162     }
163
164     private void buildCirclePatternForOneGroupOfCps(ImmutablePair<Double, Double> vfcLocation, List<ComponentInstance> cpsGroup,
165                                                     Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstLocations) {
166         final int numberOfCps = cpsGroup.size();
167         double angleBetweenCps = (!cpsGroup.isEmpty()) ? Math.toRadians(360) / numberOfCps : 0;
168         double currentAngle = 0;
169         Double xCenter = vfcLocation.getLeft();
170         Double yCenter = vfcLocation.getRight();
171         for (ComponentInstance currCp : cpsGroup) {
172             double cpXposition = xCenter + CompositionBusinessLogic.CP_RADIUS_FACTOR * Math.cos(currentAngle);
173             double cpYposition = yCenter + CompositionBusinessLogic.CP_RADIUS_FACTOR * Math.sin(currentAngle);
174             componentInstLocations.put(new ImmutablePair<>(cpXposition, cpYposition), currCp);
175             currentAngle += angleBetweenCps;
176         }
177     }
178
179     private void buildSpiralPatternForMajorComponents(Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstanceLocations,
180                                                       List<ComponentInstance> componentInstances) {
181         int elementsCounter = 0;
182         ImmutablePair<Double, Double> currPlacement;
183         ImmutablePair<Double, Double> prevPlacement = null;
184         RelativePosition relationToPrevElement = null;
185         for (ComponentInstance curr : componentInstances) {
186             elementsCounter++;
187             if (elementsCounter == 1) {
188                 currPlacement = new ImmutablePair<>(0D, 0D);
189             } else if (elementsCounter == 2) {
190                 currPlacement = new ImmutablePair<>(-1D, 0D);
191                 relationToPrevElement = RelativePosition.LEFT;
192             } else {
193                 relationToPrevElement = getRelativePositionForCurrentElement(componentInstanceLocations, relationToPrevElement, prevPlacement);
194                 currPlacement = getRelativeElementLocation(prevPlacement, relationToPrevElement);
195             }
196             componentInstanceLocations.put(currPlacement, curr);
197             prevPlacement = currPlacement;
198         }
199     }
200
201     protected Map<ComponentInstance, List<ComponentInstance>> getCpsConnectedToVFC(List<ComponentInstance> allComponentInstances,
202                                                                                    org.openecomp.sdc.be.model.Component component) {
203         Map<ComponentInstance, List<ComponentInstance>> vfcWithItsCps = new HashMap<>();
204         List<RequirementCapabilityRelDef> allRelations = new ArrayList<>();
205         if (component instanceof Resource) {
206             Resource vf = (Resource) component;
207             allRelations = vf.getComponentInstancesRelations();
208         } else if (component instanceof Service) {
209             Service sv = (Service) component;
210             allRelations = sv.getComponentInstancesRelations();
211         }
212         for (ComponentInstance curr : allComponentInstances) {
213             // Filters Only CPs
214             if (curr.getOriginType() == OriginTypeEnum.CP) {
215                 // List Of elements the CP is connected to
216                 List<RequirementCapabilityRelDef> connectedToList = allRelations.stream()
217                     .filter(p -> p.getFromNode().equals(curr.getUniqueId()) || p.getToNode().equals(curr.getUniqueId())).collect(Collectors.toList());
218                 // Adds Only CPs Which are connected to VFC
219                 filterCpConnectedToVFC(allComponentInstances, vfcWithItsCps, curr, connectedToList);
220             }
221         }
222         return vfcWithItsCps;
223     }
224
225     private void filterCpConnectedToVFC(List<ComponentInstance> allComponentInstances, Map<ComponentInstance, List<ComponentInstance>> vfcWithItsCps,
226                                         ComponentInstance currCP, List<RequirementCapabilityRelDef> connectedToTheCPList) {
227         if (!connectedToTheCPList.isEmpty()) {
228             // Set Of Ids Of components Instances which are connected certain CP
229             Set<String> mateIds = connectedToTheCPList.stream()
230                 .map(cpRelation -> cpRelation.getFromNode().equals(currCP.getUniqueId()) ? cpRelation.getToNode() : cpRelation.getFromNode())
231                 .collect(Collectors.toSet());
232             // Vfc Component instance Connected to the CP
233             Optional<ComponentInstance> optionalVfcConnectedToCP = allComponentInstances.stream().
234                 // All instances connected to CP
235                     filter(p -> mateIds.contains(p.getUniqueId())).
236                 // Filter in only VFC connected to the CP
237                     filter(p -> p.getOriginType() == OriginTypeEnum.VFC).findAny();
238             if (optionalVfcConnectedToCP.isPresent()) {
239                 final ComponentInstance vfcWithCps = optionalVfcConnectedToCP.get();
240                 if (vfcWithItsCps.containsKey(vfcWithCps)) {
241                     vfcWithItsCps.get(vfcWithCps).add(currCP);
242                 } else {
243                     List<ComponentInstance> cpsList = new ArrayList<>();
244                     cpsList.add(currCP);
245                     vfcWithItsCps.put(vfcWithCps, cpsList);
246                 }
247             }
248         }
249     }
250
251     private RelativePosition getRelativePositionForCurrentElement(Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstanceLocations,
252                                                                   RelativePosition relationToPrevElement,
253                                                                   ImmutablePair<Double, Double> prevPlacement) {
254         switch (relationToPrevElement) {
255             case LEFT: {
256                 boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.UP, componentInstanceLocations);
257                 relationToPrevElement = isOccupied ? RelativePosition.LEFT : RelativePosition.UP;
258                 break;
259             }
260             case RIGHT: {
261                 boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.DOWN, componentInstanceLocations);
262                 relationToPrevElement = isOccupied ? RelativePosition.RIGHT : RelativePosition.DOWN;
263                 break;
264             }
265             case UP: {
266                 boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.RIGHT, componentInstanceLocations);
267                 relationToPrevElement = isOccupied ? RelativePosition.UP : RelativePosition.RIGHT;
268                 break;
269             }
270             case DOWN: {
271                 boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.LEFT, componentInstanceLocations);
272                 relationToPrevElement = isOccupied ? RelativePosition.DOWN : RelativePosition.LEFT;
273                 break;
274             }
275             default: {
276                 throw new UnsupportedOperationException();
277             }
278         }
279         return relationToPrevElement;
280     }
281
282     private boolean isAdjacentElementOccupied(ImmutablePair<Double, Double> currElement, RelativePosition adjacentElementRelationToCurrElement,
283                                               Map<ImmutablePair<Double, Double>, ComponentInstance> allElements) {
284         ImmutablePair<Double, Double> adjacentElementPosition = getRelativeElementLocation(currElement, adjacentElementRelationToCurrElement);
285         return allElements.containsKey(adjacentElementPosition);
286     }
287
288     private ImmutablePair<Double, Double> getRelativeElementLocation(ImmutablePair<Double, Double> currElement, RelativePosition relativeLocation) {
289         ImmutablePair<Double, Double> relativeElementPosition;
290         switch (relativeLocation) {
291             case LEFT: {
292                 relativeElementPosition = new ImmutablePair<>(currElement.getLeft() - 1, currElement.getRight());
293                 break;
294             }
295             case RIGHT: {
296                 relativeElementPosition = new ImmutablePair<>(currElement.getLeft() + 1, currElement.getRight());
297                 break;
298             }
299             case UP: {
300                 relativeElementPosition = new ImmutablePair<>(currElement.getLeft(), currElement.getRight() + 1);
301                 break;
302             }
303             case DOWN: {
304                 relativeElementPosition = new ImmutablePair<>(currElement.getLeft(), currElement.getRight() - 1);
305                 break;
306             }
307             default: {
308                 throw new UnsupportedOperationException();
309             }
310         }
311         return relativeElementPosition;
312     }
313
314     protected void validateAndSetDefaultCoordinates(ComponentInstance resourceInstance) {
315         int xCenter = CANVAS_WIDTH / 2;
316         int yCenter = CANVAS_HEIGHT / 2;
317         double leftLimit = -10D;
318         double rightLimit = -1D;
319         double generatedDouble = leftLimit + new SecureRandom().nextDouble() * (rightLimit - leftLimit);
320         if (StringUtils.isEmpty(resourceInstance.getPosX()) || StringUtils.isEmpty(resourceInstance.getPosY())) {
321             resourceInstance.setPosX(calculateCompositionPosition(xCenter, generatedDouble, resourceInstance));
322             resourceInstance.setPosY(calculateCompositionPosition(yCenter, generatedDouble, resourceInstance));
323             log.debug("Missing Failed PosX/PosY values. new values generated automatically. PosX = {} and PosY = {}", resourceInstance.getPosX(),
324                 resourceInstance.getPosY());
325         }
326     }
327
328     enum RelativePosition {LEFT, RIGHT, UP, DOWN}
329 }