Procházet zdrojové kódy

Merge pull request #251 from victorbrambati/main

fix empty nodes
Aykut Saraç před 2 roky
rodič
revize
57389b3837
2 změnil soubory, kde provedl 290 přidání a 100 odebrání
  1. 6 1
      src/utils/getOutgoers.ts
  2. 284 99
      src/utils/jsonParser.ts

+ 6 - 1
src/utils/getOutgoers.ts

@@ -7,6 +7,11 @@ export const getOutgoers = (
   const outgoerNodes: NodeData[] = [];
   const matchingNodes: string[] = [];
 
+  if (parent.includes(nodeId)) {
+    const initialParentNode = nodes.find(n => n.id === nodeId);
+    if (initialParentNode) outgoerNodes.push(initialParentNode);
+  }
+
   const runner = (nodeId: string) => {
     const outgoerIds = edges.filter(e => e.from === nodeId).map(e => e.to);
     const nodeList = nodes.filter(n => {
@@ -14,7 +19,7 @@ export const getOutgoers = (
         matchingNodes.push(n.id);
       return outgoerIds.includes(n.id) && !parent.includes(n.id);
     });
-
+    
     outgoerNodes.push(...nodeList);
     nodeList.forEach(node => runner(node.id));
   };

+ 284 - 99
src/utils/jsonParser.ts

@@ -1,4 +1,4 @@
-import { parse } from "jsonc-parser";
+import { Node, parseTree } from "jsonc-parser";
 
 const calculateSize = (
   text: string | [string, string][],
@@ -32,113 +32,303 @@ const calculateSize = (
   };
 };
 
-const filterChild = ([_, v]) => {
-  const isNull = v === null;
-  const isArray = Array.isArray(v) && v.length;
-  const isObject = v instanceof Object;
-
-  return !isNull && (isArray || isObject);
-};
-
-const filterValues = ([k, v]) => {
-  if (Array.isArray(v) || v instanceof Object) return false;
-  return true;
-};
-
-function generateChildren(object: Object, isFolded = false, nextId: () => string) {
-  if (!(object instanceof Object)) object = [object];
-
-  return Object.entries(object)
-    .filter(filterChild)
-    .flatMap(([key, v]) => {
-      const { width, height } = calculateSize(key, true, isFolded);
-      const children = extract(v, isFolded, nextId);
+export const parser = (jsonStr: string, isFolded = false) => {
+  try {
+    let json = parseTree(jsonStr);
+    let nodes: NodeData[] = [];
+    let edges: EdgeData[] = [];
 
-      return [
+    const addNodes = (
+      text: any,
+      width: number,
+      height: number,
+      parent: boolean,
+      isEmpty?: boolean
+    ) => {
+      let actualId = String(nodes.length + 1);
+      nodes = [
+        ...nodes,
         {
-          id: nextId(),
-          text: key,
-          children,
-          width,
-          height,
+          id: actualId,
+          text: text,
+          width: width,
+          height: height,
           data: {
-            isParent: true,
-            childrenCount: children.length,
+            isParent: parent,
+            childrenCount: parent ? 1 : 0,
+            isEmpty: isEmpty,
           },
         },
       ];
-    });
-}
+      return actualId;
+    };
 
-function generateNodeData(object: Object) {
-  if (object instanceof Object) {
-    const entries = Object.entries(object).filter(filterValues);
-    return entries;
-  }
+    const addEdges = (from: string, to: string) => {
+      edges = [
+        ...edges,
+        {
+          id: `e${from}-${to}`,
+          from: from,
+          to: to,
+        },
+      ];
+    };
 
-  return String(object);
-}
+    let parentName: string = "";
+    let bracketOpen: { id: string; type: string }[] = [];
+    let objectsFromArray: number[] = [];
+    let objectsFromArrayId = 0;
+    let notHaveParent: string[] = [];
+    let brothersNode: [string, string][] = [];
+    let brothersParentId: string | undefined = "";
+    let brotherKey: string = "";
+    let brothersNodeProps: {
+      id: string;
+      parentId: string | undefined;
+      objectsFromArrayId: number | undefined;
+    }[] = [];
 
-const extract = (
-  os: string[] | object[] | null,
-  isFolded = false,
-  nextId = (
-    id => () =>
-      String(++id)
-  )(0)
-) => {
-  if (!os) return [];
+    const traverse = (
+      objectToTraverse: Node,
+      parentType?: string,
+      myParentId?: string,
+      nextType?: string
+    ) => {
+      let { type, children, value } = objectToTraverse;
 
-  return [os].flat().map(o => {
-    const text = generateNodeData(o);
-    const { width, height } = calculateSize(text, false, isFolded);
+      if (!children) {
+        if (value !== undefined) {
+          if (
+            parentType === "property" &&
+            nextType !== "object" &&
+            nextType !== "array"
+          ) {
+            brothersParentId = myParentId;
+            if (nextType === undefined) {
+              // add key and value to brothers node
+              brothersNode = [...brothersNode, [brotherKey, value]];
+            } else {
+              brotherKey = value;
+            }
+          } else if (parentType === "array") {
+            const { width, height } = calculateSize(String(value), false, isFolded);
+            const nodeFromArrayId = addNodes(String(value), width, height, false);
+            if (myParentId) {
+              addEdges(myParentId, nodeFromArrayId);
+            }
+          }
+          if (nextType && parentType !== "array") {
+            if (nextType === "object" || nextType === "array") {
+              parentName = value;
+            }
+          }
+        }
+      } else if (children) {
+        let parentId: string | undefined;
 
-    return {
-      id: nextId(),
-      text,
-      width,
-      height,
-      children: generateChildren(o, isFolded, nextId),
-      data: {
-        isParent: false,
-        childrenCount: 0,
-        isEmpty: !text.length,
-      },
-    };
-  });
-};
+        if (type !== "property" && parentName !== "") {
+          // add last brothers node and add parent node
+          
+          if (brothersNode.length > 0) {
+            // add or concat brothers node of same parent
+            let findBrothersNode = brothersNodeProps.find(
+              e =>
+                e.parentId === brothersParentId &&
+                e.objectsFromArrayId ===
+                  objectsFromArray[objectsFromArray.length - 1]
+            );
+            if (findBrothersNode) {
+              let ModifyNodes = [...nodes];
+              let findNode = nodes.findIndex(e => e.id === findBrothersNode?.id);
 
-const flatten = (xs: { id: string; children: never[] }[]) =>
-  xs.flatMap(({ children, ...rest }) => [rest, ...flatten(children)]);
-
-const relationships = (xs: { id: string; children: never[] }[]) => {
-  return xs.flatMap(({ id: from, children = [] }) => [
-    ...children.map(({ id: to }) => ({
-      id: `e${from}-${to}`,
-      from,
-      to,
-    })),
-    ...relationships(children),
-  ]);
-};
+              if (ModifyNodes[findNode]) {
+                ModifyNodes[findNode].text =
+                  ModifyNodes[findNode].text.concat(brothersNode);
+                const { width, height } = calculateSize(
+                  ModifyNodes[findNode].text,
+                  false,
+                  isFolded
+                );
+                ModifyNodes[findNode].width = width;
+                ModifyNodes[findNode].height = height;
+                nodes = [...ModifyNodes];
+                brothersNode = [];
+              }
+            } else {
+              const { width, height } = calculateSize(brothersNode, false, isFolded);
+              const brothersNodeId = addNodes(brothersNode, width, height, false);
+              brothersNode = [];
 
-export const parser = (jsonStr: string, isFolded = false) => {
-  try {
-    let json = parse(jsonStr);
-    if (!Array.isArray(json)) json = [json];
-    const nodes: NodeData[] = [];
-    const edges: EdgeData[] = [];
-
-    const mappedElements = extract(json, isFolded);
-    const res = [...flatten(mappedElements), ...relationships(mappedElements)];
-
-    res.forEach(data => {
-      if (isNode(data)) {
-        nodes.push(data);
-      } else {
-        edges.push(data);
+              if (brothersParentId) {
+                addEdges(brothersParentId, brothersNodeId);
+              } else {
+                notHaveParent = [...notHaveParent, brothersNodeId];
+              }
+
+              brothersNodeProps = [
+                ...brothersNodeProps,
+                {
+                  id: brothersNodeId,
+                  parentId: brothersParentId,
+                  objectsFromArrayId: objectsFromArray[objectsFromArray.length - 1],
+                },
+              ];
+            }
+          }
+
+          // add parent node
+          const { width, height } = calculateSize(parentName, true, isFolded);
+          parentId = addNodes(parentName, width, height, true);
+          bracketOpen = [...bracketOpen, { id: parentId, type: type }];
+          parentName = "";
+
+          // add edges from parent node
+          let brothersProps = brothersNodeProps.filter(
+            e =>
+              e.parentId === myParentId &&
+              e.objectsFromArrayId === objectsFromArray[objectsFromArray.length - 1]
+          );
+          if (
+            (brothersProps.length > 0 &&
+              bracketOpen[bracketOpen.length - 2] &&
+              bracketOpen[bracketOpen.length - 2].type !== "object") ||
+            (brothersProps.length > 0 && bracketOpen.length === 1)
+          ) {
+            addEdges(brothersProps[brothersProps.length - 1].id, parentId);
+          } else if (myParentId) {
+            addEdges(myParentId, parentId);
+          } else {
+            notHaveParent = [...notHaveParent, parentId];
+          }
+        } else if (parentType === "array") {
+            objectsFromArray = [...objectsFromArray, objectsFromArrayId++];
+          }
+        children.forEach((branch, index, array) => {
+          if (array[index + 1]) {
+            traverse(
+              branch,
+              type,
+              bracketOpen[bracketOpen.length - 1]
+                ? bracketOpen[bracketOpen.length - 1].id
+                : undefined,
+              array[index + 1].type
+            );
+          } else {
+            traverse(
+              branch,
+              type,
+              bracketOpen[bracketOpen.length - 1]
+                ? bracketOpen[bracketOpen.length - 1].id
+                : undefined
+            );
+          }
+        });
+
+        if (type !== "property") {
+          // when children end
+
+          // add or concat brothers node when it is the last parent node
+          if (brothersNode.length > 0) {
+            let findBrothersNode = brothersNodeProps.find(
+              e =>
+                e.parentId === brothersParentId &&
+                e.objectsFromArrayId ===
+                  objectsFromArray[objectsFromArray.length - 1]
+            );
+            if (findBrothersNode) {
+              let ModifyNodes = [...nodes];
+              let findNode = nodes.findIndex(e => e.id === findBrothersNode?.id);
+
+              if (ModifyNodes[findNode]) {
+                ModifyNodes[findNode].text =
+                  ModifyNodes[findNode].text.concat(brothersNode);
+                const { width, height } = calculateSize(
+                  ModifyNodes[findNode].text,
+                  false,
+                  isFolded
+                );
+                ModifyNodes[findNode].width = width;
+                ModifyNodes[findNode].height = height;
+                nodes = [...ModifyNodes];
+                brothersNode = [];
+              }
+            } else {
+              const { width, height } = calculateSize(brothersNode, false, isFolded);
+              const brothersNodeId = addNodes(brothersNode, width, height, false);
+              brothersNode = [];
+
+              if (brothersParentId) {
+                addEdges(brothersParentId, brothersNodeId);
+              } else {
+                notHaveParent = [...notHaveParent, brothersNodeId];
+              }
+
+              brothersNodeProps = [
+                ...brothersNodeProps,
+                {
+                  id: brothersNodeId,
+                  parentId: brothersParentId,
+                  objectsFromArrayId: objectsFromArray[objectsFromArray.length - 1],
+                },
+              ];
+            }
+          }
+
+          // close brackets
+          if (parentType !== "array") {
+            if (bracketOpen.length > 0) {
+              let newBracketOpen = [...bracketOpen];
+              newBracketOpen.splice(newBracketOpen.length - 1);
+              bracketOpen = [...newBracketOpen];
+            }
+          } else if (parentType === "array") {
+            if (objectsFromArray.length > 0) {
+              let newobjectsFromArray = [...objectsFromArray];
+              newobjectsFromArray.splice(newobjectsFromArray.length - 1);
+              objectsFromArray = [...newobjectsFromArray];
+            }
+          }
+
+          if (parentId) {
+            let myChildrens = edges.filter(e => e.from === parentId);
+            let myIndex = nodes.findIndex(e => e.id === parentId);
+
+            let ModifyNodes = [...nodes];
+            if (ModifyNodes[myIndex]) {
+              ModifyNodes[myIndex].data.childrenCount = myChildrens.length;
+              nodes = [...ModifyNodes];
+            }
+          }
+        }
       }
-    });
+    };
+    
+    if (json) {
+      traverse(json);
+
+      if (notHaveParent.length > 1) {
+        if (json.type !== "array") {
+          const text = "";
+          const { width, height } = calculateSize(text, false, isFolded);
+          const emptyId = addNodes(text, width, height, false, true);
+          notHaveParent.forEach(children => {
+            addEdges(emptyId, children);
+          });
+        }
+      }
+      
+      if (nodes.length === 0) {
+        if (json.type === "array") {
+          const text = "[]";
+          const { width, height } = calculateSize(text, false, isFolded);
+          addNodes(text, width, height, false);
+        } else {
+          const text = "{}";
+          const { width, height } = calculateSize(text, false, isFolded);
+          addNodes(text, width, height, false);
+        }
+      }
+    }
 
     return { nodes, edges };
   } catch (error) {
@@ -149,8 +339,3 @@ export const parser = (jsonStr: string, isFolded = false) => {
     };
   }
 };
-
-function isNode(element: NodeData | EdgeData) {
-  if ("text" in element) return true;
-  return false;
-}