UNPKG

@finos/legend-application-studio

Version:
309 lines 14.2 kB
/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { isNonNullable, addUniqueEntry, guaranteeNonNullable, } from '@finos/legend-shared'; import { CORE_DND_TYPE } from './DnDUtils.js'; import { ROOT_PACKAGE_NAME, Package, Class, Enumeration, Profile, Association, ConcreteFunctionDefinition, Measure, Unit, Database, FlatData, Mapping, Service, PackageableRuntime, PackageableConnection, FileGenerationSpecification, GenerationSpecification, DataElement, generateFunctionPrettyName, getElementRootPackage, extractDependencyGACoordinateFromRootPackageName, generateDependencyRootPackageName, } from '@finos/legend-graph'; import { ExplorerTreeRootPackageLabel } from '../ExplorerTreeState.js'; const getElementProjectExplorerDnDType = (editorStore, element) => { if (element instanceof Package) { return CORE_DND_TYPE.PROJECT_EXPLORER_PACKAGE; } else if (element instanceof Class) { return CORE_DND_TYPE.PROJECT_EXPLORER_CLASS; } else if (element instanceof Association) { return CORE_DND_TYPE.PROJECT_EXPLORER_ASSOCIATION; } else if (element instanceof Enumeration) { return CORE_DND_TYPE.PROJECT_EXPLORER_ENUMERATION; } else if (element instanceof Profile) { return CORE_DND_TYPE.PROJECT_EXPLORER_PROFILE; } else if (element instanceof ConcreteFunctionDefinition) { return CORE_DND_TYPE.PROJECT_EXPLORER_FUNCTION; } else if (element instanceof Measure) { return CORE_DND_TYPE.PROJECT_EXPLORER_MEASURE; } else if (element instanceof FlatData) { return CORE_DND_TYPE.PROJECT_EXPLORER_FLAT_DATA; } else if (element instanceof Database) { return CORE_DND_TYPE.PROJECT_EXPLORER_DATABASE; } else if (element instanceof Mapping) { return CORE_DND_TYPE.PROJECT_EXPLORER_MAPPING; } else if (element instanceof PackageableRuntime) { return CORE_DND_TYPE.PROJECT_EXPLORER_RUNTIME; } else if (element instanceof PackageableConnection) { return CORE_DND_TYPE.PROJECT_EXPLORER_CONNECTION; } else if (element instanceof Service) { return CORE_DND_TYPE.PROJECT_EXPLORER_SERVICE; } else if (element instanceof GenerationSpecification) { return CORE_DND_TYPE.PROJECT_EXPLORER_GENERATION_TREE; } else if (element instanceof FileGenerationSpecification) { return CORE_DND_TYPE.PROJECT_EXPLORER_FILE_GENERATION; } else if (element instanceof DataElement) { return CORE_DND_TYPE.PROJECT_EXPLORER_DATA; } const extraDragElementClassifiers = editorStore.pluginManager .getApplicationPlugins() .flatMap((plugin) => plugin.getExtraDragElementClassifiers?.() ?? []); for (const classifier of extraDragElementClassifiers) { const dragType = classifier(element); if (dragType) { return dragType; } } return CORE_DND_TYPE.NONE; }; export const getSelectedPackageTreeNodePackage = (node) => node ? node.packageableElement instanceof Package ? node.packageableElement : node.packageableElement.package : undefined; export const generatePackageableElementTreeNodeDataLabel = (element) => element instanceof ConcreteFunctionDefinition ? generateFunctionPrettyName(element) : element.name; export const getPackableElementTreeNodeData = (editorStore, element, childFilter) => ({ id: element.path, dndType: getElementProjectExplorerDnDType(editorStore, element), label: generatePackageableElementTreeNodeDataLabel(element), childrenIds: element instanceof Package ? element.children .filter((child) => !(child instanceof Unit)) // remove unit from package tree .filter((child) => child instanceof Package || !childFilter || childFilter(child)) .map((child) => child.path) : undefined, packageableElement: element, }); export const getDependencyPackableElementTreeNodeData = (editorStore, element, rootName, isDependencyRoot, childFilter) => ({ id: isDependencyRoot ? element.path : `${rootName}::${element.path}`, dndType: getElementProjectExplorerDnDType(editorStore, element), label: generatePackageableElementTreeNodeDataLabel(element), childrenIds: element instanceof Package ? element.children .filter((child) => !(child instanceof Unit)) // remove unit from package tree .filter((child) => child instanceof Package || !childFilter || childFilter(child)) .map((child) => `${rootName}::${child.path}`) : undefined, packageableElement: element, }); export const getPackableElementTreeData = (editorStore, _package, rootWrapperName, childFilter) => { const rootIds = []; const nodes = new Map(); if (rootWrapperName === '') { _package.children .slice() .filter((child) => !(child instanceof Unit)) // remove unit from package tree // packages comes first, within each group, sort by name .sort((a, b) => a.name.localeCompare(b.name)) .sort((a, b) => (b instanceof Package ? 1 : 0) - (a instanceof Package ? 1 : 0)) .forEach((childPackage) => { const childTreeNodeData = getPackableElementTreeNodeData(editorStore, childPackage, childFilter); addUniqueEntry(rootIds, childTreeNodeData.id); nodes.set(childTreeNodeData.id, childTreeNodeData); }); } else { const rootNode = getPackableElementTreeNodeData(editorStore, _package, childFilter); rootNode.label = rootWrapperName; addUniqueEntry(rootIds, rootNode.id); nodes.set(rootNode.id, rootNode); } return { rootIds, nodes }; }; export const getDependenciesPackableElementTreeData = (editorStore, _packages, rootWrapperName, childFilter) => { const rootIds = []; // Here we warp all the dependency roots with an new root node 'dependencies' // We push all the dependency roots as children nodes of this new node so that // we can show the tree structure of dependencies wrapped with the label 'dependencies'. // In future we would want to support a new tree node type to accomodate these // excpetions to the existing structure. const root = new Package(ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT); const nodes = new Map(); _packages.forEach((_package) => { const childRootNode = getDependencyPackableElementTreeNodeData(editorStore, _package, _package.name, true, childFilter); const dependencyGACoordinates = extractDependencyGACoordinateFromRootPackageName(_package.name) ?? _package.name; childRootNode.label = generateDependencyRootPackageName(dependencyGACoordinates); childRootNode.id = generateDependencyRootPackageName(dependencyGACoordinates); addUniqueEntry(rootIds, childRootNode.id); nodes.set(childRootNode.id, childRootNode); root.children.push(_package); }); const rootNode = { id: rootWrapperName, dndType: getElementProjectExplorerDnDType(editorStore, root), label: rootWrapperName, childrenIds: rootIds, packageableElement: root, }; rootNode.label = rootWrapperName; nodes.set(rootNode.id, rootNode); const ids = []; ids.push(rootNode.id); return { rootIds: ids, nodes }; }; /** * Resolve all children of a node */ export const populatePackageTreeNodeChildren = (editorStore, node, treeData, isDependencyTree) => { if (node.childrenIds && node.packageableElement instanceof Package && node.id === ExplorerTreeRootPackageLabel.PROJECT_DEPENDENCY) { // do nothing } else if (node.childrenIds && node.packageableElement instanceof Package && isDependencyTree) { const rootNodeName = node.id.split('::')[0] ?? node.id; node.childrenIds = node.packageableElement.children .filter((child) => !(child instanceof Unit)) // remove unit from package tree .map((child) => `${rootNodeName}::${child.path}`); node.packageableElement.children .filter((child) => !(child instanceof Unit)) // remove unit from package tree .map((child) => getDependencyPackableElementTreeNodeData(editorStore, child, rootNodeName, false)) .forEach((childNode) => { const currentNode = treeData.nodes.get(childNode.id); if (currentNode) { // Note here that we keep track of isSelected status using reference, we cannot swap out the currentNode to use new childNode currentNode.childrenIds = childNode.childrenIds; currentNode.label = childNode.label; } else { treeData.nodes.set(childNode.id, childNode); } }); } else if (node.childrenIds && node.packageableElement instanceof Package) { node.childrenIds = node.packageableElement.children .filter((child) => !(child instanceof Unit)) // remove unit from package tree .map((child) => child.path); node.packageableElement.children .filter((child) => !(child instanceof Unit)) // remove unit from package tree .map((child) => getPackableElementTreeNodeData(editorStore, child)) .forEach((childNode) => { const currentNode = treeData.nodes.get(childNode.id); if (currentNode) { // Note here that we keep track of isSelected status using reference, we cannot swap out the currentNode to use new childNode currentNode.childrenIds = childNode.childrenIds; currentNode.label = childNode.label; } else { treeData.nodes.set(childNode.id, childNode); } }); } }; export const addNode = (editorStore, element, treeData, childFilter, isDependencyTreeNode) => { const rootNodeName = getElementRootPackage(element).name; const newNode = isDependencyTreeNode ? getDependencyPackableElementTreeNodeData(editorStore, element, rootNodeName, false, childFilter) : getPackableElementTreeNodeData(editorStore, element, childFilter); treeData.nodes.set(newNode.id, newNode); if (!element.package || element.package.path === ROOT_PACKAGE_NAME.MAIN) { treeData.rootIds = Array.from(new Set(treeData.rootIds).add(newNode.id)); } else if (element.package.path === ROOT_PACKAGE_NAME.SYSTEM || element.package.path === ROOT_PACKAGE_NAME.MODEL_GENERATION) { const baseNode = treeData.nodes.get(element.package.path); if (baseNode) { baseNode.isOpen = true; } } else { const parentNode = treeData.nodes.get(element.package.path); if (parentNode) { parentNode.childrenIds = parentNode.childrenIds ? Array.from(new Set(parentNode.childrenIds).add(newNode.id)) : [newNode.id]; } } return newNode; }; export const openNode = (editorStore, element, treeData, childFilter, isDependencyElement) => { let currentElement = element; let openingNode; while (currentElement.package) { const currentNode = treeData.nodes.get(currentElement.path) ?? addNode(editorStore, currentElement, treeData, childFilter, isDependencyElement); currentNode.isOpen = currentElement instanceof Package; openingNode = !openingNode ? currentNode : openingNode; currentElement = currentElement.package; } // Open the dependency root const node = treeData.nodes.get(currentElement.name); if (node) { node.isOpen = currentElement instanceof Package; if (node.isOpen && treeData.rootIds.length) { const rootNode = treeData.nodes.get(guaranteeNonNullable(treeData.rootIds[0])); if (rootNode) { rootNode.isOpen = true; } } } return openingNode; }; export const openNodes = (editorStore, parent, activeElements, treeData) => { if (!parent.children.filter((child) => !(child instanceof Unit)).length || // remove unit from package tree !activeElements.size) { return; } parent.children .filter((child) => !(child instanceof Unit)) // remove unit from package tree .forEach((child) => { if (activeElements.has(child)) { openNode(editorStore, child, treeData); activeElements.delete(child); if (!activeElements.size) { return; } } if (child instanceof Package) { openNodes(editorStore, child, activeElements, treeData); } }); }; export const openNodeById = (id, treeData) => { if (treeData) { const node = treeData.nodes.get(id); if (node) { node.isOpen = true; } } }; export const getTreeChildNodes = (editorStore, node, treeData, isDependencyTree) => { if (node.childrenIds && node.packageableElement instanceof Package) { populatePackageTreeNodeChildren(editorStore, node, treeData, isDependencyTree); return (node.childrenIds .map((id) => treeData.nodes.get(id)) .filter(isNonNullable) // packages comes first, within each group, sort by name .sort((a, b) => a.label.localeCompare(b.label)) .sort((a, b) => (b.packageableElement instanceof Package ? 1 : 0) - (a.packageableElement instanceof Package ? 1 : 0))); } return []; }; //# sourceMappingURL=PackageTreeUtils.js.map