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