Explorar o código

Merge pull request #13614 from jbrundage/av_2

HPCC-23738 Add tweaks to ECL Archive Viewer

Reviewed-By: Gordon Smith <gordon.smith@lexisnexis.com>
Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman %!s(int64=5) %!d(string=hai) anos
pai
achega
ecb8de8f19
Modificáronse 1 ficheiros con 197 adicións e 161 borrados
  1. 197 161
      esp/src/src/ECLArchiveWidget.ts

+ 197 - 161
esp/src/src/ECLArchiveWidget.ts

@@ -2,7 +2,7 @@ import { ECLEditor } from "@hpcc-js/codemirror";
 import { Workunit } from "@hpcc-js/comms";
 import { SplitPanel } from "@hpcc-js/phosphor";
 import { DirectoryTree } from "@hpcc-js/tree";
-import { xml2json, XMLNode } from "@hpcc-js/util";
+import { xml2json } from "@hpcc-js/util";
 import "dijit/form/Button";
 import "dijit/layout/BorderContainer";
 import "dijit/layout/ContentPane";
@@ -24,99 +24,6 @@ class DirectoryTreeEx extends DirectoryTree {
     }
 }
 
-interface TreeNode {
-    label: string;
-    content?: string;
-    selected?: boolean;
-    children?: TreeNode[];
-}
-
-interface FlatNode {
-    path: string;
-    data: TreeNode;
-}
-
-type TreeNodeMap = { [path: string]: TreeNode };
-
-function flattenizeNode(node: XMLNode, pathStr: string, pathToQuery: string, flatMap: TreeNodeMap, flatNodes: FlatNode[]): TreeNode | undefined {
-    if (typeof pathStr === "undefined") {
-        pathStr = "";
-    }
-    const hasName = node.name;
-    const hasMoneyName = node["$"] && node["$"].name;
-    const childrenUndefined = typeof node.children() === "undefined";
-    const ret: TreeNode = {
-        label: hasMoneyName ? node["$"].name : ""
-    };
-
-    if (!hasMoneyName && hasName) {
-        ret.label = node.name;
-    }
-    const pathToNode = (pathStr.length > 0 ? pathStr + "." : "") + ret.label;
-    if (!childrenUndefined && (node.children().length > 0 || !node.content)) {
-        if (node.children().length === 1 && node.children()[0].name === "Text") {
-            ret.content = node.children()[0].content.trim();
-            if (pathToNode === pathToQuery) {
-                ret.selected = true;
-            }
-        } else {
-            ret.children = node.children().map(function (n) {
-                return flattenizeNode(n, pathToNode, pathToQuery, flatMap, flatNodes);
-            }).filter(function (n) {
-                return n;
-            });
-        }
-    } else if (typeof node.content === "string" && node.content.trim()) {
-        ret.content = node.content.trim();
-        ret.selected = pathToNode === pathToQuery;
-    } else {
-        if (node.name && node.name !== "Query") {
-            return {
-                label: node.name,
-                content: JSON.stringify(node, undefined, 4)
-            };
-        }
-        return undefined;
-    }
-    let path = (pathStr.length > 0 ? pathStr + "." : "") + ret.label;
-    if (node["$"] && node["$"].name === "" && node["$"].key === "") {
-        // new root folder
-        path = "";
-    }
-    if (ret.label === "Query" && (!node.children || node.children().length === 0)) {
-        // skipping 'Query' metadata
-    } else {
-        if (typeof flatMap[path] === "undefined") {
-            flatMap[path] = ret;
-            flatNodes.push({
-                data: ret,
-                path
-            });
-        } else {
-            if (flatMap[path].label !== ret.label || ret.content) {
-                flatNodes.push({
-                    data: ret,
-                    path
-                });
-            }
-        }
-
-        let pathSegmentArr = path.split(".").slice(0, -1);
-        while (pathSegmentArr.length > 0) {
-            const ancestorPath = pathSegmentArr.join(".");
-            if (!flatMap[ancestorPath]) {
-                flatMap[ancestorPath] = { label: ancestorPath, children: [] };
-                flatNodes.push({
-                    data: flatMap[ancestorPath],
-                    path: ancestorPath
-                });
-            }
-            pathSegmentArr = pathSegmentArr.slice(0, -1);
-        }
-    }
-    return undefined;
-}
-
 type _Widget = {
     id: string;
     widget: any;
@@ -215,88 +122,217 @@ export class ECLArchiveWidget {
                     .render()
                     ;
 
-                wu.fetchArchive().then(function (archiveResp) {
-                    const json = xml2json(archiveResp);
-                    let pathToQuery = "";
-                    try {
-                        pathToQuery = json.children()[0].children().find(function (n) {
-                            return n.name && n.name.toLowerCase() === "query";
-                        })["$"].attributePath;
-                    } catch (e) { }
-                    const data: { label: string, children: any[] } = {
-                        label: "",
-                        children: []
-                    };
-                    if (json.children() && json.children()[0]) {
-                        const flatMap: TreeNodeMap = {};
-                        const flatNodes: FlatNode[] = [];
+                wu.fetchArchive().then(function (archiveXML) {
+                    renderArchive(archiveXML);
+                });
+            }
 
-                        // 1) Flatten and normalize the nodes
-                        json.children()[0].children().forEach(function (n) {
-                            flattenizeNode(n, "", pathToQuery, flatMap, flatNodes);
-                        });
+        });
 
-                        // 2) Sort the flattened nodes
-                        flatNodes.sort(function (a, b) {
-                            const aIsFolder = !!a.data.children;
-                            const bIsFolder = !!b.data.children;
-                            if ((aIsFolder && bIsFolder) || (!aIsFolder && !bIsFolder)) {
-                                return a.path.toLowerCase() > b.path.toLowerCase() ? 1 : -1;
-                            } else {
-                                return aIsFolder && !bIsFolder ? -1 : 1;
-                            }
-                        });
+        function renderArchive(archiveXmlStr) {
+            const archive = xml2json(archiveXmlStr);
+
+            const data = {
+                label: ".",
+                children: []
+            };
+            let queryPath = "";
+
+            function walk(node, pathArr) {
+                if (node && typeof node === "object") {
+                    // 1 === Move empty string nodes to root
+                    const hasRootIndicator = node["$"]
+                        && node["$"].key === ""
+                        && node["$"].name === ""
+                        ;
+                    // 2 === Move $.flags to './Libraries'
+                    const hasFlagsIndicator = node["$"]
+                        && typeof node["$"].flags !== "undefined"
+                        ;
+                    // 3 === "./AdditionalFiles" should be renamed to "./Resources"
+                    const hasResourceIndicator = node.name === "AdditionalFiles"
+                        ;
+                    // 4 === node._children.length === 1 && node._children[0].name === Text
+                    const hasRedundantTextNode = node._children.length === 1
+                        && node._children[0].name === "Text"
+                        ;
+                    // 5 === Archive folder contents goes to root
+                    const hasArchiveIndicator = node._children
+                        && node._children.length > 0
+                        && node.name === "Archive"
+                        ;
+                    // 6 === Has query path
+                    const hasQueryPath = node.name === "Query";
 
-                        // 3) Push sorted nodes into tree data shape
-                        flatNodes.forEach(function (flatNode, i) {
-                            const pathArr = flatNode.path.split(".").filter(function (n) {
-                                return n !== "";
+                    // 7 === Check for base64 encoded
+                    const hasABase64Indicator = node["$"]
+                        && node["$"]["xsi:type"] === "SOAP-ENC:base64"
+                        ;
+                    let skipPathConcat = false;
+
+                    if (hasRootIndicator || hasArchiveIndicator) { // 1 & 5
+                        pathArr = [];
+                        skipPathConcat = true;
+                    } else if (hasFlagsIndicator) { // 2
+                        pathArr = ["Libraries"];
+                        if (hasRedundantTextNode) { // 4
+                            node.content = node._children[0].content;
+                            node._children = [];
+                        }
+                        skipPathConcat = true;
+                    } else if (hasResourceIndicator) { // 3
+                        pathArr = ["Resources"];
+                        skipPathConcat = true;
+                    }
+                    if (node["$"]) {
+                        let label = getNodeLabel(node);
+                        if (hasQueryPath) { // 6
+                            if (node["$"].attributePath) {
+                                queryPath = node["$"].attributePath;
+                            } else if (node.content && node.content.trim() && label.trim() === "") {
+                                label = "<unnamed query>";
+                                queryPath = label;
+                            }
+                        }
+                        if (node.content && node.content.trim()) {
+                            let _data = data;
+                            pathArr.forEach(function(pathSegmentName, i) {
+                                const found = _data.children.find(function(_n) {
+                                    return _n.label === pathSegmentName;
+                                });
+                                if (found) {
+                                    _data = found;
+                                } else {
+                                    const _temp = {
+                                        label: pathSegmentName,
+                                        path: pathArr.slice(0, i).join("."),
+                                        children: []
+                                    };
+                                    _data.children.push(_temp);
+                                    _data = _temp;
+                                }
+                            })
+                            ;
+                            let content = node.content.trim();
+                            content = hasABase64Indicator ? atob(content) : content;
+                            const path = pathArr.join(".");
+                            _data.children.push({
+                                label,
+                                path,
+                                content,
+                                fullPath: path.length === 0 ? label : path + "." + label,
+                                selected: false
                             });
-                            if (pathArr.length === 0) {
-                                data.children = data.children.concat(flatNode.data.children);
+                        } else if (node._children) {
+                            if (skipPathConcat) {
+                                node._children.forEach(function(_node, i) {
+                                    walk(_node, [...pathArr]);
+                                });
                             } else {
-                                const parentPath = pathArr.slice(0, -1).join(".");
-                                if (!parentPath) {
-                                    data.children.push(flatNode.data);
-                                } else {
-                                    if (!flatNode.data.content || !flatNode.data.content.trim()) {
-                                        flatNode.data.label = pathArr.slice(-1).join("");
+                                if (label.indexOf(".") !== -1) {
+                                    const name = getNodeName(node);
+                                    if (name.indexOf(".") !== -1) {
+                                        name.split(".").forEach(function(seg, i, arr) {
+                                            if (i < arr.length - 1) {
+                                                pathArr.push(seg);
+                                            }
+                                            label = seg;
+                                        });
                                     }
-                                    if (!flatMap[parentPath].children) {
-                                        flatMap[parentPath].children = [];
-                                    }
-                                    flatMap[parentPath].children.push(flatNode.data);
                                 }
+                                node._children.forEach(function(_node, i) {
+                                    walk(_node, [...pathArr].concat([label]));
+                                });
                             }
-                        });
+                        }
                     }
-                    context.directoryTree
-                        .data({})
-                        .render(function () {
-                            context.directoryTree
-                                .data(data)
-                                .iconSize(16)
-                                .rowItemPadding(2)
-                                .textFileIcon("fa fa-file-code-o")
-                                .render()
-                                ;
-                            context.directoryTree.rowClick = function (contentStr) {
-                                context.editor.text(contentStr);
-                            };
-                            if (!context.archiveViewer.isDOMHidden()) {
-                                const rw = context.directoryTree.calcWidth() + 20;
-                                const pw = context.archiveViewer.width();
-                                const ratio = rw / pw;
-                                context.archiveViewer.relativeSizes([ratio, 1 - ratio]).resize().render();
-                            }
-                        });
+                }
+            }
+
+            walk((archive as any)._children[0], []);
 
+            recursiveSort(data);
+
+            let firstTierLabels = data.children.map(function(n) {
+                return n.label;
+            });
+
+            const librariesIdx = firstTierLabels.indexOf("Libraries");
+            if (librariesIdx !== -1) {
+                const lib = data.children[librariesIdx];
+                data.children.splice(librariesIdx, 1);
+                data.children.push(lib);
+                firstTierLabels = data.children.map(function(n) {
+                    return n.label;
+                });
+            }
+            const resourcesIdx = firstTierLabels.indexOf("Resources");
+            if (resourcesIdx !== -1) {
+                const res = data.children[resourcesIdx];
+                data.children.splice(resourcesIdx, 1);
+                data.children.push(res);
+            }
+            context.directoryTree
+                .data({})
+                .render(function () {
+                    context.directoryTree
+                        .data(data)
+                        .iconSize(16)
+                        .rowItemPadding(2)
+                        .textFileIcon("fa fa-file-code-o")
+                        .render()
+                        ;
                     context.directoryTree.rowClick = function (contentStr) {
                         context.editor.text(contentStr);
                     };
+                    if (!context.archiveViewer.isDOMHidden()) {
+                        const rw = context.directoryTree.calcWidth() + 20;
+                        const pw = context.archiveViewer.width();
+                        const ratio = rw / pw;
+                        context.archiveViewer.relativeSizes([ratio, 1 - ratio]).resize().render();
+                    }
                 });
+
+            context.directoryTree.rowClick = function (contentStr) {
+                context.editor.text(contentStr);
+            };
+
+            function recursiveSort(n) {
+                if (n.fullPath === queryPath) {
+                    n.selected = true;
+                    n.iconClass = "fa fa-code";
+                }
+                if (n && n.children) {
+                    n.children.sort(function(a, b) {
+                        const aContent = a.content && a.content.trim();
+                        const bContent = b.content && b.content.trim();
+                        if (aContent && !bContent) {
+                            return -1;
+                        }
+                        if (!aContent && bContent) {
+                            return 1;
+                        }
+                        return a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1;
+                    });
+                    n.children.forEach(recursiveSort);
+                }
+            }
+            function getNodeName(node) {
+                return node["$"].name || node["$"].key;
             }
+            function getNodeLabel(node) {
+                let label = getNodeName(node);
+                label = label ? label : "";
+                if (!label && node["$"].resourcePath) {
+                    label = node["$"].resourcePath.split("/").pop();
+                } else if (!label && node["$"].originalFilename) {
+                    label = node["$"].originalFilename.split("\\").pop();
+                } else if (!label && node["$"].originalFilename) {
+                    label = node["$"].originalFilename.split("\\").pop();
+                }
+                return label;
+            }
+        }
 
-        });
     }
 }