diff --git a/mscr-ui/public/locales/en/common.json b/mscr-ui/public/locales/en/common.json index 15831027a..262c7c639 100644 --- a/mscr-ui/public/locales/en/common.json +++ b/mscr-ui/public/locales/en/common.json @@ -300,6 +300,7 @@ "search-placeholder": "Find an attribute...", "search-schema": "Search from schema", "selected-node": "Selected node:", + "show-titles": "Show node titles", "tree-label": "Schema tree" }, "search": { diff --git a/mscr-ui/public/locales/fi/common.json b/mscr-ui/public/locales/fi/common.json index 434b5cbb8..2d47ed93c 100644 --- a/mscr-ui/public/locales/fi/common.json +++ b/mscr-ui/public/locales/fi/common.json @@ -300,6 +300,7 @@ "search-placeholder": "", "search-schema": "", "selected-node": "", + "show-titles": "", "tree-label": "" }, "search": { diff --git a/mscr-ui/public/locales/sv/common.json b/mscr-ui/public/locales/sv/common.json index fef1d1951..8e420840b 100644 --- a/mscr-ui/public/locales/sv/common.json +++ b/mscr-ui/public/locales/sv/common.json @@ -300,6 +300,7 @@ "search-placeholder": "", "search-schema": "", "selected-node": "", + "show-titles": "", "tree-label": "" }, "search": { diff --git a/mscr-ui/src/common/components/schema-info/index.tsx b/mscr-ui/src/common/components/schema-info/index.tsx index 40f8a6910..dc559349a 100644 --- a/mscr-ui/src/common/components/schema-info/index.tsx +++ b/mscr-ui/src/common/components/schema-info/index.tsx @@ -1,14 +1,12 @@ -import * as React from 'react'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import {Checkbox, SearchInput, ToggleButton} from 'suomifi-ui-components'; +import { Checkbox, SearchInput } from 'suomifi-ui-components'; import IconButton from '@mui/material/IconButton'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import Box from '@mui/material/Box'; import SchemaTree from '@app/common/components/schema-info/schema-tree'; import NodeInfo from '@app/common/components/schema-info/schema-tree/node-info'; import { RenderTree } from '@app/common/interfaces/crosswalk-connection.interface'; -import { cloneDeep } from 'lodash'; import { generateTreeFromJson } from '@app/common/components/schema-info/schema-tree/schema-tree-renderer'; import { useGetFrontendSchemaQuery } from '@app/common/components/schema/schema.slice'; import { useTranslation } from 'next-i18next'; @@ -22,7 +20,9 @@ import { } from '@app/common/components/schema-info/schema-info.styles'; import { useRouter } from 'next/router'; import { getLanguageVersion } from '@app/common/utils/get-language-version'; -import SpinnerOverlay, { SpinnerType } from "@app/common/components/spinner-overlay"; +import SpinnerOverlay, { + SpinnerType, +} from '@app/common/components/spinner-overlay'; import Tooltip from '@mui/material/Tooltip'; export default function SchemaInfo(props: { @@ -39,52 +39,37 @@ export default function SchemaInfo(props: { }) { const { t } = useTranslation('common'); const lang = useRouter().locale ?? ''; - const emptyTreeSelection: RenderTree = { - elementPath: '', - qname: 'empty', - parentElementPath: undefined, - name: '', - id: '', - visualTreeId: '', - properties: undefined, - children: [], - uri: '', - }; - - const { - data: getSchemaData, - isLoading: getSchemaDataIsLoading, - isSuccess: getSchemaDataIsSuccess, - isError: getSchemaDataIsError, - error: getSchemaDataError, - } = useGetFrontendSchemaQuery(props.schemaUrn); - const [treeDataOriginal, setTreeDataOriginal] = React.useState( - [] - ); - const [treeData, setTreeData] = React.useState([]); - const [treeExpandedArray, setTreeExpanded] = React.useState([]); + const { data: getSchemaData, isSuccess: getSchemaDataIsSuccess } = + useGetFrontendSchemaQuery(props.schemaUrn); + const [treeData, setTreeData] = useState([]); + const [nodeIdToNodeDictionary, setNodeIdToNodeDictionary] = useState<{ + [key: string]: RenderTree[]; + }>({}); // These are used by tree visualization - const [treeSelectedArray, setTreeSelections] = React.useState([]); - + const [treeSelectedArray, setTreeSelectedArray] = useState([]); + const [treeExpandedArray, setTreeExpandedArray] = useState([]); // These are used by datamodel - const [selectedTreeNodes, setSelectedTreeNodes] = React.useState([emptyTreeSelection]); + const [selectedTreeNodes, setSelectedTreeNodes] = useState([]); - const [isTreeDataFetched, setTreeDataFetched] = - React.useState(false); + const [isTreeDataFetched, setTreeDataFetched] = useState(false); - const [showAttributeNames, setShowAttributeNames] = React.useState(true); + const [showAttributeNames, setShowAttributeNames] = useState(true); useEffect(() => { if (getSchemaData?.content) { - generateTreeFromJson(getSchemaData).then((res) => { + // Get two different representations of attributes: a tree and a dictionary with keys being the node ids and + // values being the nodes that have that id, with no children attached + const { generatedTree, nodeIdToShallowNode } = + generateTreeFromJson(getSchemaData); + generatedTree.then((res) => { if (res) { // Expand tree when data is loaded - setExpanded(); - setTreeDataOriginal(cloneDeep(res)); + setPartlyExpanded(); setTreeData(res); setTreeDataFetched(true); + setNodeIdToNodeDictionary(nodeIdToShallowNode); //refetchOriginalSourceSchemaData(); } }); @@ -93,7 +78,7 @@ export default function SchemaInfo(props: { // Expand tree when data is loaded useEffect(() => { - setExpanded(); + setPartlyExpanded(); }, [isTreeDataFetched]); // Expand and select nodes when input changed (from mappings accordion) @@ -105,160 +90,117 @@ export default function SchemaInfo(props: { useEffect(() => { // Update selections for node info and parent component for mappings - const selectedTreeNodeIds = getTreeNodesByIds(treeSelectedArray); + const selectedNodes = treeSelectedArray + .map((nodeId) => nodeIdToNodeDictionary[nodeId]) + .flat(); if ( props.updateTreeNodeSelectionsOutput && props.isSourceTree !== undefined ) { - props.updateTreeNodeSelectionsOutput( - selectedTreeNodeIds, - props.isSourceTree - ); + props.updateTreeNodeSelectionsOutput(selectedNodes, props.isSourceTree); } - setSelectedTreeNodes(selectedTreeNodeIds); + setSelectedTreeNodes(selectedNodes); }, [treeSelectedArray]); - const setExpanded = () => { - const retData: string[] = []; + const setPartlyExpanded = () => { + const nodeIdsToExpand: string[] = []; treeData.forEach(({ children, id }) => { - if (children && children?.length > 0) { - retData.push(id.toString()); - } - }); - setTreeExpanded(() => { - return retData; - }); - }; - - // Used to generate data for mappings modal - function getTreeNodesByIds(nodeIds: string[]) { - const foundSourceNodes: RenderTree[] = []; - return findNodesFromTree(treeDataOriginal, nodeIds, foundSourceNodes); - } - - // Used to tree filtering - function findNodesFromTree( - tree: RenderTree[], - itemsToFind: string[], - results: RenderTree[] - ) { - tree.forEach((item: RenderTree) => { - if (itemsToFind.includes(item.id)) { - results.push(item); - } else { - if (item.children && item.children.length > 0) { - return findNodesFromTree(item.children, itemsToFind, results); + if (children && children.length > 0) { + nodeIdsToExpand.push(id); + if (children.length === 1) { + nodeIdsToExpand.push(children[0].id); } } }); - return results; - } + setTreeExpandedArray(nodeIdsToExpand); + }; function clearTreeSearch() { - setTreeSelections([]); - setTreeData(cloneDeep(treeDataOriginal)); - setExpanded(); - } - - function doFiltering( - tree: RenderTree[], - nameToFind: string, - results: { nodeIds: string[]; childNodeIds: string[] } - ) { - tree.forEach((item) => { - if (showAttributeNames && - item.name && - item.name.toLowerCase().includes(nameToFind.toLowerCase()) || !showAttributeNames && - item.qname && - item.qname.toLowerCase().includes(nameToFind.toLowerCase()) - ) { - results.nodeIds.push(item.id); - if (item.children && item.children.length > 0) { - item.children.forEach((child) => { - results.childNodeIds.push(child.id); - }); - } - } - if (item.children && item.children.length > 0) { - return doFiltering(item.children, nameToFind, results); - } - }); - return results; - } - - function getElementPathsFromTree( - treeData: RenderTree[], - nodeIds: string[], - results: string[] - ) { - treeData.forEach((item) => { - if (nodeIds.includes(item.id)) { - if (item.elementPath != null) { - results.push(item.elementPath); - } - } - if (item.children && item.children.length > 0) { - return getElementPathsFromTree(item.children, nodeIds, results); - } - }); - return results; + setTreeSelectedArray([]); + setPartlyExpanded(); + setSelectedTreeNodes([]); } // Used by tree select and filtering function getAllNodeIdsOnPathToLeaf(nodeIds: string[]) { - const elementPaths = getElementPathsFromTree(treeData, nodeIds, []); - const nodesToSelect: Set = new Set(); + const elementPaths: string[] = []; + nodeIds.forEach((nodeId) => { + const nodes = nodeIdToNodeDictionary[nodeId]; + nodes.map((node) => elementPaths.push(node.elementPath)); + }); + const nodesToSelect: Set = new Set(); elementPaths.forEach((path) => { - const nodes = path.split('.'); - nodes.forEach((node) => { - nodesToSelect.add(node); + const nodeIdsOnPath = path.split('.'); + nodeIdsOnPath.forEach((nodeId) => { + nodesToSelect.add(nodeId); }); }); + return Array.from(nodesToSelect); } const handleExpandClick = () => { - const allTreeNodes: string[] = []; - treeData.forEach(({ children, id }) => { - if (children && children?.length > 0) { - allTreeNodes.push(id.toString()); - } - }); - setTreeExpanded((oldExpanded) => { - return oldExpanded.length === 0 ? allTreeNodes : []; - }); + if (treeExpandedArray.length === 0) { + setPartlyExpanded(); + } else { + setTreeExpandedArray([]); + } }; function expandAndSelectNodes(nodeIds: string[]) { if (nodeIds.length > 0) { const nodeIdsToExpand = getAllNodeIdsOnPathToLeaf(nodeIds); - setTreeExpanded(nodeIdsToExpand); - setTreeSelections(nodeIds); + setTreeExpandedArray(nodeIdsToExpand); + setTreeSelectedArray(nodeIds); } } function searchFromTree(input: string) { clearTreeSearch(); - const hits = { nodeIds: [], childNodeIds: [] }; - doFiltering(treeData, input.toString(), hits); - expandAndSelectNodes(hits.nodeIds); + // The nodeIdToNodeDictionary values are arrays of nodes, because there can be nodes in a schema with identical + // ids but different paths. It's enough to check matching to one of them. + const hits: string[] = []; + Object.values(nodeIdToNodeDictionary).map((nodesWithSameId) => { + if ( + (showAttributeNames && + nodesWithSameId[0].name && + nodesWithSameId[0].name + .toLowerCase() + .includes(input.toLowerCase())) || + (!showAttributeNames && + nodesWithSameId[0].qname && + nodesWithSameId[0].qname.toLowerCase().includes(input.toLowerCase())) + ) { + hits.push(nodesWithSameId[0].id); + } + }); + expandAndSelectNodes(hits); } function handleTreeClick(nodeIds: string[]) { - setTreeSelections(nodeIds); + setTreeSelectedArray(nodeIds); + // If there's several nodes with the same id, expand paths to all + const isMultiple = nodeIds + .map( + (nodeId) => + nodeIdToNodeDictionary[nodeId] && + nodeIdToNodeDictionary[nodeId].length > 1 + ) + .some((b) => b); + if (isMultiple) { + const newExpanded = new Set(treeExpandedArray); + getAllNodeIdsOnPathToLeaf(nodeIds).forEach((nodeIdToAdd) => + newExpanded.add(nodeIdToAdd) + ); + setTreeExpandedArray(Array.from(newExpanded)); + } } function handleTreeToggle(nodeIds: string[]) { - setTreeExpanded(nodeIds); + setTreeExpandedArray(nodeIds); } - // const selectFromTreeById = (nodeId: string) => { - // const nodeIds = []; - // nodeIds.push(nodeId); - // expandAndSelectNodes(nodeIds); - // }; - const performCallbackFromTreeAction = (action: string, nodeIds: string[]) => { if (action === 'handleSelect') { handleTreeClick(nodeIds); @@ -267,10 +209,6 @@ export default function SchemaInfo(props: { } }; - // const performNodeInfoAction = (nodeId: any, isSourceTree: boolean) => { - // selectFromTreeById(nodeId); - // }; - return ( <>
@@ -301,37 +239,43 @@ export default function SchemaInfo(props: {
- {isTreeDataFetched && (<> - { - if (typeof value === 'string') { - searchFromTree(value); - } - }} - onChange={(value) => { - if (!value) { - clearTreeSearch(); - } - }}/> - - handleExpandClick()} - aria-label={t('schema-tree.expand')} - color="primary" - size="large" - > - {treeExpandedArray.length === 0 ? ( - - ) : ( - - )} - - )} + {isTreeDataFetched && ( + <> + + { + if (typeof value === 'string') { + searchFromTree(value); + } + }} + onChange={(value) => { + if (!value) { + clearTreeSearch(); + } + }} + /> + + + handleExpandClick()} + aria-label={t('schema-tree.expand')} + color="primary" + size="large" + > + {treeExpandedArray.length === 0 ? ( + + ) : ( + + )} + + + + )}
<> -
- + > +
+
- {isTreeDataFetched && ( + dataIsLoaded={isTreeDataFetched} + /> - Show node titles + {t('schema-tree.show-titles')} diff --git a/mscr-ui/src/common/components/schema-info/schema-tree/index.tsx b/mscr-ui/src/common/components/schema-info/schema-tree/index.tsx index d7f9e8a86..f8d2ae91d 100644 --- a/mscr-ui/src/common/components/schema-info/schema-tree/index.tsx +++ b/mscr-ui/src/common/components/schema-info/schema-tree/index.tsx @@ -12,7 +12,7 @@ function toTree(nodes: RenderTree, showQname: boolean) { return nodes.map((node) => { return ( (); + const [dropDownList, setDropDownList] = useState([]); useEffect(() => { if (props.treeData && props.treeData.length > 0) { - sourceSelectionInit = props.treeData[0].id; - setDropdownValue(props.treeData[0].id); - // console.log('props.sourceData'); + setDropDownList(props.treeData); + setSelectedNode(props.treeData[0]); + } else { + setSelectedNode(undefined); } - if (props.dataIsLoaded) { - setDataIsLoaded(true); - } - }, [props]); - - const [dataIsLoaded, setDataIsLoaded] = useState(false); - const [sourceDropdownValue, setDropdownValue] = useState(sourceSelectionInit); - const [selectedNode] = props.treeData.filter( - (item) => item.id === sourceDropdownValue - ); + }, [props.treeData]); - let dropdownInit: { id: string; name?: string }[] = [ - { - id: '1', - }, - ]; - if (props.treeData && props.treeData.length > 0) { - dropdownInit = props.treeData; - } + const handleDropDownSelect = (nodeId: string) => { + const newSelectedNode = props.treeData.find((item) => item.id === nodeId); + setSelectedNode(newSelectedNode ?? selectedNode); + }; - interface constantAttribute { + interface ConstantAttribute { name: string; value: string | undefined; } - const nodeProperties: constantAttribute[] = []; - if (props.treeData.length > 0 && props.treeData[0]?.properties) { - for (const [key, value] of Object.entries(props.treeData[0]?.properties)) { + const nodeProperties: ConstantAttribute[] = []; + if (selectedNode && selectedNode.properties) { + for (const [key, value] of Object.entries(selectedNode.properties)) { nodeProperties.push({ name: key, value: typeof value === 'string' ? value.toString() : undefined, @@ -89,32 +78,26 @@ export default function NodeInfo(props: {
- {!dataIsLoaded && -
- {t('node-info.loading')} -
- } - {dataIsLoaded && -
- {t('node-info.select-a-node')} -
- } + {!props.dataIsLoaded &&
{t('node-info.loading')}
} + {props.dataIsLoaded && ( +
{t('node-info.select-a-node')}
+ )}
)} - {props.treeData.length > 1 && ( + {dropDownList.length > 1 && ( setDropdownValue(newValue)} + value={selectedNode?.id ?? ''} + onChange={(newValue) => handleDropDownSelect(newValue)} > - {dropdownInit.map((rt) => ( - + {dropDownList.map((rt) => ( + {rt.name} ))} diff --git a/mscr-ui/src/common/components/schema-info/schema-tree/schema-tree-renderer/index.tsx b/mscr-ui/src/common/components/schema-info/schema-tree/schema-tree-renderer/index.tsx index 918c0cc8b..b7fc8b64b 100644 --- a/mscr-ui/src/common/components/schema-info/schema-tree/schema-tree-renderer/index.tsx +++ b/mscr-ui/src/common/components/schema-info/schema-tree/schema-tree-renderer/index.tsx @@ -1,257 +1,281 @@ -import {cloneDeep} from "lodash"; -import {RenderTree, RenderTreeOld} from "@app/common/interfaces/crosswalk-connection.interface"; +import {RenderTree, RenderTreeOld} from '@app/common/interfaces/crosswalk-connection.interface'; -export default function MockupSchemaLoader(emptyTemplate: boolean): Promise { +// export default function MockupSchemaLoader(emptyTemplate: boolean): Promise { +// +// let allTreeNodes: RenderTreeOld[] = []; +// +// let currentTreeNode: RenderTreeOld = { +// idNumeric: 0, +// id: '0', +// name: '', +// isLinked: false, +// title: '', +// type: '', +// description: '', +// required: '', +// isMappable: '', +// parentName: '', +// jsonPath: '$schema', +// parentId: 0, +// children: [] +// }; +// +// let nodeId = 0; +// +// function increaseNodeNumber() { +// nodeId += 1; +// } +// +// function createTreeObject(object: string, value: string, parent: string, rootId: any, jsonPath: string) { +// currentTreeNode.jsonPath = jsonPath + '.' + object; +// currentTreeNode.idNumeric = nodeId; +// currentTreeNode.id = nodeId.toString(); +// currentTreeNode.parentId = rootId; +// currentTreeNode.name = object; +// currentTreeNode.title = value; +// currentTreeNode.parentName = parent; +// increaseNodeNumber(); +// } +// +// function walkJson(json_object: any, parent: any, rootId: number, jsonPath: string) { +// for (const obj in json_object) { +// if (typeof json_object[obj] === 'string') { +// //console.log(`leaf ${obj} = ${json_object[obj]}`); +// +// // OBJECT IS A LEAF LEVEL OBJECT +// currentTreeNode = { +// isLinked: false, +// idNumeric: 0, +// id: '0', +// name: '', +// title: '', +// type: 'string', +// description: '', +// required: '', +// parentId: 0, +// jsonPath, +// children: [] +// }; +// createTreeObject(obj, json_object[obj], parent, rootId, jsonPath); +// allTreeNodes.push(cloneDeep(currentTreeNode)); +// } else { +// // OBJECT HAS CHILDREN +// currentTreeNode = { +// isLinked: false, +// idNumeric: 0, +// id: '0', +// name: '', +// title: '', +// type: Array.isArray(json_object[obj]) ? 'array' : 'composite', +// description: '', +// required: '', +// parentId: 0, +// jsonPath, +// children: [] +// }; +// currentTreeNode.name = obj; +// currentTreeNode.parentName = parent; +// currentTreeNode.parentId = rootId; +// currentTreeNode.idNumeric = nodeId; +// currentTreeNode.id = nodeId.toString(); +// +// +// currentTreeNode.jsonPath = jsonPath + '.' + obj; +// increaseNodeNumber(); +// allTreeNodes.push(cloneDeep(currentTreeNode)); +// walkJson(json_object[obj], obj, nodeId - 1, currentTreeNode.jsonPath); +// } +// } +// return allTreeNodes; +// } +// +// +// function walkJsonOld(json_object: any, parent: any, rootId: number, jsonPath: string) { +// for (const obj in json_object) { +// if (typeof json_object[obj] === 'string') { +// //console.log(`leaf ${obj} = ${json_object[obj]}`); +// +// // OBJECT IS A LEAF LEVEL OBJECT +// currentTreeNode = { +// isLinked: false, +// idNumeric: 0, +// id: '0', +// name: '', +// title: '', +// type: 'string', +// description: '', +// required: '', +// parentId: 0, +// jsonPath, +// children: [] +// }; +// createTreeObject(obj, json_object[obj], parent, rootId, jsonPath); +// allTreeNodes.push(cloneDeep(currentTreeNode)); +// } else if (typeof json_object[obj] === 'boolean') { +// //console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! FOUND BOOLEAN', obj, json_object[obj], json_object); +// +// // OBJECT IS A LEAF LEVEL OBJECT +// currentTreeNode = { +// isLinked: false, +// idNumeric: 0, +// id: '0', +// name: '', +// title: '', +// type: json_object[obj].toString(), +// description: '', +// required: '', +// parentId: 0, +// jsonPath, +// children: [] +// }; +// createTreeObject(obj, json_object[obj], parent, rootId, jsonPath); +// allTreeNodes.push(cloneDeep(currentTreeNode)); +// } else { +// // OBJECT HAS CHILDREN +// currentTreeNode = { +// isLinked: false, +// idNumeric: 0, +// id: '0', +// name: '', +// title: '', +// type: Array.isArray(json_object[obj]) ? 'array' : 'composite', +// description: '', +// required: '', +// parentId: 0, +// jsonPath, +// children: [] +// }; +// currentTreeNode.name = obj; +// currentTreeNode.parentName = parent; +// currentTreeNode.parentId = rootId; +// currentTreeNode.idNumeric = nodeId; +// currentTreeNode.id = nodeId.toString(); +// +// +// currentTreeNode.jsonPath = jsonPath + '.' + obj; +// increaseNodeNumber(); +// allTreeNodes.push(cloneDeep(currentTreeNode)); +// walkJsonOld(json_object[obj], obj, nodeId - 1, currentTreeNode.jsonPath); +// } +// } +// return allTreeNodes; +// } +// +// function mergeAttributesToParent(inputNodes: RenderTreeOld[] | undefined) { +// if (inputNodes) { +// let outputNodes = inputNodes.map((parent: RenderTreeOld) => { +// if (parent.children) { +// let i = parent.children.length; +// while (i--) { +// // @ts-ignore +// if (parent.children[i] && parent.children[i].children.length > 0) { +// mergeAttributesToParent([parent.children[i]]); +// } +// if (parent.children[i].name === 'type') { +// parent.type = parent.children[i].title; +// //parent.children.splice(i, 1); +// } else if (parent.children[i].name === 'description') { +// parent.description = parent.children[i].title; +// //parent.children.splice(i, 1); +// } else if (parent.children[i].name === 'title') { +// parent.title = parent.children[i].title; +// //parent.children.splice(i, 1); +// } +// } +// } +// return parent; +// } +// ); +// return outputNodes; +// } +// } +// +// // Unused +// function reverseTreeChildren(inputNodes: RenderTreeOld[] | undefined) { +// if (inputNodes) { +// for (let i = 0; i < inputNodes.length; i += 1) { +// // @ts-ignore +// if (inputNodes[i].children.length > 1) { +// // @ts-ignore +// inputNodes[i].children = inputNodes[i].children.reverse() +// reverseTreeChildren(inputNodes[i].children); +// } +// } +// return inputNodes; +// } +// } +// } - let allTreeNodes: RenderTreeOld[] = []; - let currentTreeNode: RenderTreeOld = { - idNumeric: 0, - id: '0', - name: '', - isLinked: false, - title: '', - type: '', - description: '', - required: '', - isMappable: '', - parentName: '', - jsonPath: '$schema', - parentId: 0, - children: [] - }; - - let nodeId = 0; - - function increaseNodeNumber() { - nodeId += 1; - } - - function createTreeObject(object: string, value: string, parent: string, rootId: any, jsonPath: string) { - currentTreeNode.jsonPath = jsonPath + '.' + object; - currentTreeNode.idNumeric = nodeId; - currentTreeNode.id = nodeId.toString(); - currentTreeNode.parentId = rootId; - currentTreeNode.name = object; - currentTreeNode.title = value; - currentTreeNode.parentName = parent; - increaseNodeNumber(); - } - - function walkJson(json_object: any, parent: any, rootId: number, jsonPath: string) { - for (const obj in json_object) { - if (typeof json_object[obj] === 'string') { - //console.log(`leaf ${obj} = ${json_object[obj]}`); - - // OBJECT IS A LEAF LEVEL OBJECT - currentTreeNode = { - isLinked: false, - idNumeric: 0, - id: '0', - name: '', - title: '', - type: 'string', - description: '', - required: '', - parentId: 0, - jsonPath, - children: [] - }; - createTreeObject(obj, json_object[obj], parent, rootId, jsonPath); - allTreeNodes.push(cloneDeep(currentTreeNode)); - } else { - // OBJECT HAS CHILDREN - currentTreeNode = { - isLinked: false, - idNumeric: 0, - id: '0', - name: '', - title: '', - type: Array.isArray(json_object[obj]) ? 'array' : 'composite', - description: '', - required: '', - parentId: 0, - jsonPath, - children: [] - }; - currentTreeNode.name = obj; - currentTreeNode.parentName = parent; - currentTreeNode.parentId = rootId; - currentTreeNode.idNumeric = nodeId; - currentTreeNode.id = nodeId.toString(); - - - currentTreeNode.jsonPath = jsonPath + '.' + obj; - increaseNodeNumber(); - allTreeNodes.push(cloneDeep(currentTreeNode)); - walkJson(json_object[obj], obj, nodeId - 1, currentTreeNode.jsonPath); - } - } - return allTreeNodes; - } - - - function walkJsonOld(json_object: any, parent: any, rootId: number, jsonPath: string) { - for (const obj in json_object) { - if (typeof json_object[obj] === 'string') { - //console.log(`leaf ${obj} = ${json_object[obj]}`); - - // OBJECT IS A LEAF LEVEL OBJECT - currentTreeNode = { - isLinked: false, - idNumeric: 0, - id: '0', - name: '', - title: '', - type: 'string', - description: '', - required: '', - parentId: 0, - jsonPath, - children: [] - }; - createTreeObject(obj, json_object[obj], parent, rootId, jsonPath); - allTreeNodes.push(cloneDeep(currentTreeNode)); - } else if (typeof json_object[obj] === 'boolean') { - //console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! FOUND BOOLEAN', obj, json_object[obj], json_object); - - // OBJECT IS A LEAF LEVEL OBJECT - currentTreeNode = { - isLinked: false, - idNumeric: 0, - id: '0', - name: '', - title: '', - type: json_object[obj].toString(), - description: '', - required: '', - parentId: 0, - jsonPath, - children: [] - }; - createTreeObject(obj, json_object[obj], parent, rootId, jsonPath); - allTreeNodes.push(cloneDeep(currentTreeNode)); - } else { - // OBJECT HAS CHILDREN - currentTreeNode = { - isLinked: false, - idNumeric: 0, - id: '0', - name: '', - title: '', - type: Array.isArray(json_object[obj]) ? 'array' : 'composite', - description: '', - required: '', - parentId: 0, - jsonPath, - children: [] - }; - currentTreeNode.name = obj; - currentTreeNode.parentName = parent; - currentTreeNode.parentId = rootId; - currentTreeNode.idNumeric = nodeId; - currentTreeNode.id = nodeId.toString(); - - - currentTreeNode.jsonPath = jsonPath + '.' + obj; - increaseNodeNumber(); - allTreeNodes.push(cloneDeep(currentTreeNode)); - walkJsonOld(json_object[obj], obj, nodeId - 1, currentTreeNode.jsonPath); - } - } - return allTreeNodes; - } - - function mergeAttributesToParent(inputNodes: RenderTreeOld[] | undefined) { - if (inputNodes) { - let outputNodes = inputNodes.map((parent: RenderTreeOld) => { - if (parent.children) { - let i = parent.children.length; - while (i--) { - // @ts-ignore - if (parent.children[i] && parent.children[i].children.length > 0) { - mergeAttributesToParent([parent.children[i]]); - } - if (parent.children[i].name === 'type') { - parent.type = parent.children[i].title; - //parent.children.splice(i, 1); - } else if (parent.children[i].name === 'description') { - parent.description = parent.children[i].title; - //parent.children.splice(i, 1); - } else if (parent.children[i].name === 'title') { - parent.title = parent.children[i].title; - //parent.children.splice(i, 1); - } - } - } - return parent; - } - ); - return outputNodes; - } - } - - // Unused - function reverseTreeChildren(inputNodes: RenderTreeOld[] | undefined) { - if (inputNodes) { - for (let i = 0; i < inputNodes.length; i += 1) { - // @ts-ignore - if (inputNodes[i].children.length > 1) { - // @ts-ignore - inputNodes[i].children = inputNodes[i].children.reverse() - reverseTreeChildren(inputNodes[i].children); - } - } - return inputNodes; - } - } -} let treeIndex = 0; -function createRenderTree(input: any, elementPath: string, definitions: any) { - let retArray: RenderTree[] = []; - for (let obj in input) { - treeIndex += 1; - let newNode: RenderTree = { - name: definitions[obj].title, - qname: definitions[obj]?.qname ? definitions[obj]?.qname : 'empty', - visualTreeId: treeIndex.toString(), - id: obj.toString(), - properties: definitions[obj], - elementPath: elementPath + '.' + obj.toString(), - parentElementPath: elementPath, - children: [], - uri: definitions[obj]['@id'] - }; +function createRenderTree( + input: any, + elementPath: string, + definitions: any, + idToNodeDictionary: { [key: string]: RenderTree[] }, +) { + const retArray: RenderTree[] = []; + for (const obj in input) { + treeIndex += 1; + const newNode: RenderTree = { + name: definitions[obj].title, + qname: definitions[obj]?.qname ? definitions[obj]?.qname : 'empty', + visualTreeId: treeIndex.toString(), + id: obj.toString(), + properties: definitions[obj], + elementPath: elementPath + '.' + obj.toString(), + parentElementPath: elementPath, + children: [], + uri: definitions[obj]['@id'], + }; + idToNodeDictionary[newNode.id] = idToNodeDictionary[newNode.id] ?? []; + idToNodeDictionary[newNode.id].push(newNode); - //console.log('OBJ', obj, input[obj].keys, Object.keys(input[obj])); - if (Object.keys(input[obj]).length > 0) { - // HAS CHILDREN - newNode.children = createRenderTree(input[obj], newNode.elementPath, definitions); - } else { - // IS LEAF - } - retArray.push(newNode); + //console.log('OBJ', obj, input[obj].keys, Object.keys(input[obj])); + if (Object.keys(input[obj]).length > 0) { + // HAS CHILDREN + newNode.children = createRenderTree( + input[obj], + newNode.elementPath, + definitions, + idToNodeDictionary, + ); + } else { + // IS LEAF } - return retArray; + retArray.push(newNode); + } + return retArray; } export function generateTreeFromJson(jsonInput: any) { - return new Promise((resolve) => { - let renderedTree = createRenderTree(jsonInput.content.tree, 'ROOT', jsonInput.content.definitions); - let retTree: RenderTree[] = []; - let treeRoot = { - name: 'ROOT', - qname: 'ROOT', - visualTreeId: '0', - id: 'ROOT', - properties: undefined, - elementPath: 'ROOT', - parentElementPath: undefined, - children: renderedTree, - } - retTree.push(treeRoot); - // console.log('renderedTree', renderedTree); - resolve(retTree); + const nodeIdToShallowNode: { [key: string]: RenderTree[] } = {}; + const treeRoot: RenderTree = { + name: 'ROOT', + qname: 'ROOT', + visualTreeId: '0', + id: 'ROOT', + properties: undefined, + uri: '', + elementPath: 'ROOT', + parentElementPath: undefined, + children: [], + }; + nodeIdToShallowNode['ROOT'] = [treeRoot]; + + const generatedTree = new Promise((resolve) => { + const renderedTree = createRenderTree( + jsonInput.content.tree, + 'ROOT', + jsonInput.content.definitions, + nodeIdToShallowNode + ); + const retTree: RenderTree[] = []; + treeRoot.children = renderedTree; + retTree.push(treeRoot); + // console.log('renderedTree', renderedTree); + resolve(retTree); }); + return {generatedTree, nodeIdToShallowNode}; }