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=========================================================
21 import * as _ from "lodash";
22 import {ComponentInstance, Component, Match, CompositionCiLinkBase, CompositionCiNodeUcpeCp} from "app/models";
23 import {QueueUtils, Dictionary, GraphUIObjects} from "app/utils";
24 import {LoaderService} from "app/services";
25 import {MatchCapabilitiesRequirementsUtils} from "./match-capability-requierment-utils";
26 import {CommonGraphUtils} from "../../common/common-graph-utils";
29 export class CompositionGraphGeneralUtils {
31 public componentRequirementsAndCapabilitiesCaching = new Dictionary<string, Component>();
32 protected static graphUtilsUpdateQueue:QueueUtils;
34 constructor(private $q:ng.IQService,
35 private LoaderService:LoaderService,
36 private commonGraphUtils:CommonGraphUtils,
37 private matchCapabilitiesRequirementsUtils:MatchCapabilitiesRequirementsUtils) {
38 CompositionGraphGeneralUtils.graphUtilsUpdateQueue = new QueueUtils(this.$q);
43 * Get the offset for the link creation Menu
45 * @returns {Cy.Position}
47 public calcMenuOffset:Function = (point:Cy.Position):Cy.Position => {
48 point.x = point.x + 60;
49 point.y = point.y + 105;
54 * return the top left position of the link menu
56 * @param targetNodePosition
57 * @returns {Cy.Position}
59 public getLinkMenuPosition = (cy:Cy.Instance, targetNodePosition:Cy.Position) => {
60 let menuPosition:Cy.Position = this.calcMenuOffset(targetNodePosition); //get the link mid point
61 if ($(document.body).height() < menuPosition.y + GraphUIObjects.LINK_MENU_HEIGHT + $(document.getElementsByClassName('sdc-composition-graph-wrapper')).offset().top) { // if position menu is overflow bottom
62 menuPosition.y = $(document.body).height() - GraphUIObjects.TOP_HEADER_HEIGHT - GraphUIObjects.LINK_MENU_HEIGHT;
68 public zoomGraphTo = (cy:Cy.Instance, zoomLevel: number):void => {
69 let zy = cy.height() / 2;
70 let zx = cy.width() / 2;
73 renderedPosition: { x: zx, y: zy }
77 * will return true/false if two nodes overlapping
81 private isNodesOverlapping(node:Cy.CollectionFirstNode, draggedNode:Cy.CollectionFirstNode):boolean {
83 let nodeBoundingBox:Cy.BoundingBox = node.renderedBoundingBox();
84 let secondNodeBoundingBox:Cy.BoundingBox = draggedNode.renderedBoundingBox();
86 return this.isBBoxOverlapping(nodeBoundingBox, secondNodeBoundingBox);
90 * Checks whether the bounding boxes of two nodes are overlapping on any side
95 private isBBoxOverlapping(nodeOneBBox:Cy.BoundingBox, nodeTwoBBox:Cy.BoundingBox) {
96 return (((nodeOneBBox.x1 < nodeTwoBBox.x1 && nodeOneBBox.x2 > nodeTwoBBox.x1) ||
97 (nodeOneBBox.x1 < nodeTwoBBox.x2 && nodeOneBBox.x2 > nodeTwoBBox.x2) ||
98 (nodeTwoBBox.x1 < nodeOneBBox.x1 && nodeTwoBBox.x2 > nodeOneBBox.x2)) &&
99 ((nodeOneBBox.y1 < nodeTwoBBox.y1 && nodeOneBBox.y2 > nodeTwoBBox.y1) ||
100 (nodeOneBBox.y1 < nodeTwoBBox.y2 && nodeOneBBox.y2 > nodeTwoBBox.y2) ||
101 (nodeTwoBBox.y1 < nodeOneBBox.y1 && nodeTwoBBox.y2 > nodeOneBBox.y2)))
106 * Checks whether a specific component instance can be hosted on the UCPE instance
107 * @param cy - Cytoscape instance
108 * @param fromUcpeInstance
109 * @param toComponentInstance
112 public canBeHostedOn(cy:Cy.Instance, fromUcpeInstance:ComponentInstance, toComponentInstance:ComponentInstance):Match {
114 let matches:Array<Match> = this.matchCapabilitiesRequirementsUtils.getMatchedRequirementsCapabilities(fromUcpeInstance, toComponentInstance, this.getAllCompositionCiLinks(cy));
115 let hostedOnMatch:Match = _.find(matches, (match:Match) => {
116 return match.requirement.capability.toLowerCase() === 'tosca.capabilities.container';
119 return hostedOnMatch;
124 * Checks whether node can be dropped into UCPE
126 * @param nodeToInsert
130 private isValidDropInsideUCPE(cy:Cy.Instance, nodeToInsert:ComponentInstance, ucpeNode:ComponentInstance):boolean {
132 let hostedOnMatch:Match = this.canBeHostedOn(cy, ucpeNode, nodeToInsert);
133 let result:boolean = !angular.isUndefined(hostedOnMatch) || nodeToInsert.isVl(); //group validation
140 * For drops from palette, checks whether the node can be dropped. If node is being held over another node, check if capable of hosting
142 * @param pseudoNodeBBox
143 * @param paletteComponentInstance
146 public isPaletteDropValid(cy:Cy.Instance, pseudoNodeBBox:Cy.BoundingBox, paletteComponentInstance:ComponentInstance) {
148 let componentIsUCPE:boolean = (paletteComponentInstance.capabilities && paletteComponentInstance.capabilities['tosca.capabilities.Container'] && paletteComponentInstance.name.toLowerCase().indexOf('ucpe') > -1);
150 if (componentIsUCPE && cy.nodes('[?isUcpe]').length > 0) { //second UCPE not allowed
154 let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode:Cy.CollectionFirstNode) => {
156 if (this.isBBoxOverlapping(pseudoNodeBBox, graphNode.renderedBoundingBox())) {
157 if (!componentIsUCPE && graphNode.data().isUcpe) {
158 return !this.isValidDropInsideUCPE(cy, paletteComponentInstance, graphNode.data().componentInstance); //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
166 return illegalOverlappingNodes.length === 0;
170 * will return true/false if a drop of a single node is valid
174 public isValidDrop(cy:Cy.Instance, draggedNode:Cy.CollectionFirstNode):boolean {
176 let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode:Cy.CollectionFirstNode) => { //all sdc nodes, removing child nodes (childe node allways collaps
178 if (draggedNode.data().isUcpe && (graphNode.isChild() || graphNode.data().isInsideGroup)) { //ucpe cps always inside ucpe, no overlapping
181 if (draggedNode.data().isInsideGroup && (!draggedNode.active() || graphNode.data().isUcpe)) {
185 if (!draggedNode.data().isUcpe && !(draggedNode.data() instanceof CompositionCiNodeUcpeCp) && graphNode.data().isUcpe) { //case we are dragging a node into UCPE
186 let isEntirelyInUCPE:boolean = this.commonGraphUtils.isFirstBoxContainsInSecondBox(draggedNode.renderedBoundingBox(), graphNode.renderedBoundingBox());
187 if (isEntirelyInUCPE) {
188 if (this.isValidDropInsideUCPE(cy, draggedNode.data().componentInstance, graphNode.data().componentInstance)) { //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
193 return graphNode.data().id !== draggedNode.data().id && this.isNodesOverlapping(draggedNode, graphNode);
197 return illegalOverlappingNodes.length === 0;
201 * will return true/false if the move of the nodes is valid (no node overlapping and verifying if insert into UCPE is valid)
203 * @param nodesArray - the selected drags nodes
205 public isGroupValidDrop(cy:Cy.Instance, nodesArray:Cy.CollectionNodes):boolean {
206 let filterDraggedNodes = nodesArray.filter('[?isDraggable]');
207 let isValidDrop = _.every(filterDraggedNodes, (node:Cy.CollectionFirstNode) => {
208 return this.isValidDrop(cy, node);
215 * get all links in diagram
217 * @returns {any[]|boolean[]}
219 public getAllCompositionCiLinks = (cy:Cy.Instance):Array<CompositionCiLinkBase> => {
220 return _.map(cy.edges("[isSdcElement]"), (edge:Cy.CollectionEdges) => {
227 * Get Graph Utils server queue
228 * @returns {QueueUtils}
230 public getGraphUtilsServerUpdateQueue():QueueUtils {
231 return CompositionGraphGeneralUtils.graphUtilsUpdateQueue;
237 * @param blockAction - true/false if this is a block action
241 public pushMultipleUpdateComponentInstancesRequestToQueue = (blockAction:boolean, instances:Array<ComponentInstance>, component:Component):void => {
243 this.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
244 () => component.updateMultipleComponentInstances(instances)
247 this.getGraphUtilsServerUpdateQueue().addNonBlockingUIAction(
248 () => component.updateMultipleComponentInstances(instances),
249 () => this.LoaderService.hideLoader('composition-graph'));
254 * this function will update component instance data
255 * @param blockAction - true/false if this is a block action
256 * @param updatedInstance
258 public pushUpdateComponentInstanceActionToQueue = (component:Component, blockAction:boolean, updatedInstance:ComponentInstance):void => {
261 this.LoaderService.showLoader('composition-graph');
262 this.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
263 () => component.updateComponentInstance(updatedInstance)
266 this.getGraphUtilsServerUpdateQueue().addNonBlockingUIAction(
267 () => component.updateComponentInstance(updatedInstance),
268 () => this.LoaderService.hideLoader('composition-graph'));
273 CompositionGraphGeneralUtils.$inject = ['$q', 'LoaderService', 'CommonGraphUtils', 'MatchCapabilitiesRequirementsUtils'];