2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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 * ================================================================================
22 package org.openecomp.sdc.be.components.impl;
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;
30 import java.util.Map.Entry;
31 import java.util.Optional;
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;
47 * This class holds the logic of arranging resource instance on the canvas for imported VF
51 @Component("compositionBusinessLogic")
52 public class CompositionBusinessLogic {
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;
64 public CompositionBusinessLogic(ComponentInstanceBusinessLogic componentInstanceBusinessLogic) {
65 this.componentInstanceBusinessLogic = componentInstanceBusinessLogic;
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(
75 // Set Relative Locations According to Canvas Size
76 componentInstanceLocations.entrySet().forEach(this::setRelativePosition);
78 componentInstanceBusinessLogic.updateComponentInstance(ComponentTypeEnum.RESOURCE_PARAM_NAME, resource, resource.getUniqueId(), userId,
79 resource.getComponentInstances(), false);
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(
92 // Set Relative Locations According to Canvas Size
93 componentInstanceLocations.entrySet().forEach(this::setRelativePosition);
95 componentInstanceBusinessLogic.updateComponentInstance(ComponentTypeEnum.SERVICE_PARAM_NAME, service, service.getUniqueId(), userId,
96 service.getComponentInstances(), false);
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));
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()) {
114 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.CP_CANVAS_ELEMENT_SIZE / 2.0;
117 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.CP_CANVAS_ELEMENT_SIZE / 2.0;
120 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2.0;
123 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2.0;
126 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2.0;
129 offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2.0;
132 return String.valueOf(offsetedCanvasPosition);
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);
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;
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));
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;
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) {
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;
193 relationToPrevElement = getRelativePositionForCurrentElement(componentInstanceLocations, relationToPrevElement, prevPlacement);
194 currPlacement = getRelativeElementLocation(prevPlacement, relationToPrevElement);
196 componentInstanceLocations.put(currPlacement, curr);
197 prevPlacement = currPlacement;
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();
212 for (ComponentInstance curr : allComponentInstances) {
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);
222 return vfcWithItsCps;
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);
243 List<ComponentInstance> cpsList = new ArrayList<>();
245 vfcWithItsCps.put(vfcWithCps, cpsList);
251 private RelativePosition getRelativePositionForCurrentElement(Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstanceLocations,
252 RelativePosition relationToPrevElement,
253 ImmutablePair<Double, Double> prevPlacement) {
254 switch (relationToPrevElement) {
256 boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.UP, componentInstanceLocations);
257 relationToPrevElement = isOccupied ? RelativePosition.LEFT : RelativePosition.UP;
261 boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.DOWN, componentInstanceLocations);
262 relationToPrevElement = isOccupied ? RelativePosition.RIGHT : RelativePosition.DOWN;
266 boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.RIGHT, componentInstanceLocations);
267 relationToPrevElement = isOccupied ? RelativePosition.UP : RelativePosition.RIGHT;
271 boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.LEFT, componentInstanceLocations);
272 relationToPrevElement = isOccupied ? RelativePosition.DOWN : RelativePosition.LEFT;
276 throw new UnsupportedOperationException();
279 return relationToPrevElement;
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);
288 private ImmutablePair<Double, Double> getRelativeElementLocation(ImmutablePair<Double, Double> currElement, RelativePosition relativeLocation) {
289 ImmutablePair<Double, Double> relativeElementPosition;
290 switch (relativeLocation) {
292 relativeElementPosition = new ImmutablePair<>(currElement.getLeft() - 1, currElement.getRight());
296 relativeElementPosition = new ImmutablePair<>(currElement.getLeft() + 1, currElement.getRight());
300 relativeElementPosition = new ImmutablePair<>(currElement.getLeft(), currElement.getRight() + 1);
304 relativeElementPosition = new ImmutablePair<>(currElement.getLeft(), currElement.getRight() - 1);
308 throw new UnsupportedOperationException();
311 return relativeElementPosition;
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());
328 enum RelativePosition {LEFT, RIGHT, UP, DOWN}