|
import { Node, NodeType } from "jsonc-parser"; |
|
import { calculateNodeSize } from "src/lib/utils/graph/calculateNodeSize"; |
|
import { Graph, States } from "src/lib/utils/json/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; |
|
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; |
|
|
|
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; |
|
} |
|
} else if (parentType === "array") { |
|
const nodeFromArrayId = addNodeToGraph({ graph, text: String(value) }); |
|
|
|
if (myParentId) { |
|
addEdgeToGraph(graph, myParentId, nodeFromArrayId); |
|
} |
|
} |
|
|
|
if (nextType && parentType !== "array" && (nextType === "object" || nextType === "array")) { |
|
states.parentName = value; |
|
} |
|
} |
|
|
|
function handleHasChildren( |
|
type: NodeType, |
|
states: States, |
|
graph: Graph, |
|
children: Node[], |
|
myParentId?: string, |
|
parentType?: string |
|
) { |
|
let parentId: string | undefined; |
|
|
|
if (type !== "property" && states.parentName !== "") { |
|
|
|
|
|
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); |
|
} |
|
|
|
states.brothersNodeProps.push({ |
|
id: brothersNodeId, |
|
parentId: states.brothersParentId, |
|
objectsFromArrayId: states.objectsFromArray[states.objectsFromArray.length - 1], |
|
}); |
|
} |
|
} |
|
|
|
|
|
parentId = addNodeToGraph({ graph, type, text: states.parentName }); |
|
states.bracketOpen.push({ id: parentId, type }); |
|
states.parentName = ""; |
|
|
|
|
|
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 (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 (type !== "property") { |
|
|
|
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]; |
|
} |
|
} |
|
|
|
|
|
if (parentType === "array") { |
|
if (states.objectsFromArray.length > 0) { |
|
states.objectsFromArray.pop(); |
|
} |
|
} else { |
|
if (states.bracketOpen.length > 0) { |
|
states.bracketOpen.pop(); |
|
} |
|
} |
|
|
|
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); |
|
} |
|
}; |
|
|