diff --git a/src/containers/Editor/components/views/GraphView/lib/jsonParser.ts b/src/containers/Editor/components/views/GraphView/lib/jsonParser.ts index 68eb6316bd5..13aac83e50f 100644 --- a/src/containers/Editor/components/views/GraphView/lib/jsonParser.ts +++ b/src/containers/Editor/components/views/GraphView/lib/jsonParser.ts @@ -54,7 +54,9 @@ export function parser(jsonStr: string): Graph { throw new Error("Invalid document"); } - traverse({ states, objectToTraverse: parsedJsonTree }); + console.log(parsedJsonTree); + const rootId = addNodeToGraph({ graph: states.graph, text: "Root", type: "object" }); + traverse({ states, objectToTraverse: parsedJsonTree, myParentId: rootId }); const { notHaveParent, graph } = states; diff --git a/src/containers/Editor/components/views/GraphView/lib/utils/getNodePath.ts b/src/containers/Editor/components/views/GraphView/lib/utils/getNodePath.ts index 326b347767a..01d88b2317e 100644 --- a/src/containers/Editor/components/views/GraphView/lib/utils/getNodePath.ts +++ b/src/containers/Editor/components/views/GraphView/lib/utils/getNodePath.ts @@ -38,7 +38,9 @@ export function getNodePath(nodes: NodeData[], edges: EdgeData[], nodeId: string if (!curNode) break; if (curNode.data?.type === "array") { - resolvedPath += `.${curNode.text}`; + if (curNode.text !== "") { + resolvedPath += `.${curNode.text}`; + } if (i !== path.length - 1) { const toNodeId = path[i + 1]; @@ -49,7 +51,9 @@ export function getNodePath(nodes: NodeData[], edges: EdgeData[], nodeId: string } if (curNode.data?.type === "object") { - resolvedPath += `.${curNode.text}`; + if (curNode.text !== "") { + resolvedPath += `.${curNode.text}`; + } } } diff --git a/src/containers/Editor/components/views/GraphView/lib/utils/traverse.ts b/src/containers/Editor/components/views/GraphView/lib/utils/traverse.ts index b66ff4533a7..0eea555e5df 100644 --- a/src/containers/Editor/components/views/GraphView/lib/utils/traverse.ts +++ b/src/containers/Editor/components/views/GraphView/lib/utils/traverse.ts @@ -1,258 +1,126 @@ -import type { Node, NodeType } from "jsonc-parser"; -import type { - Graph, - States, -} from "src/containers/Editor/components/views/GraphView/lib/jsonParser"; -import { calculateNodeSize } from "src/containers/Editor/components/views/GraphView/lib/utils/calculateNodeSize"; +// import type { Node, NodeType } from "jsonc-parser"; +import type { Node } from "jsonc-parser"; +import type { States } from "src/containers/Editor/components/views/GraphView/lib/jsonParser"; import { addEdgeToGraph } from "./addEdgeToGraph"; import { addNodeToGraph } from "./addNodeToGraph"; -type PrimitiveOrNullType = "boolean" | "string" | "number" | "null"; - type Traverse = { states: States; objectToTraverse: Node; parentType?: string; - myParentId?: string; + myParentId: string; nextType?: string; }; -const isPrimitiveOrNullType = (type: unknown): type is PrimitiveOrNullType => { - return ["boolean", "string", "number", "null"].includes(type as string); -}; - -const alignChildren = (nodeA: Node, nodeB: Node): number => { - const aChildType = nodeA?.children?.[1]?.type; - const bChildType = nodeB?.children?.[1]?.type; - - if (isPrimitiveOrNullType(aChildType) && !isPrimitiveOrNullType(bChildType)) { - return -1; - } - - return 0; -}; - -function handleNoChildren( - value: string | undefined, - states: States, - graph: Graph, - myParentId?: string, - parentType?: string, - nextType?: string -) { - if (value === undefined) return; +const traverseArray = (states: States, array: Node, parentId: string) => { + // Unpack input args + const graph = states.graph; - if (parentType === "property" && nextType !== "object" && nextType !== "array") { - states.brothersParentId = myParentId; - if (nextType === undefined && Array.isArray(states.brothersNode)) { - states.brothersNode.push([states.brotherKey, value]); - } else { - states.brotherKey = value; + // Check that the array has children. + if (array.children) { + // Record the number of child elements the array will have. + const parentNode = graph.nodes.at(Number(parentId) - 1); + if (parentNode) { + parentNode.data.childrenCount = array.children.length; } - } else if (parentType === "array") { - const nodeFromArrayId = addNodeToGraph({ graph, text: String(value) }); - if (myParentId) { - addEdgeToGraph(graph, myParentId, nodeFromArrayId); - } - } + // Begin looping over each array element processing accordingly. + for (let i = 0; i < array.children.length; i++) { + const child = array.children[i]; + + // Set up the node text. + // For an array of complex type (object/array), we need + // to construct dummy nodes that handle either nested arrays + // or multiple nodes within an object. + // For simple types, we can just read in the child object's value. + let nodeText = ""; + if (child.value !== undefined) { + nodeText = String(child.value); + } - if (nextType && parentType !== "array" && (nextType === "object" || nextType === "array")) { - states.parentName = value; + const nodeId = addNodeToGraph({ + graph, + text: String(nodeText), + type: child.type, + }); + addEdgeToGraph(graph, parentId, nodeId); + + // Call the appropriate traversal function + // or end if there are no more nested elements. + if (child.type === "array") { + traverseArray(states, array.children[i], nodeId); + } else if (child.type === "object") { + traverse({ objectToTraverse: child, states, myParentId: nodeId }); + } + } } -} - -function handleHasChildren( - type: NodeType, - states: States, - graph: Graph, - children: Node[], - myParentId?: string, - parentType?: string -) { - let parentId: string | undefined; - - if (type !== "property" && states.parentName !== "") { - // add last brothers node and add parent node - - if (states.brothersNode.length > 0) { - const findBrothersNode = states.brothersNodeProps.find( - e => - e.parentId === states.brothersParentId && - e.objectsFromArrayId === states.objectsFromArray[states.objectsFromArray.length - 1] - ); - - if (findBrothersNode) { - const findNodeIndex = graph.nodes.findIndex(e => e.id === findBrothersNode?.id); - - if (findNodeIndex !== -1) { - const modifyNodes = [...graph.nodes]; - const foundNode = modifyNodes[findNodeIndex]; - - foundNode.text = foundNode.text.concat(states.brothersNode as any); - const { width, height } = calculateNodeSize(foundNode.text, false); - - foundNode.width = width; - foundNode.height = height; - - graph.nodes = modifyNodes; - states.brothersNode = []; - } - } else { - const brothersNodeId = addNodeToGraph({ graph, text: states.brothersNode }); - - states.brothersNode = []; +}; - if (states.brothersParentId) { - addEdgeToGraph(graph, states.brothersParentId, brothersNodeId); - } else { - states.notHaveParent.push(brothersNodeId); +export const traverse = ({ objectToTraverse, states, myParentId }: Traverse) => { + // Unpack input arguments + const graph = states.graph; + const { children, type } = objectToTraverse; + + // Set up initial step conditions. + let nodeId = ""; + const nodeText: [string, string][] = []; + const childQueue: Node[] = []; + if (children) { + if (type === "object") { + // Loop over the children of the JSON node + for (let i = 0; i < children.length; i++) { + const child = children[i]; + if (child.children) { + // If the child of our child is not an array or object, it is a property; + // record it in the nodeText. + // Otherwise, push it onto the nodes to be traversed. + if (child.children[1].type !== "object" && child.children[1].type !== "array") { + nodeText.push([child.children[0].value, child.children[1].value]); + } else { + childQueue.push(child); + } } - - states.brothersNodeProps.push({ - id: brothersNodeId, - parentId: states.brothersParentId, - objectsFromArrayId: states.objectsFromArray[states.objectsFromArray.length - 1], - }); - } + } } - - // Add parent node - parentId = addNodeToGraph({ graph, type, text: states.parentName }); - states.bracketOpen.push({ id: parentId, type }); - states.parentName = ""; - - // Add edges from parent node - const brothersProps = states.brothersNodeProps.filter( - e => - e.parentId === myParentId && - e.objectsFromArrayId === states.objectsFromArray[states.objectsFromArray.length - 1] - ); - - if ( - (brothersProps.length > 0 && - states.bracketOpen[states.bracketOpen.length - 2]?.type !== "object") || - (brothersProps.length > 0 && states.bracketOpen.length === 1) - ) { - addEdgeToGraph(graph, brothersProps[brothersProps.length - 1].id, parentId); - } else if (myParentId) { - addEdgeToGraph(graph, myParentId, parentId); - } else { - states.notHaveParent.push(parentId); + else if (type === "array") { + traverseArray(states, objectToTraverse, myParentId); } - } else if (parentType === "array") { - states.objectsFromArray = [...states.objectsFromArray, states.objectsFromArrayId++]; } - const traverseObject = (objectToTraverse: Node, nextType: string) => { - traverse({ - states, - objectToTraverse, - parentType: type, - myParentId: states.bracketOpen[states.bracketOpen.length - 1]?.id, - nextType, - }); - }; - - const traverseArray = () => { - children.forEach((objectToTraverse, index, array) => { - const nextType = array[index + 1]?.type; - traverseObject(objectToTraverse, nextType); - }); - }; - - if (type === "object") { - children.sort(alignChildren); - traverseArray(); - } else { - traverseArray(); + // If we have parent and we have recorded some number of properties, + // add the recorded properties as a node in the graph. + if (myParentId && nodeText.length !== 0) { + nodeId = addNodeToGraph({ graph, text: nodeText }); + addEdgeToGraph(graph, myParentId, nodeId); } - if (type !== "property") { - // Add or concatenate brothers node when it is the last parent node - if (states.brothersNode.length > 0) { - const findBrothersNode = states.brothersNodeProps.find( - e => - e.parentId === states.brothersParentId && - e.objectsFromArrayId === states.objectsFromArray[states.objectsFromArray.length - 1] - ); - - if (findBrothersNode) { - const modifyNodes = [...graph.nodes]; - const findNodeIndex = modifyNodes.findIndex(e => e.id === findBrothersNode?.id); - - if (modifyNodes[findNodeIndex] && typeof states.brothersNode === "string") { - modifyNodes[findNodeIndex].text += states.brothersNode; - - const { width, height } = calculateNodeSize(modifyNodes[findNodeIndex].text, false); - - modifyNodes[findNodeIndex].width = width; - modifyNodes[findNodeIndex].height = height; - - graph.nodes = modifyNodes; - states.brothersNode = []; - } - } else { - const brothersNodeId = addNodeToGraph({ graph, text: states.brothersNode }); - - states.brothersNode = []; - - if (states.brothersParentId) { - addEdgeToGraph(graph, states.brothersParentId, brothersNodeId); - } else { - states.notHaveParent = [...states.notHaveParent, brothersNodeId]; - } - - const brothersNodeProps = { - id: brothersNodeId, - parentId: states.brothersParentId, - objectsFromArrayId: states.objectsFromArray[states.objectsFromArray.length - 1], - }; - - states.brothersNodeProps = [...states.brothersNodeProps, brothersNodeProps]; + // Record the number of child objects the node will have. + if (myParentId) { + const node = graph.nodes.at(Number(myParentId) - 1); + if (node) { + node.data.childrenCount = childQueue.length; + if (nodeText.length !== 0) { + node.data.childrenCount += 1; } } + } - // Close brackets - if (parentType === "array") { - if (states.objectsFromArray.length > 0) { - states.objectsFromArray.pop(); + // Iterate over the child queue, processing each child accordingly. + childQueue.forEach(child => { + if (child.children) { + const brotherNodeId = addNodeToGraph({ + graph, + text: child.children[0].value, + type: child.children[1].type, + }); + if (myParentId) { + addEdgeToGraph(graph, myParentId, brotherNodeId); } - } else { - if (states.bracketOpen.length > 0) { - states.bracketOpen.pop(); + if (child.children[1].type === "object") { + traverse({ objectToTraverse: child.children[1], states, myParentId: brotherNodeId }); + } else if (child.children[1].type === "array") { + traverseArray(states, child.children[1], brotherNodeId); } } - - if (parentId) { - const myChildren = graph.edges.filter(edge => edge.from === parentId); - const parentIndex = graph.nodes.findIndex(node => node.id === parentId); - - graph.nodes = graph.nodes.map((node, index) => { - if (index === parentIndex) { - const childrenCount = myChildren.length; - - return { ...node, data: { ...node.data, childrenCount } }; - } - return node; - }); - } - } -} - -export const traverse = ({ - objectToTraverse, - states, - myParentId, - nextType, - parentType, -}: Traverse) => { - const graph = states.graph; - const { type, children, value } = objectToTraverse; - - if (!children) { - handleNoChildren(value, states, graph, myParentId, parentType, nextType); - } else if (children) { - handleHasChildren(type, states, graph, children, myParentId, parentType); - } + }); };