diff src/Layout.ts @ 103:f12feced00ce

WIP rewriting Layout
author drewp@bigasterisk.com
date Sat, 12 Mar 2022 00:42:00 -0800
parents src/tabulate.ts@26c55d5d5202
children 1aea03d306af
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Layout.ts	Sat Mar 12 00:42:00 2022 -0800
@@ -0,0 +1,201 @@
+// Organize graph data into tables (column orders, etc) for the view layer.
+
+import Immutable from "immutable"; // mostly using this for the builtin equals() testing, since NamedNode(x)!=NamedNode(x)
+import {
+  DataFactory,
+  NamedNode,
+  Quad,
+  Quad_Object,
+  Store,
+  Term,
+  Util,
+} from "n3";
+import { ViewConfig } from "./ViewConfig";
+
+const { namedNode } = DataFactory;
+
+// // import ns from 'n3/src/IRIs';
+// // const { rdf } = ns;
+
+type UriSet = Immutable.Set<NamedNode>;
+export type TypeToSubjs = Immutable.Map<NamedNode, UriSet>;
+
+// https://github.com/rdfjs/N3.js/issues/265
+if ((NamedNode.prototype as any).hashCode === undefined) {
+  (NamedNode.prototype as any).hashCode = () => 0;
+}
+
+interface ColumnHeader {
+  rdfType?: NamedNode; // could be more than one column that introduces an rdf:type for a join
+  pred: NamedNode;
+}
+interface AlignedTable {
+  columnHeaders: ColumnHeader[];
+  rows: (Term | null)[][]; // each row is 1 wider than columnHeaders since the 1st element is the subject for that row
+}
+interface FreeStatements {
+  statements: Quad[];
+}
+export interface LayoutResult {
+  sections: (AlignedTable | FreeStatements)[];
+}
+
+export class Layout {
+  constructor(public viewConfig?: ViewConfig) {}
+  plan(graph: Store): LayoutResult {
+    const ungrouped: Quad[] = [];
+
+    graph.forEach(
+      (q: Quad) => {
+        ungrouped.push(q);
+      },
+      null,
+      null,
+      null,
+      null
+    );
+    return { sections: [{ statements: ungrouped }] };
+  }
+}
+
+// function getType(graph: Store, subj: NamedNode): NamedNode | null {
+//   let subjType: NamedNode | null = null;
+
+//   graph.forObjects(
+//     (o: Quad_Object) => {
+//       subjType = o as NamedNode;
+//     },
+//     subj,
+//     rdf.type,
+//     null
+//   );
+//   return subjType;
+// }
+
+// // When there are multiple types, an arbitrary one is used.
+// export function groupByRdfType(
+//   graph: Store
+// ): {
+//   byType: TypeToSubjs;
+//   typesPresent: NamedNode[];
+//   untypedSubjs: NamedNode[];
+// } {
+//   let byType: TypeToSubjs = Immutable.Map();
+//   let untyped: UriSet = Immutable.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;
+
+//       const subjType = getType(graph, subj);
+
+//       if (subjType !== null) {
+//         // (subj, rdf:type, subjType) in graph
+//         const oldKeys = Array.from(byType.keys());
+//         const oldVal = byType.get(subjType, Immutable.Set<NamedNode>());
+//         const newVal = oldVal.add(subj);
+//         byType = byType.set(subjType, newVal);
+//       } else {
+//         untyped = untyped.add(subj);
+//       }
+//     },
+//     null,
+//     null,
+//     null,
+//     null
+//   );
+
+//   const typesPresent = Array.from(byType.keys());
+//   typesPresent.sort();
+
+//   const untypedSubjs = Array.from(untyped.values());
+//   untypedSubjs.sort();
+//   return {
+//     byType: byType,
+//     typesPresent: typesPresent,
+//     untypedSubjs: untypedSubjs,
+//   };
+// }
+
+// export function predsForSubj(graph: Store, typeUri: NamedNode): NamedNode[] {
+//   const predsSet: Set<NamedNode> = new Set();
+//   graph.forEach(
+//     (q: Quad) => {
+//       predsSet.add(q.predicate as NamedNode);
+//     },
+//     typeUri,
+//     null,
+//     null,
+//     null
+//   );
+//   const preds = Array.from(predsSet.values());
+//   preds.sort();
+//   return preds;
+// }
+
+// interface ISP {
+//   subj: NamedNode;
+//   pred: NamedNode;
+// }
+// const SP = Immutable.Record<ISP>({
+//   subj: new NamedNode(""),
+//   pred: new NamedNode(""),
+// });
+
+// // One table of rows with a common rdf:type.
+// export class MultiSubjsTypeBlockLayout {
+//   subjs: NamedNode[];
+//   preds: NamedNode[];
+//   graphCells: Immutable.Map<ISP, Immutable.Set<Term>>;
+//   constructor(graph: Store, byType: TypeToSubjs, table: TableDesc) {
+//     const subjSet = byType.get(table.primary);
+//     this.subjs = subjSet ? Array.from(subjSet) : [];
+//     this.subjs.sort();
+
+//     let preds = Immutable.Set<NamedNode>();
+
+//     this.graphCells = Immutable.Map<ISP, Immutable.Set<Term>>().withMutations(
+//       (mutGraphCells) => {
+//         this.subjs.forEach((subj: NamedNode) => {
+//           graph.forEach(
+//             (q: Quad) => {
+//               if (!Util.isNamedNode(q.predicate)) {
+//                 throw new Error();
+//               }
+
+//               const pred = q.predicate as NamedNode;
+//               if (pred.equals(rdf.type)) {
+//                 // the whole block is labeled with the type
+//                 return;
+//               }
+//               preds = preds.add(pred);
+//               const cellKey = this.makeCellKey(subj, pred);
+//               mutGraphCells.set(
+//                 cellKey,
+//                 mutGraphCells.get(cellKey, Immutable.Set<Term>()).add(q.object)
+//               );
+//             },
+//             subj,
+//             null,
+//             null,
+//             null
+//           );
+//         });
+//       }
+//     );
+//     this.preds = Array.from(preds);
+//     this.preds.splice(this.preds.indexOf(rdf.type), 1);
+//     // also pull out label, which should be used on 1st column
+//     this.preds.sort();
+//   }
+
+//   makeCellKey(subj: NamedNode, pred: NamedNode): ISP {
+//     return SP({
+//       subj: subj,
+//       pred: pred,
+//     });
+//   }
+// }