diff src/graph_view.ts @ 84:067d66a45a51

enable more code. factor out setGraphView
author drewp@bigasterisk.com
date Wed, 17 Nov 2021 16:45:10 -0800
parents 0c188ed3bcd8
children ac7ad087d474
line wrap: on
line diff
--- a/src/graph_view.ts	Wed Nov 17 16:42:59 2021 -0800
+++ b/src/graph_view.ts	Wed Nov 17 16:45:10 2021 -0800
@@ -1,327 +1,327 @@
-// import { html, TemplateResult } from 'lit-html';
-// import { DataFactory, Literal, N3Store, NamedNode, Quad, Quad_Object, Term, Util } from 'n3';
+import { html, TemplateResult } from 'lit';
+import { DataFactory, Literal, Store, NamedNode, Quad, Quad_Object, Term, Util } from 'n3';
 
-// import { SuffixLabels } from './suffixLabels';
+import { SuffixLabels } from './suffixLabels';
 
-// const { namedNode } = DataFactory;
+const { namedNode } = DataFactory;
 
 // // import ns from 'n3/src/IRIs';
 // // const { rdf } = ns;
-// const rdf = {
-//   type: namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
-// };
+const rdf = {
+  type: namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
+};
 
-// type TypeToSubjs = Map<NamedNode, Set<NamedNode>>;
-// // When there are multiple types, an arbitrary one is used.
-// function groupByRdfType(
-//   graph: N3Store
-// ): { byType: TypeToSubjs; untyped: Set<NamedNode> } {
-//   const rdfType = rdf.type;
-//   const byType: TypeToSubjs = new Map();
-//   const untyped: Set<NamedNode> = new Set(); // subjs
-//   const internSubjs = new Map<string, NamedNode>();
-//   graph.forEach(
-//     q => {
-//       if (!Util.isNamedNode(q.subject)) {
-//         throw new Error("unsupported " + q.subject.value);
-//       }
-//       const subj = q.subject as NamedNode;
+type TypeToSubjs = Map<NamedNode, Set<NamedNode>>;
+// When there are multiple types, an arbitrary one is used.
+function groupByRdfType(
+  graph: Store
+): { byType: TypeToSubjs; untyped: Set<NamedNode> } {
+  const rdfType = rdf.type;
+  const byType: TypeToSubjs = new Map();
+  const untyped: Set<NamedNode> = new Set(); // subjs
+  const internSubjs = new Map<string, NamedNode>();
+  graph.forEach(
+    q => {
+      if (!Util.isNamedNode(q.subject)) {
+        throw new Error("unsupported " + q.subject.value);
+      }
+      const subj = q.subject as NamedNode;
 
-//       let subjType: NamedNode | null = null;
+      let subjType: NamedNode | null = null;
 
-//       graph.forObjects(
-//         (o: Quad_Object) => {
-//           subjType = o as NamedNode;
-//         },
-//         subj,
-//         rdfType,
-//         null
-//       );
+      graph.forObjects(
+        (o: Quad_Object) => {
+          subjType = o as NamedNode;
+        },
+        subj,
+        rdfType,
+        null
+      );
 
-//       if (subjType !== null) {
-//         // (subj, rdf:type, subjType) in graph
-//         if (!byType.has(subjType)) {
-//           byType.set(subjType, new Set());
-//         }
-//         (byType.get(subjType) as Set<NamedNode>).add(subj);
-//       } else {
-//         // no rdf:type stmt in graph
-//         if (!internSubjs.has(subj.value)) {
-//           internSubjs.set(subj.value, subj);
-//         }
-//         const intSubj: NamedNode = internSubjs.get(
-//           subj.value as string
-//         ) as NamedNode;
-//         untyped.add(intSubj);
-//       }
-//     },
-//     null,
-//     null,
-//     null,
-//     null
-//   );
-//   return { byType: byType, untyped: untyped };
-// }
+      if (subjType !== null) {
+        // (subj, rdf:type, subjType) in graph
+        if (!byType.has(subjType)) {
+          byType.set(subjType, new Set());
+        }
+        (byType.get(subjType) as Set<NamedNode>).add(subj);
+      } else {
+        // no rdf:type stmt in graph
+        if (!internSubjs.has(subj.value)) {
+          internSubjs.set(subj.value, subj);
+        }
+        const intSubj: NamedNode = internSubjs.get(
+          subj.value as string
+        ) as NamedNode;
+        untyped.add(intSubj);
+      }
+    },
+    null,
+    null,
+    null,
+    null
+  );
+  return { byType: byType, untyped: untyped };
+}
 
-// class NodeDisplay {
-//   labels: SuffixLabels;
-//   constructor(labels: SuffixLabels) {
-//     this.labels = labels;
-//   }
-//   getHtml(n: Term | NamedNode): TemplateResult {
-//     if (Util.isLiteral(n)) {
-//       n = n as Literal;
-//       let dtPart: any = "";
-//       if (n.datatype) {
-//         dtPart = html`
-//           ^^<span class="literalType">
-//             ${this.getHtml(n.datatype)}
-//           </span>
-//         `;
-//       }
-//       return html`
-//         <span class="literal">${n.value}${dtPart}</span>
-//       `;
-//     }
+class NodeDisplay {
+  labels: SuffixLabels;
+  constructor(labels: SuffixLabels) {
+    this.labels = labels;
+  }
+  getHtml(n: Term | NamedNode): TemplateResult {
+    if (Util.isLiteral(n)) {
+      n = n as Literal;
+      let dtPart: any = "";
+      if (n.datatype) {
+        dtPart = html`
+          ^^<span class="literalType">
+            ${this.getHtml(n.datatype)}
+          </span>
+        `;
+      }
+      return html`
+        <span class="literal">${n.value}${dtPart}</span>
+      `;
+    }
 
-//     if (Util.isNamedNode(n)) {
-//       n = n as NamedNode;
-//       let shortened = false;
-//       let uriValue: string = n.value;
-//       for (let [long, short] of [
-//         ["http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf:"],
-//         ["http://www.w3.org/2000/01/rdf-schema#", "rdfs:"],
-//         ["http://purl.org/dc/elements/1.1/", "dc:"],
-//         ["http://www.w3.org/2001/XMLSchema#", "xsd:"]
-//       ]) {
-//         if (uriValue.startsWith(long)) {
-//           uriValue = short + uriValue.substr(long.length);
-//           shortened = true;
-//           break;
-//         }
-//       }
-//       if (!shortened) {
-//         let dn: string | undefined = this.labels.getLabelForNode(uriValue);
-//         if (dn === undefined) {
-//           throw new Error(`dn=${dn}`);
-//         }
-//         uriValue = dn;
-//       }
+    if (Util.isNamedNode(n)) {
+      n = n as NamedNode;
+      let shortened = false;
+      let uriValue: string = n.value;
+      for (let [long, short] of [
+        ["http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf:"],
+        ["http://www.w3.org/2000/01/rdf-schema#", "rdfs:"],
+        ["http://purl.org/dc/elements/1.1/", "dc:"],
+        ["http://www.w3.org/2001/XMLSchema#", "xsd:"]
+      ]) {
+        if (uriValue.startsWith(long)) {
+          uriValue = short + uriValue.substr(long.length);
+          shortened = true;
+          break;
+        }
+      }
+      if (!shortened) {
+        let dn: string | undefined = this.labels.getLabelForNode(uriValue);
+        if (dn === undefined) {
+          throw new Error(`dn=${dn}`);
+        }
+        uriValue = dn;
+      }
 
-//       return html`
-//         <a class="graphUri" href="${n.value}">${uriValue}</a>
-//       `;
-//     }
+      return html`
+        <a class="graphUri" href="${n.value}">${uriValue}</a>
+      `;
+    }
 
-//     return html`
-//       [${n.termType} ${n.value}]
-//     `;
-//   }
-// }
+    return html`
+      [${n.termType} ${n.value}]
+    `;
+  }
+}
 
-// export class GraphView {
-//   url: string;
-//   graph: N3Store;
-//   nodeDisplay: NodeDisplay;
-//   constructor(url: string, graph: N3Store) {
-//     this.url = url;
-//     this.graph = graph;
+export class GraphView {
+  url: string;
+  graph: Store;
+  nodeDisplay: NodeDisplay;
+  constructor(url: string, graph: Store) {
+    this.url = url;
+    this.graph = graph;
 
-//     const labels = new SuffixLabels();
-//     this._addLabelsForAllTerms(labels);
-//     this.nodeDisplay = new NodeDisplay(labels);
-//   }
+    const labels = new SuffixLabels();
+    this._addLabelsForAllTerms(labels);
+    this.nodeDisplay = new NodeDisplay(labels);
+  }
 
-//   _addLabelsForAllTerms(labels: SuffixLabels) {
-//     return this.graph.forEach(
-//       (q: Quad) => {
-//         if (q.subject.termType === "NamedNode") {
-//           labels.planDisplayForNode(q.subject);
-//         }
-//         if (q.predicate.termType === "NamedNode") {
-//           labels.planDisplayForNode(q.predicate);
-//         }
-//         if (q.object.termType === "NamedNode") {
-//           labels.planDisplayForNode(q.object);
-//         }
-//         if (q.object.termType === "Literal" && q.object.datatype) {
-//           labels.planDisplayForNode(q.object.datatype);
-//         }
-//       },
-//       null,
-//       null,
-//       null,
-//       null
-//     );
-//   }
+  _addLabelsForAllTerms(labels: SuffixLabels) {
+    return this.graph.forEach(
+      (q: Quad) => {
+        if (q.subject.termType === "NamedNode") {
+          labels.planDisplayForNode(q.subject);
+        }
+        if (q.predicate.termType === "NamedNode") {
+          labels.planDisplayForNode(q.predicate);
+        }
+        if (q.object.termType === "NamedNode") {
+          labels.planDisplayForNode(q.object);
+        }
+        if (q.object.termType === "Literal" && q.object.datatype) {
+          labels.planDisplayForNode(q.object.datatype);
+        }
+      },
+      null,
+      null,
+      null,
+      null
+    );
+  }
 
-//   _subjBlock(subj: NamedNode) {
-//     const predsSet: Set<NamedNode> = new Set();
-//     this.graph.forEach(
-//       (q: Quad) => {
-//         predsSet.add(q.predicate as NamedNode);
-//       },
-//       subj,
-//       null,
-//       null,
-//       null
-//     );
-//     const preds = Array.from(predsSet.values());
-//     preds.sort();
-//     return html`
-//       <div class="subject">
-//         ${this.nodeDisplay.getHtml(subj)}
-//         <!-- todo: special section for uri/type-and-icon/label/comment -->
-//         <div>
-//           ${preds.map(p => {
-//             return this._predBlock(subj, p);
-//           })}
-//         </div>
-//       </div>
-//     `;
-//   }
+  _subjBlock(subj: NamedNode) {
+    const predsSet: Set<NamedNode> = new Set();
+    this.graph.forEach(
+      (q: Quad) => {
+        predsSet.add(q.predicate as NamedNode);
+      },
+      subj,
+      null,
+      null,
+      null
+    );
+    const preds = Array.from(predsSet.values());
+    preds.sort();
+    return html`
+      <div class="subject">
+        ${this.nodeDisplay.getHtml(subj)}
+        <!-- todo: special section for uri/type-and-icon/label/comment -->
+        <div>
+          ${preds.map(p => {
+            return this._predBlock(subj, p);
+          })}
+        </div>
+      </div>
+    `;
+  }
 
-//   _objBlock(obj: Term) {
-//     return html`
-//       <div class="object">
-//         ${this.nodeDisplay.getHtml(obj)}
-//         <!-- indicate what source or graph said this stmt -->
-//       </div>
-//     `;
-//   }
+  _objBlock(obj: Term) {
+    return html`
+      <div class="object">
+        ${this.nodeDisplay.getHtml(obj)}
+        <!-- indicate what source or graph said this stmt -->
+      </div>
+    `;
+  }
 
-//   _predBlock(subj: NamedNode, pred: NamedNode) {
-//     const objsSet = new Set<Term>();
-//     this.graph.forEach(
-//       (q: Quad) => {
-//         objsSet.add(q.object);
-//       },
-//       subj,
-//       pred,
-//       null,
-//       null
-//     );
-//     const objs = Array.from(objsSet.values());
-//     objs.sort();
-//     return html`
-//       <div class="predicate">
-//         ${this.nodeDisplay.getHtml(pred)}
-//         <div>
-//           ${objs.map(this._objBlock.bind(this))}
-//         </div>
-//       </div>
-//     `;
-//   }
+  _predBlock(subj: NamedNode, pred: NamedNode) {
+    const objsSet = new Set<Term>();
+    this.graph.forEach(
+      (q: Quad) => {
+        objsSet.add(q.object);
+      },
+      subj,
+      pred,
+      null,
+      null
+    );
+    const objs = Array.from(objsSet.values());
+    objs.sort();
+    return html`
+      <div class="predicate">
+        ${this.nodeDisplay.getHtml(pred)}
+        <div>
+          ${objs.map(this._objBlock.bind(this))}
+        </div>
+      </div>
+    `;
+  }
 
-//   byTypeBlock(byType: TypeToSubjs, typeUri: NamedNode) {
-//     const subjSet = byType.get(typeUri);
-//     const subjs: Array<NamedNode> = subjSet ? Array.from(subjSet) : [];
-//     subjs.sort();
+  byTypeBlock(byType: TypeToSubjs, typeUri: NamedNode) {
+    const subjSet = byType.get(typeUri);
+    const subjs: Array<NamedNode> = subjSet ? Array.from(subjSet) : [];
+    subjs.sort();
 
-//     const graphCells = new Map<string, Set<Term>>(); // [subj, pred] : objs
-//     const makeCellKey = (subj: NamedNode, pred: NamedNode) =>
-//       subj.value + "|||" + pred.value;
-//     const preds = new Set<NamedNode>();
+    const graphCells = new Map<string, Set<Term>>(); // [subj, pred] : objs
+    const makeCellKey = (subj: NamedNode, pred: NamedNode) =>
+      subj.value + "|||" + pred.value;
+    const preds = new Set<NamedNode>();
 
-//     subjs.forEach((subj: NamedNode) => {
-//       this.graph.forEach(
-//         (q: Quad) => {
-//           if (!Util.isNamedNode(q.predicate)) {
-//             throw new Error();
-//           }
-//           preds.add(q.predicate as NamedNode);
-//           const cellKey = makeCellKey(subj, q.predicate as NamedNode);
-//           if (!graphCells.has(cellKey)) {
-//             graphCells.set(cellKey, new Set<Term>());
-//           }
-//           graphCells.get(cellKey)!.add(q.object);
-//         },
-//         subj,
-//         null,
-//         null,
-//         null
-//       );
-//     });
-//     const predsList = Array.from(preds);
-//     predsList.splice(predsList.indexOf(rdf.type), 1);
-//     // also pull out label, which should be used on 1st column
-//     predsList.sort();
+    subjs.forEach((subj: NamedNode) => {
+      this.graph.forEach(
+        (q: Quad) => {
+          if (!Util.isNamedNode(q.predicate)) {
+            throw new Error();
+          }
+          preds.add(q.predicate as NamedNode);
+          const cellKey = makeCellKey(subj, q.predicate as NamedNode);
+          if (!graphCells.has(cellKey)) {
+            graphCells.set(cellKey, new Set<Term>());
+          }
+          graphCells.get(cellKey)!.add(q.object);
+        },
+        subj,
+        null,
+        null,
+        null
+      );
+    });
+    const predsList = Array.from(preds);
+    predsList.splice(predsList.indexOf(rdf.type), 1);
+    // also pull out label, which should be used on 1st column
+    predsList.sort();
 
-//     const thead = () => {
-//       const predColumnHead = (pred: NamedNode) => {
-//         return html`
-//           <th>${this.nodeDisplay.getHtml(pred)}</th>
-//         `;
-//       };
-//       return html`
-//         <thead>
-//           <tr>
-//             <th></th>
-//             ${predsList.map(predColumnHead)}
-//           </tr>
-//         </thead>
-//       `;
-//     };
+    const thead = () => {
+      const predColumnHead = (pred: NamedNode) => {
+        return html`
+          <th>${this.nodeDisplay.getHtml(pred)}</th>
+        `;
+      };
+      return html`
+        <thead>
+          <tr>
+            <th></th>
+            ${predsList.map(predColumnHead)}
+          </tr>
+        </thead>
+      `;
+    };
 
-//     const instanceRow = (subj: NamedNode) => {
-//       const cell = (pred: NamedNode) => {
-//         const objs = graphCells.get(subj + "|||" + pred);
-//         if (!objs) {
-//           return html`
-//             <td></td>
-//           `;
-//         }
-//         const objsList = Array.from(objs);
-//         objsList.sort();
-//         const draw = (obj: Term) => {
-//           return html`
-//             <div>${this.nodeDisplay.getHtml(obj)}</div>
-//           `;
-//         };
-//         return html`
-//           <td>${objsList.map(draw)}</td>
-//         `;
-//       };
+    const instanceRow = (subj: NamedNode) => {
+      const cell = (pred: NamedNode) => {
+        const objs = graphCells.get(subj + "|||" + pred);
+        if (!objs) {
+          return html`
+            <td></td>
+          `;
+        }
+        const objsList = Array.from(objs);
+        objsList.sort();
+        const draw = (obj: Term) => {
+          return html`
+            <div>${this.nodeDisplay.getHtml(obj)}</div>
+          `;
+        };
+        return html`
+          <td>${objsList.map(draw)}</td>
+        `;
+      };
 
-//       return html`
-//         <tr>
-//           <td>${this.nodeDisplay.getHtml(subj)}</td>
-//           ${predsList.map(cell)}
-//         </tr>
-//       `;
-//     };
+      return html`
+        <tr>
+          <td>${this.nodeDisplay.getHtml(subj)}</td>
+          ${predsList.map(cell)}
+        </tr>
+      `;
+    };
 
-//     return html`
-//       <div>[icon] ${this.nodeDisplay.getHtml(typeUri)} resources</div>
-//       <div class="typeBlockScroll">
-//         <table class="typeBlock">
-//           ${thead()} ${subjs.map(instanceRow)}
-//         </table>
-//       </div>
-//     `;
-//   }
+    return html`
+      <div>[icon] ${this.nodeDisplay.getHtml(typeUri)} resources</div>
+      <div class="typeBlockScroll">
+        <table class="typeBlock">
+          ${thead()} ${subjs.map(instanceRow)}
+        </table>
+      </div>
+    `;
+  }
 
-//   makeTemplate(): TemplateResult {
-//     const { byType, untyped } = groupByRdfType(this.graph);
-//     const typedSubjs = Array.from(byType.keys());
-//     typedSubjs.sort();
+  makeTemplate(): TemplateResult {
+    const { byType, untyped } = groupByRdfType(this.graph);
+    const typedSubjs = Array.from(byType.keys());
+    typedSubjs.sort();
 
-//     const untypedSubjs = Array.from(untyped.values());
-//     untypedSubjs.sort();
+    const untypedSubjs = Array.from(untyped.values());
+    untypedSubjs.sort();
 
-//     return html`
-//       <section>
-//         <h2>Current graph (<a href="${this.url}">${this.url}</a>)</h2>
-//         <div>
-//           <!-- todo: graphs and provenance.
-//             These statements are all in the
-//             <span data-bind="html: $root.createCurie(graphUri())">...</span> graph.-->
-//         </div>
-//         ${typedSubjs.map((t: NamedNode) => this.byTypeBlock(byType, t))}
-//         <div class="spoGrid">
-//           ${untypedSubjs.map(this._subjBlock.bind(this))}
-//         </div>
-//       </section>
-//     `;
-//   }
-// }
+    return html`
+      <section>
+        <h2>Current graph (<a href="${this.url}">${this.url}</a>)</h2>
+        <div>
+          <!-- todo: graphs and provenance.
+            These statements are all in the
+            <span data-bind="html: $root.createCurie(graphUri())">...</span> graph.-->
+        </div>
+        ${typedSubjs.map((t: NamedNode) => this.byTypeBlock(byType, t))}
+        <div class="spoGrid">
+          ${untypedSubjs.map(this._subjBlock.bind(this))}
+        </div>
+      </section>
+    `;
+  }
+}