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