@finos/legend-application-studio
Version:
Legend Studio application core
352 lines • 17.8 kB
JavaScript
/**
* 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 { action, observable, makeObservable } from 'mobx';
import { LogEvent, IllegalStateError, isNonNullable, UnsupportedOperationError, guaranteeNonNullable, ActionState, } from '@finos/legend-shared';
import { getDependenciesPackableElementTreeData, getPackableElementTreeData, openNode, openNodeById, openNodes, populatePackageTreeNodeChildren, } from './utils/PackageTreeUtils.js';
import { LEGEND_STUDIO_APP_EVENT } from '../../__lib__/LegendStudioEvent.js';
import { getFileSystemTreeData, } from './utils/FileSystemTreeUtils.js';
import { ROOT_PACKAGE_NAME, Package, Unit, PrimitiveType, getElementRootPackage, isDependencyElement, } from '@finos/legend-graph';
import { APPLICATION_EVENT } from '@finos/legend-application';
import { DatabaseBuilderWizardState } from './editor-state/element-editor-state/connection/DatabaseBuilderWizardState.js';
import { DatabaseModelBuilderState } from './editor-state/element-editor-state/connection/DatabaseModelBuilderState.js';
export var ExplorerTreeRootPackageLabel;
(function (ExplorerTreeRootPackageLabel) {
ExplorerTreeRootPackageLabel["FILE_GENERATION"] = "generated-files";
ExplorerTreeRootPackageLabel["MODEL_GENERATION"] = "generated-models";
ExplorerTreeRootPackageLabel["SYSTEM"] = "system";
ExplorerTreeRootPackageLabel["PROJECT_DEPENDENCY"] = "dependencies";
})(ExplorerTreeRootPackageLabel || (ExplorerTreeRootPackageLabel = {}));
export class ExplorerTreeState {
editorStore;
buildState = ActionState.create();
treeData;
generationTreeData;
systemTreeData;
legalTreeData;
dependencyTreeData;
selectedNode;
artifactsGenerationTreeData;
elementToRename;
classToGenerateSampleData;
databaseBuilderState;
databaseModelBuilderState;
constructor(editorStore) {
makeObservable(this, {
treeData: observable.ref,
generationTreeData: observable.ref,
systemTreeData: observable.ref,
legalTreeData: observable.ref,
dependencyTreeData: observable.ref,
artifactsGenerationTreeData: observable.ref,
selectedNode: observable.ref,
elementToRename: observable,
classToGenerateSampleData: observable,
databaseBuilderState: observable,
databaseModelBuilderState: observable,
setTreeData: action,
setGenerationTreeData: action,
setSystemTreeData: action,
setLegalTreeData: action,
setDependencyTreeData: action,
setArtifactsGenerationTreeData: action,
setSelectedNode: action,
setElementToRename: action,
setClassToGenerateSampleData: action,
build: action,
buildImmutableModelTrees: action,
buildTreeInTextMode: action,
buildDatabaseModels: action,
openExplorerTreeNodes: action,
reprocess: action,
buildDatabase: action,
setDatabaseBuilderState: action,
onTreeNodeSelect: action,
openNode: action,
});
this.editorStore = editorStore;
}
getTreeData(rootPackageName = ROOT_PACKAGE_NAME.MAIN) {
let treeData;
switch (rootPackageName) {
case ROOT_PACKAGE_NAME.MODEL_GENERATION:
treeData = this.generationTreeData;
break;
case ROOT_PACKAGE_NAME.SYSTEM:
treeData = this.systemTreeData;
break;
case ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT:
treeData = this.dependencyTreeData;
break;
default:
treeData = this.treeData;
}
if (!treeData || !this.buildState.hasCompleted) {
this.editorStore.applicationStore.logService.error(LogEvent.create(APPLICATION_EVENT.ILLEGAL_APPLICATION_STATE_OCCURRED), `Can't get explorer tree data for root package '${rootPackageName}' as it hasn't been initialized`);
throw new IllegalStateError(`Can't get explorer tree data for root package '${rootPackageName}' as it hasn't been initialized`);
}
return treeData;
}
getSelectedNodePackage() {
if (!this.selectedNode) {
return this.editorStore.graphManagerState.graph.root;
}
return this.selectedNode.packageableElement instanceof Package
? this.selectedNode.packageableElement
: (this.selectedNode.packageableElement.package ??
this.editorStore.graphManagerState.graph.root);
}
setTreeData(data) {
this.treeData = data;
}
setGenerationTreeData(data) {
this.generationTreeData = data;
}
setSystemTreeData(data) {
this.systemTreeData = data;
}
setLegalTreeData(data) {
this.legalTreeData = data;
}
setDependencyTreeData(data) {
this.dependencyTreeData = data;
}
setArtifactsGenerationTreeData(data) {
this.artifactsGenerationTreeData = data;
}
setElementToRename(val) {
this.elementToRename = val;
}
setClassToGenerateSampleData(val) {
this.classToGenerateSampleData = val;
}
setDatabaseBuilderState(val) {
this.databaseBuilderState = val;
}
buildDatabase(val, isReadOnly) {
const dbBuilderState = new DatabaseBuilderWizardState(this.editorStore, val, isReadOnly);
dbBuilderState.setShowModal(true);
this.setDatabaseBuilderState(dbBuilderState);
}
setDatabaseModelBuilderState(val) {
this.databaseModelBuilderState = val;
}
buildDatabaseModels(val, isReadOnly) {
const dbBuilderState = new DatabaseModelBuilderState(this.editorStore, val, isReadOnly, this.editorStore.graphManagerState.graph);
dbBuilderState.setShowModal(true);
this.setDatabaseModelBuilderState(dbBuilderState);
}
setSelectedNode(node) {
if (this.selectedNode) {
this.selectedNode.isSelected = false;
}
if (node) {
node.isSelected = true;
}
this.selectedNode = node;
}
build() {
this.buildState.reset();
this.treeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.root, '');
this.generationTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.generationModel.root, ExplorerTreeRootPackageLabel.MODEL_GENERATION);
this.artifactsGenerationTreeData = getFileSystemTreeData(this.editorStore.graphState.graphGenerationState.rootFileDirectory, ExplorerTreeRootPackageLabel.FILE_GENERATION);
this.setSelectedNode(undefined);
this.buildState.complete();
}
buildTreeInTextMode() {
this.buildState.reset();
if (!this.systemTreeData) {
this.systemTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.systemModel.root, ExplorerTreeRootPackageLabel.SYSTEM);
}
if (!this.dependencyTreeData) {
this.dependencyTreeData = getDependenciesPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.dependencyManager.roots, ExplorerTreeRootPackageLabel.PROJECT_DEPENDENCY);
}
this.treeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.root, '');
this.generationTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.generationModel.root, ExplorerTreeRootPackageLabel.MODEL_GENERATION);
this.artifactsGenerationTreeData = getFileSystemTreeData(this.editorStore.graphState.graphGenerationState.rootFileDirectory, ExplorerTreeRootPackageLabel.FILE_GENERATION);
this.setSelectedNode(undefined);
this.buildState.complete();
}
buildImmutableModelTrees() {
this.systemTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.systemModel.root, ExplorerTreeRootPackageLabel.SYSTEM);
this.dependencyTreeData = getDependenciesPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.dependencyManager.roots, ExplorerTreeRootPackageLabel.PROJECT_DEPENDENCY);
}
openExplorerTreeNodes(mainTreeOpenedNodeIds, generationTreeOpenedNodeIds, selectedNodeId) {
// Main tree
{
const openElements = new Set(mainTreeOpenedNodeIds
.map((id) => this.editorStore.graphManagerState.graph.getNullableElement(id, true))
.filter(isNonNullable));
openNodes(this.editorStore, this.editorStore.graphManagerState.graph.root, openElements, guaranteeNonNullable(this.treeData));
}
// Generated tree
{
const openElements = new Set(generationTreeOpenedNodeIds
.map((id) => this.editorStore.graphManagerState.graph.generationModel.getOwnNullableElement(id, true))
.filter(isNonNullable));
openNodes(this.editorStore, this.editorStore.graphManagerState.graph.generationModel.root, openElements, guaranteeNonNullable(this.generationTreeData));
if (generationTreeOpenedNodeIds.includes(ROOT_PACKAGE_NAME.MODEL_GENERATION)) {
openNodeById(ROOT_PACKAGE_NAME.MODEL_GENERATION, this.generationTreeData);
}
}
{
if (selectedNodeId) {
const element = this.editorStore.graphManagerState.graph.getNullableElement(selectedNodeId);
if (element) {
const openingNode = openNode(this.editorStore, element, guaranteeNonNullable(this.treeData)) ??
openNode(this.editorStore, element, guaranteeNonNullable(this.generationTreeData));
// ?? openNode(element, this.systemTreeData)
// ?? openNode(element, this.legalTreeData)
// ?? openNode(element, this.dependencyTreeData);
this.setSelectedNode(openingNode);
}
else {
this.setSelectedNode(undefined);
}
}
}
}
/**
* FIXME: this method should be replaced altogether as this could potentially cause memory leak when we `replace` the graph
* When we refresh the graph (after compilation in text mode for example), we want to reprocess the app to
* preserve the status of the explorer tree (opening nodes, selected nodes, etc.)
*
* @risk memory-leak
*/
reprocess() {
this.buildState.reset();
if (!this.systemTreeData) {
this.systemTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.systemModel.root, ExplorerTreeRootPackageLabel.SYSTEM);
}
if (!this.dependencyTreeData) {
this.dependencyTreeData = getDependenciesPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.dependencyManager.roots, ExplorerTreeRootPackageLabel.PROJECT_DEPENDENCY);
}
// Main tree
{
const openedTreeNodeIds = this.treeData
? Array.from(this.treeData.nodes.values())
.filter((node) => node.isOpen)
.map((node) => node.id)
: [];
this.treeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.root, '');
const openElements = new Set(openedTreeNodeIds
.map((id) => this.editorStore.graphManagerState.graph.getNullableElement(id, true))
.filter(isNonNullable));
openNodes(this.editorStore, this.editorStore.graphManagerState.graph.root, openElements, this.treeData);
}
// Generated tree
{
const openedTreeNodeIds = this.generationTreeData
? Array.from(this.generationTreeData.nodes.values())
.filter((node) => node.isOpen)
.map((node) => node.id)
: [];
this.generationTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.generationModel.root, ExplorerTreeRootPackageLabel.MODEL_GENERATION);
const openElements = new Set(openedTreeNodeIds
.map((id) => this.editorStore.graphManagerState.graph.generationModel.getOwnNullableElement(id, true))
.filter(isNonNullable));
openNodes(this.editorStore, this.editorStore.graphManagerState.graph.generationModel.root, openElements, this.generationTreeData);
if (openedTreeNodeIds.includes(ROOT_PACKAGE_NAME.MODEL_GENERATION)) {
openNodeById(ROOT_PACKAGE_NAME.MODEL_GENERATION, this.generationTreeData);
}
}
// File generation tree
// TODO: fix this so it does proper reprocessing, right now it just rebuilds
this.artifactsGenerationTreeData = getFileSystemTreeData(this.editorStore.graphState.graphGenerationState.rootFileDirectory, ExplorerTreeRootPackageLabel.FILE_GENERATION);
if (this.selectedNode) {
const element = this.editorStore.graphManagerState.graph.getNullableElement(this.selectedNode.id);
if (element) {
const openingNode = openNode(this.editorStore, element, this.treeData) ??
openNode(this.editorStore, element, this.generationTreeData);
// ?? openNode(element, this.systemTreeData)
// ?? openNode(element, this.legalTreeData)
// ?? openNode(element, this.dependencyTreeData);
this.setSelectedNode(openingNode);
}
else {
this.setSelectedNode(undefined);
}
}
this.setTreeData({ ...this.treeData });
this.setGenerationTreeData({ ...this.generationTreeData });
this.buildState.complete();
}
onTreeNodeSelect = (node, treeData, rootPackageName = ROOT_PACKAGE_NAME.MAIN) => {
// Open non-package element
if (!(node.packageableElement instanceof Package)) {
this.editorStore.graphEditorMode.openElement(node.packageableElement);
}
// Expand package element
if (node.childrenIds?.length) {
node.isOpen = !node.isOpen;
if (node.packageableElement instanceof Package) {
populatePackageTreeNodeChildren(this.editorStore, node, treeData, rootPackageName === ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT);
}
}
this.setSelectedNode(node);
switch (rootPackageName) {
case ROOT_PACKAGE_NAME.MODEL_GENERATION:
this.setGenerationTreeData({ ...treeData });
break;
case ROOT_PACKAGE_NAME.SYSTEM:
this.setSystemTreeData({ ...treeData });
break;
case ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT:
this.setDependencyTreeData({ ...treeData });
break;
default:
this.setTreeData({ ...treeData });
}
};
/**
* Given an element we open the node depending on what package tree corresponds to it
*/
openNode(element) {
if (element instanceof PrimitiveType || element instanceof Unit) {
throw new UnsupportedOperationError(`Can't open package tree node for element`, element);
}
const rootPackageName = getElementRootPackage(element).name;
let opened = false;
if (rootPackageName === ROOT_PACKAGE_NAME.MAIN && this.treeData) {
const openingNode = openNode(this.editorStore, element, this.treeData);
this.setSelectedNode(openingNode);
opened = true;
}
else if (rootPackageName === ROOT_PACKAGE_NAME.MODEL_GENERATION &&
this.generationTreeData) {
const openingNode = openNode(this.editorStore, element, this.generationTreeData);
this.setSelectedNode(openingNode);
opened = true;
}
else if (rootPackageName === ROOT_PACKAGE_NAME.SYSTEM &&
this.systemTreeData) {
const openingNode = openNode(this.editorStore, element, this.systemTreeData);
this.setSelectedNode(openingNode);
opened = true;
}
else if (isDependencyElement(element) && this.dependencyTreeData) {
const openingNode = openNode(this.editorStore, element, this.dependencyTreeData, undefined, true);
this.setSelectedNode(openingNode);
opened = true;
}
if (!opened) {
this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.PACKAGE_TREE_BUILDER_FAILURE), `Can't open package tree node for element '${element.path}' with root package '${rootPackageName}'`);
}
}
getArtifactsGenerationTreeData() {
return guaranteeNonNullable(this.artifactsGenerationTreeData, 'File generation tree data has not been initialized');
}
}
//# sourceMappingURL=ExplorerTreeState.js.map