view 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 source

// 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,
//     });
//   }
// }