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