@graphty/graph-io 0.3.39 → 0.3.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{choose-CS3fvGT1.js → choose-CwFQamH9.js} +2 -2
- package/dist/chunks/{choose-CS3fvGT1.js.map → choose-CwFQamH9.js.map} +1 -1
- package/dist/chunks/{constants-DqKZT6cH.js → constants-Cf4Nn6tw.js} +2 -2
- package/dist/chunks/{constants-DqKZT6cH.js.map → constants-Cf4Nn6tw.js.map} +1 -1
- package/dist/chunks/{escape-BpYMQZVa.js → escape-DBb8zuNE.js} +4 -3
- package/dist/chunks/escape-DBb8zuNE.js.map +1 -0
- package/dist/chunks/{export-B-aVCwyG.js → export-DD3Lvw-F.js} +173 -116
- package/dist/chunks/export-DD3Lvw-F.js.map +1 -0
- package/dist/chunks/{exporter-CNJZF_BN.js → exporter-BqtmlxNr.js} +545 -459
- package/dist/chunks/exporter-BqtmlxNr.js.map +1 -0
- package/dist/chunks/{exporter-BqKs1Q12.js → exporter-CS35R6Fg.js} +6 -5
- package/dist/chunks/exporter-CS35R6Fg.js.map +1 -0
- package/dist/chunks/{exporter-DXqCPNrI.js → exporter-D8r1N2dU.js} +67 -42
- package/dist/chunks/exporter-D8r1N2dU.js.map +1 -0
- package/dist/chunks/{exporter-SFYkDzD0.js → exporter-NMplSnYn.js} +23 -20
- package/dist/chunks/{exporter-SFYkDzD0.js.map → exporter-NMplSnYn.js.map} +1 -1
- package/dist/chunks/{importer-CSt-gaYE.js → importer-BKC7AmCA.js} +42 -29
- package/dist/chunks/importer-BKC7AmCA.js.map +1 -0
- package/dist/chunks/{importer-DvFznt5-.js → importer-BdWHmlmG.js} +112 -62
- package/dist/chunks/importer-BdWHmlmG.js.map +1 -0
- package/dist/chunks/{importer-CqgYq4Ld.js → importer-BfbzWk6k.js} +202 -181
- package/dist/chunks/importer-BfbzWk6k.js.map +1 -0
- package/dist/chunks/{importer-PVXkeoaS.js → importer-BmaZcskq.js} +79 -66
- package/dist/chunks/importer-BmaZcskq.js.map +1 -0
- package/dist/chunks/{importer-B7Ir269L.js → importer-DviEsHy4.js} +118 -72
- package/dist/chunks/importer-DviEsHy4.js.map +1 -0
- package/dist/chunks/{importer--tQQsY4W.js → importer-S-abxJHn.js} +5 -5
- package/dist/chunks/{importer--tQQsY4W.js.map → importer-S-abxJHn.js.map} +1 -1
- package/dist/chunks/{importer-BfJ1jnNr.js → importer-r29cWnEf.js} +28 -13
- package/dist/chunks/importer-r29cWnEf.js.map +1 -0
- package/dist/chunks/{json-elements-CXs4z1iL.js → json-elements-DdF-etPT.js} +7 -5
- package/dist/chunks/{json-elements-CXs4z1iL.js.map → json-elements-DdF-etPT.js.map} +1 -1
- package/dist/chunks/{ontology-export-50L9DOV4.js → ontology-export-BC0zyPhj.js} +2 -2
- package/dist/chunks/{ontology-export-50L9DOV4.js.map → ontology-export-BC0zyPhj.js.map} +1 -1
- package/dist/chunks/{records-C9jd7yLD.js → records-Uc3LkABx.js} +150 -106
- package/dist/chunks/records-Uc3LkABx.js.map +1 -0
- package/dist/chunks/{text-Bjfa1xYL.js → text-Bh9SaAob.js} +20 -11
- package/dist/chunks/text-Bh9SaAob.js.map +1 -0
- package/dist/csv.js +67 -49
- package/dist/csv.js.map +1 -1
- package/dist/cx.js +1 -1
- package/dist/cx2.js +2 -2
- package/dist/cys.js +1 -1
- package/dist/dot.js +1 -1
- package/dist/gexf.js +199 -99
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +170 -149
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +26 -26
- package/dist/graphml.js +2 -2
- package/dist/json.js +1 -1
- package/dist/neo4j.js +103 -65
- package/dist/neo4j.js.map +1 -1
- package/dist/obo.js +1 -1
- package/dist/pajek.js +1 -1
- package/dist/src/common/attributes.d.ts.map +1 -1
- package/dist/src/common/attributes.js +34 -9
- package/dist/src/common/attributes.js.map +1 -1
- package/dist/src/common/input.d.ts.map +1 -1
- package/dist/src/common/input.js +180 -102
- package/dist/src/common/input.js.map +1 -1
- package/dist/src/common/json-elements.d.ts.map +1 -1
- package/dist/src/common/json-elements.js +4 -2
- package/dist/src/common/json-elements.js.map +1 -1
- package/dist/src/common/text.d.ts +7 -0
- package/dist/src/common/text.d.ts.map +1 -1
- package/dist/src/common/text.js +15 -7
- package/dist/src/common/text.js.map +1 -1
- package/dist/src/common/xml.d.ts.map +1 -1
- package/dist/src/common/xml.js +2 -1
- package/dist/src/common/xml.js.map +1 -1
- package/dist/src/common/zip.d.ts.map +1 -1
- package/dist/src/common/zip.js +36 -19
- package/dist/src/common/zip.js.map +1 -1
- package/dist/src/formats/csv/exporter.d.ts.map +1 -1
- package/dist/src/formats/csv/exporter.js +77 -25
- package/dist/src/formats/csv/exporter.js.map +1 -1
- package/dist/src/formats/csv/importer.d.ts.map +1 -1
- package/dist/src/formats/csv/importer.js +16 -6
- package/dist/src/formats/csv/importer.js.map +1 -1
- package/dist/src/formats/csv/records.d.ts.map +1 -1
- package/dist/src/formats/csv/records.js +156 -110
- package/dist/src/formats/csv/records.js.map +1 -1
- package/dist/src/formats/cx/importer.d.ts.map +1 -1
- package/dist/src/formats/cx/importer.js +108 -53
- package/dist/src/formats/cx/importer.js.map +1 -1
- package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
- package/dist/src/formats/cx2/exporter.js +2 -1
- package/dist/src/formats/cx2/exporter.js.map +1 -1
- package/dist/src/formats/cx2/importer.d.ts.map +1 -1
- package/dist/src/formats/cx2/importer.js +28 -6
- package/dist/src/formats/cx2/importer.js.map +1 -1
- package/dist/src/formats/cys/tables.d.ts.map +1 -1
- package/dist/src/formats/cys/tables.js +11 -8
- package/dist/src/formats/cys/tables.js.map +1 -1
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
- package/dist/src/formats/gexf/exporter.js +69 -39
- package/dist/src/formats/gexf/exporter.js.map +1 -1
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +138 -50
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gml/exporter.d.ts.map +1 -1
- package/dist/src/formats/gml/exporter.js +51 -35
- package/dist/src/formats/gml/exporter.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +103 -58
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/syntax.d.ts.map +1 -1
- package/dist/src/formats/gml/syntax.js +17 -8
- package/dist/src/formats/gml/syntax.js.map +1 -1
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
- package/dist/src/formats/graphml/exporter.js +36 -25
- package/dist/src/formats/graphml/exporter.js.map +1 -1
- package/dist/src/formats/graphml/importer.d.ts.map +1 -1
- package/dist/src/formats/graphml/importer.js +20 -15
- package/dist/src/formats/graphml/importer.js.map +1 -1
- package/dist/src/formats/graphml/tree.js +23 -14
- package/dist/src/formats/graphml/tree.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +22 -10
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/exporter.d.ts.map +1 -1
- package/dist/src/formats/json/exporter.js +38 -21
- package/dist/src/formats/json/exporter.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +536 -343
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/obographs.js +29 -17
- package/dist/src/formats/json/obographs.js.map +1 -1
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
- package/dist/src/formats/neo4j/exporter.js +73 -43
- package/dist/src/formats/neo4j/exporter.js.map +1 -1
- package/dist/src/formats/neo4j/header.d.ts.map +1 -1
- package/dist/src/formats/neo4j/header.js +37 -16
- package/dist/src/formats/neo4j/header.js.map +1 -1
- package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
- package/dist/src/formats/neo4j/importer.js +21 -11
- package/dist/src/formats/neo4j/importer.js.map +1 -1
- package/dist/src/formats/obo/exporter.d.ts.map +1 -1
- package/dist/src/formats/obo/exporter.js +18 -7
- package/dist/src/formats/obo/exporter.js.map +1 -1
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +142 -73
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +26 -15
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +85 -58
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts +13 -0
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +112 -50
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
- package/dist/src/formats/xgmml/exporter.js +92 -34
- package/dist/src/formats/xgmml/exporter.js.map +1 -1
- package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
- package/dist/src/formats/xgmml/importer.js +38 -18
- package/dist/src/formats/xgmml/importer.js.map +1 -1
- package/dist/src/sniff.js +109 -57
- package/dist/src/sniff.js.map +1 -1
- package/dist/xgmml.js +2 -2
- package/package.json +2 -2
- package/src/common/attributes.ts +59 -19
- package/src/common/input.ts +216 -102
- package/src/common/json-elements.ts +4 -2
- package/src/common/text.ts +24 -15
- package/src/common/xml.ts +2 -1
- package/src/common/zip.ts +47 -18
- package/src/formats/csv/exporter.ts +103 -39
- package/src/formats/csv/importer.ts +17 -6
- package/src/formats/csv/records.ts +162 -109
- package/src/formats/cx/importer.ts +140 -58
- package/src/formats/cx2/exporter.ts +2 -1
- package/src/formats/cx2/importer.ts +34 -10
- package/src/formats/cys/tables.ts +11 -10
- package/src/formats/gexf/exporter.ts +71 -38
- package/src/formats/gexf/importer.ts +173 -63
- package/src/formats/gml/exporter.ts +78 -82
- package/src/formats/gml/importer.ts +119 -68
- package/src/formats/gml/syntax.ts +18 -8
- package/src/formats/graphml/exporter.ts +46 -27
- package/src/formats/graphml/importer.ts +21 -13
- package/src/formats/graphml/tree.ts +22 -12
- package/src/formats/graphml/yfiles.ts +23 -10
- package/src/formats/json/exporter.ts +40 -21
- package/src/formats/json/importer.ts +701 -385
- package/src/formats/json/obographs.ts +35 -15
- package/src/formats/neo4j/exporter.ts +79 -41
- package/src/formats/neo4j/header.ts +50 -16
- package/src/formats/neo4j/importer.ts +21 -10
- package/src/formats/obo/exporter.ts +34 -17
- package/src/formats/pajek/exporter.ts +180 -117
- package/src/formats/pajek/importer.ts +27 -15
- package/src/formats/pajek/syntax.ts +99 -58
- package/src/formats/xgmml/emit.ts +122 -50
- package/src/formats/xgmml/exporter.ts +103 -35
- package/src/formats/xgmml/importer.ts +45 -23
- package/src/sniff.ts +115 -59
- package/dist/chunks/escape-BpYMQZVa.js.map +0 -1
- package/dist/chunks/export-B-aVCwyG.js.map +0 -1
- package/dist/chunks/exporter-BqKs1Q12.js.map +0 -1
- package/dist/chunks/exporter-CNJZF_BN.js.map +0 -1
- package/dist/chunks/exporter-DXqCPNrI.js.map +0 -1
- package/dist/chunks/importer-B7Ir269L.js.map +0 -1
- package/dist/chunks/importer-BfJ1jnNr.js.map +0 -1
- package/dist/chunks/importer-CSt-gaYE.js.map +0 -1
- package/dist/chunks/importer-CqgYq4Ld.js.map +0 -1
- package/dist/chunks/importer-DvFznt5-.js.map +0 -1
- package/dist/chunks/importer-PVXkeoaS.js.map +0 -1
- package/dist/chunks/records-C9jd7yLD.js.map +0 -1
- package/dist/chunks/text-Bjfa1xYL.js.map +0 -1
|
@@ -773,12 +773,13 @@ function checkNumberVerification(value: unknown, count: number, report: ImportRe
|
|
|
773
773
|
const raw = isRecord(value) ? value.longNumber : undefined;
|
|
774
774
|
const digits = raw instanceof ExactInteger ? raw.digits : String(raw);
|
|
775
775
|
if (count > 1 || !NUMBER_VERIFICATION_VALUES.has(digits)) {
|
|
776
|
+
const held = raw === undefined ? null : digits;
|
|
776
777
|
report.warning(
|
|
777
778
|
"validation-error",
|
|
778
779
|
CX_ISSUE.NUMBER_VERIFICATION,
|
|
779
780
|
count > 1
|
|
780
781
|
? "a second numberVerification element; ignored"
|
|
781
|
-
: `numberVerification holds ${shown(
|
|
782
|
+
: `numberVerification holds ${shown(held)}, not 281474976710655`,
|
|
782
783
|
{ line, element: "numberVerification" },
|
|
783
784
|
);
|
|
784
785
|
}
|
|
@@ -993,10 +994,12 @@ async function readDocument(
|
|
|
993
994
|
break;
|
|
994
995
|
}
|
|
995
996
|
countMember(event.block, null);
|
|
997
|
+
const what =
|
|
998
|
+
keys.length > 1 ? `an object with ${keys.length} keys (${keys.join(", ")})` : shown(value);
|
|
996
999
|
report.error(
|
|
997
1000
|
"parse-error",
|
|
998
1001
|
BAD_ASPECT_BLOCK_CODE,
|
|
999
|
-
`a member of the document is ${
|
|
1002
|
+
`a member of the document is ${what}, not a one-key aspect fragment; skipped`,
|
|
1000
1003
|
{ line: event.line, element: keys[0] ?? null },
|
|
1001
1004
|
);
|
|
1002
1005
|
break;
|
|
@@ -1361,6 +1364,26 @@ function memberCount(members: ReadonlySet<NodeId> | null, total: number): number
|
|
|
1361
1364
|
|
|
1362
1365
|
// ============================================================ the build
|
|
1363
1366
|
|
|
1367
|
+
/** The cyVisualProperties of a graph's view, gathered before they are written. */
|
|
1368
|
+
interface VisualCollection {
|
|
1369
|
+
/** per domain, per property: the [row, text] cells */
|
|
1370
|
+
columns: Record<"node" | "edge", Map<string, [number, string][]>>;
|
|
1371
|
+
/** the network values by property */
|
|
1372
|
+
network: Map<string, string>;
|
|
1373
|
+
defaults: number;
|
|
1374
|
+
mappings: number;
|
|
1375
|
+
dependencies: number;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* A visual property value as text: a string as it is, anything else as JSON.
|
|
1380
|
+
* @param raw - the value
|
|
1381
|
+
* @returns its text
|
|
1382
|
+
*/
|
|
1383
|
+
function visualText(raw: unknown): string {
|
|
1384
|
+
return typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1364
1387
|
/** One attribute column of the graph being built. */
|
|
1365
1388
|
interface AttributeColumn {
|
|
1366
1389
|
readonly name: string;
|
|
@@ -2849,57 +2872,99 @@ class CxReader {
|
|
|
2849
2872
|
* applied.
|
|
2850
2873
|
*/
|
|
2851
2874
|
private readVisualProperties(): void {
|
|
2852
|
-
const entries = aspect(this.doc, "cyVisualProperties");
|
|
2853
2875
|
// Cytoscape's table-cell styles: a visual property aspect graph-io keeps but does not read
|
|
2854
2876
|
const tableStyles = TABLE_STYLE_ASPECTS.reduce((n, name) => n + (this.doc.kept.get(name)?.length ?? 0), 0);
|
|
2855
2877
|
// the graph's own view (its first): the one its positions come from; the others stay in meta.extra.cx
|
|
2856
2878
|
const views = new Set(this.plan.views.slice(0, 1));
|
|
2857
|
-
const
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
if (view !== null && views.size > 0 && !views.has(view)) {
|
|
2868
|
-
continue;
|
|
2879
|
+
const visual: VisualCollection = {
|
|
2880
|
+
columns: { node: new Map(), edge: new Map() },
|
|
2881
|
+
network: new Map(),
|
|
2882
|
+
defaults: 0,
|
|
2883
|
+
mappings: 0,
|
|
2884
|
+
dependencies: 0,
|
|
2885
|
+
};
|
|
2886
|
+
for (const { value } of aspect(this.doc, "cyVisualProperties")) {
|
|
2887
|
+
if (isRecord(value)) {
|
|
2888
|
+
this.collectVisualEntry(value, views, visual);
|
|
2869
2889
|
}
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2890
|
+
}
|
|
2891
|
+
this.writeBypassColumns(visual.columns);
|
|
2892
|
+
this.writeBypassNetwork(visual.network);
|
|
2893
|
+
this.reportStyleRules(visual, tableStyles);
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
/**
|
|
2897
|
+
* File one cyVisualProperties element of this graph's view into the collection: per-element and
|
|
2898
|
+
* network values by property, defaults, mappings and dependencies counted.
|
|
2899
|
+
* @param value - the element
|
|
2900
|
+
* @param views - the views read (empty: every view)
|
|
2901
|
+
* @param visual - the collection to add to
|
|
2902
|
+
*/
|
|
2903
|
+
private collectVisualEntry(
|
|
2904
|
+
value: Record<string, unknown>,
|
|
2905
|
+
views: ReadonlySet<NodeId>,
|
|
2906
|
+
visual: VisualCollection,
|
|
2907
|
+
): void {
|
|
2908
|
+
const view = value.view === undefined || value.view === null ? null : refId(value.view);
|
|
2909
|
+
if (view !== null && views.size > 0 && !views.has(view)) {
|
|
2910
|
+
return;
|
|
2911
|
+
}
|
|
2912
|
+
const properties = isRecord(value.properties) ? value.properties : {};
|
|
2913
|
+
visual.mappings += isRecord(value.mappings) ? Object.keys(value.mappings).length : 0;
|
|
2914
|
+
visual.dependencies += isRecord(value.dependencies) ? Object.keys(value.dependencies).length : 0;
|
|
2915
|
+
switch (value.properties_of) {
|
|
2916
|
+
case "nodes":
|
|
2917
|
+
this.collectElementProperties("node", value.applies_to, properties, visual.columns);
|
|
2918
|
+
break;
|
|
2919
|
+
case "edges":
|
|
2920
|
+
this.collectElementProperties("edge", value.applies_to, properties, visual.columns);
|
|
2921
|
+
break;
|
|
2922
|
+
case "network":
|
|
2923
|
+
for (const [property, raw] of Object.entries(properties)) {
|
|
2924
|
+
visual.network.set(property, visualText(raw));
|
|
2892
2925
|
}
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2926
|
+
break;
|
|
2927
|
+
default:
|
|
2928
|
+
visual.defaults += Object.keys(properties).length;
|
|
2929
|
+
break;
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
/**
|
|
2934
|
+
* File the visual values of one node or edge under its row; an element not in this graph is
|
|
2935
|
+
* skipped, and reported dangling when it is in no graph of the file.
|
|
2936
|
+
* @param domain - node or edge
|
|
2937
|
+
* @param appliesTo - the element's applies_to
|
|
2938
|
+
* @param properties - its visual properties
|
|
2939
|
+
* @param columns - the per-property cells to add to
|
|
2940
|
+
*/
|
|
2941
|
+
private collectElementProperties(
|
|
2942
|
+
domain: "node" | "edge",
|
|
2943
|
+
appliesTo: unknown,
|
|
2944
|
+
properties: Record<string, unknown>,
|
|
2945
|
+
columns: VisualCollection["columns"],
|
|
2946
|
+
): void {
|
|
2947
|
+
const target = refId(appliesTo);
|
|
2948
|
+
const rows = domain === "node" ? this.nodeRows : this.edgeRows;
|
|
2949
|
+
const row = target === null ? undefined : rows.get(target);
|
|
2950
|
+
if (row === undefined) {
|
|
2951
|
+
if (target === null || !(domain === "node" ? this.rootNodes : this.rootEdges).has(target)) {
|
|
2952
|
+
this.dangle(`${domain} visual property entry`);
|
|
2901
2953
|
}
|
|
2954
|
+
return;
|
|
2902
2955
|
}
|
|
2956
|
+
for (const [property, raw] of Object.entries(properties)) {
|
|
2957
|
+
const list = columns[domain].get(property) ?? [];
|
|
2958
|
+
list.push([row, visualText(raw)]);
|
|
2959
|
+
columns[domain].set(property, list);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
/**
|
|
2964
|
+
* Write the collected per-element visual values as string columns in the cx.bypass namespace.
|
|
2965
|
+
* @param columns - the per-property cells of each domain
|
|
2966
|
+
*/
|
|
2967
|
+
private writeBypassColumns(columns: VisualCollection["columns"]): void {
|
|
2903
2968
|
for (const domain of ["node", "edge"] as const) {
|
|
2904
2969
|
for (const [property, cells] of columns[domain]) {
|
|
2905
2970
|
const handle = declareFresh(
|
|
@@ -2922,6 +2987,13 @@ class CxReader {
|
|
|
2922
2987
|
}
|
|
2923
2988
|
}
|
|
2924
2989
|
}
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
/**
|
|
2993
|
+
* Write the network visual values as graph values in the cx.bypass namespace.
|
|
2994
|
+
* @param network - the value of each property
|
|
2995
|
+
*/
|
|
2996
|
+
private writeBypassNetwork(network: ReadonlyMap<string, string>): void {
|
|
2925
2997
|
for (const [property, text] of network) {
|
|
2926
2998
|
try {
|
|
2927
2999
|
this.sink.setGraphValue(property, text, {
|
|
@@ -2932,20 +3004,30 @@ class CxReader {
|
|
|
2932
3004
|
this.report.recordError(err, { element: property });
|
|
2933
3005
|
}
|
|
2934
3006
|
}
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
{ element: "cyVisualProperties" },
|
|
2947
|
-
);
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
/**
|
|
3010
|
+
* Warn that the style rules (defaults, mappings, dependencies, table styles) are kept, not applied.
|
|
3011
|
+
* @param visual - the counted rules
|
|
3012
|
+
* @param tableStyles - the number of table style elements
|
|
3013
|
+
*/
|
|
3014
|
+
private reportStyleRules(visual: VisualCollection, tableStyles: number): void {
|
|
3015
|
+
const { defaults, mappings, dependencies } = visual;
|
|
3016
|
+
if (defaults + mappings + dependencies + tableStyles === 0) {
|
|
3017
|
+
return;
|
|
2948
3018
|
}
|
|
3019
|
+
const parts = [
|
|
3020
|
+
`cyVisualProperties: ${defaults} default${plural(defaults)}, ${mappings} mapping${plural(mappings)}, ${dependencies} dependenc(ies)`,
|
|
3021
|
+
];
|
|
3022
|
+
if (tableStyles > 0) {
|
|
3023
|
+
parts.push(`${tableStyles} table style element${plural(tableStyles)}`);
|
|
3024
|
+
}
|
|
3025
|
+
this.report.warning(
|
|
3026
|
+
"unsupported",
|
|
3027
|
+
STYLES_NOT_IMPORTED_CODE,
|
|
3028
|
+
`the file's style rules are not applied (${parts.join("; ")}); they are kept in meta.extra.cx`,
|
|
3029
|
+
{ element: "cyVisualProperties" },
|
|
3030
|
+
);
|
|
2949
3031
|
}
|
|
2950
3032
|
|
|
2951
3033
|
/**
|
|
@@ -304,9 +304,10 @@ function capabilityNotes(
|
|
|
304
304
|
continue;
|
|
305
305
|
}
|
|
306
306
|
if (gen.code === LOSS.JSON) {
|
|
307
|
+
const what = gen.column === null ? "a column" : `column "${gen.column}"`;
|
|
307
308
|
note(
|
|
308
309
|
CX2_LOSS.JSON_AS_STRING,
|
|
309
|
-
`${
|
|
310
|
+
`${what} holds nested values; written as a string attribute holding their JSON text`,
|
|
310
311
|
gen.column,
|
|
311
312
|
gen.count,
|
|
312
313
|
);
|
|
@@ -688,6 +688,25 @@ async function readDocument(
|
|
|
688
688
|
return doc;
|
|
689
689
|
}
|
|
690
690
|
|
|
691
|
+
/**
|
|
692
|
+
* Fail the import: the first member of the array is not a descriptor.
|
|
693
|
+
* @param value - the member
|
|
694
|
+
* @param line - its line
|
|
695
|
+
* @param report - the report
|
|
696
|
+
* @returns never: the import fails
|
|
697
|
+
*/
|
|
698
|
+
function failNoDescriptor(value: unknown, line: number, report: ImportReportBuilder): never {
|
|
699
|
+
const keys = isRecord(value) ? Object.keys(value) : [];
|
|
700
|
+
const keyList = keys.length > 0 ? ` (keys: ${keys.join(", ")})` : "";
|
|
701
|
+
return report.fail(
|
|
702
|
+
CX2_ISSUE.NO_DESCRIPTOR,
|
|
703
|
+
`the first member of the array is not a descriptor with CXVersion${keyList}`,
|
|
704
|
+
{
|
|
705
|
+
line,
|
|
706
|
+
},
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
|
|
691
710
|
/**
|
|
692
711
|
* Check the descriptor, the first member of the array.
|
|
693
712
|
* @param value - the member
|
|
@@ -697,12 +716,7 @@ async function readDocument(
|
|
|
697
716
|
*/
|
|
698
717
|
function readDescriptor(value: unknown, line: number, doc: Cx2Document, report: ImportReportBuilder): void {
|
|
699
718
|
if (!isRecord(value) || !("CXVersion" in value)) {
|
|
700
|
-
|
|
701
|
-
report.fail(
|
|
702
|
-
CX2_ISSUE.NO_DESCRIPTOR,
|
|
703
|
-
`the first member of the array is not a descriptor with CXVersion${keys.length > 0 ? ` (keys: ${keys.join(", ")})` : ""}`,
|
|
704
|
-
{ line },
|
|
705
|
-
);
|
|
719
|
+
failNoDescriptor(value, line, report);
|
|
706
720
|
}
|
|
707
721
|
const raw = value.CXVersion;
|
|
708
722
|
const text = typeof raw === "string" || typeof raw === "number" ? String(raw) : "";
|
|
@@ -779,10 +793,11 @@ function memberAspect(
|
|
|
779
793
|
collect(keys[0], value[keys[0]]);
|
|
780
794
|
return;
|
|
781
795
|
}
|
|
796
|
+
const what = keys.length > 1 ? `an object with ${keys.length} keys (${keys.join(", ")})` : shown(value);
|
|
782
797
|
report.error(
|
|
783
798
|
"parse-error",
|
|
784
799
|
BAD_ASPECT_BLOCK_CODE,
|
|
785
|
-
`a member of the document is ${
|
|
800
|
+
`a member of the document is ${what}, not a one-key aspect block; skipped`,
|
|
786
801
|
{ line, element: keys[0] ?? null },
|
|
787
802
|
);
|
|
788
803
|
}
|
|
@@ -1325,10 +1340,11 @@ class Cx2Reader {
|
|
|
1325
1340
|
}
|
|
1326
1341
|
const other = table.byName.get(alias) ?? table.byAlias.get(alias);
|
|
1327
1342
|
if (other !== undefined) {
|
|
1343
|
+
const clash = other.name === alias ? "an attribute's name" : `the alias of "${other.name}"`;
|
|
1328
1344
|
this.report.error(
|
|
1329
1345
|
"validation-error",
|
|
1330
1346
|
CX2_ISSUE.ALIAS_CONFLICT,
|
|
1331
|
-
`the alias "${alias}" of the ${domain} attribute "${decl.name}" is also ${
|
|
1347
|
+
`the alias "${alias}" of the ${domain} attribute "${decl.name}" is also ${clash}; the alias is ignored`,
|
|
1332
1348
|
{ element: decl.name },
|
|
1333
1349
|
);
|
|
1334
1350
|
decl.alias = null;
|
|
@@ -2259,6 +2275,14 @@ class Cx2Reader {
|
|
|
2259
2275
|
}
|
|
2260
2276
|
}
|
|
2261
2277
|
}
|
|
2278
|
+
this.checkStatus();
|
|
2279
|
+
structure.checkCounts();
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
/** The status block: present, and exactly one well-formed element. */
|
|
2283
|
+
private checkStatus(): void {
|
|
2284
|
+
const { report } = this;
|
|
2285
|
+
const { structure } = this.doc;
|
|
2262
2286
|
if (!structure.hasStatus) {
|
|
2263
2287
|
report.error("validation-error", CX2_ISSUE.NO_STATUS, "the document ends without a status block", {
|
|
2264
2288
|
element: "status",
|
|
@@ -2266,14 +2290,14 @@ class Cx2Reader {
|
|
|
2266
2290
|
} else if (!structure.statusWellFormed() || structure.counts.get("status") !== 1) {
|
|
2267
2291
|
// the specification: exactly one status aspect holding exactly one element
|
|
2268
2292
|
const count = structure.counts.get("status") ?? 0;
|
|
2293
|
+
const found = count === 1 ? shown(structure.status) : `${count} elements`;
|
|
2269
2294
|
report.error(
|
|
2270
2295
|
"validation-error",
|
|
2271
2296
|
CX2_ISSUE.NO_STATUS,
|
|
2272
|
-
`the status is malformed (${
|
|
2297
|
+
`the status is malformed (${found}): a document ends with one status block holding one object with a boolean success`,
|
|
2273
2298
|
{ element: "status" },
|
|
2274
2299
|
);
|
|
2275
2300
|
}
|
|
2276
|
-
structure.checkCounts();
|
|
2277
2301
|
}
|
|
2278
2302
|
|
|
2279
2303
|
/** Record the metadata: source format and version, name, description, id type, the kept aspects. */
|
|
@@ -217,24 +217,25 @@ export function cellAtt(
|
|
|
217
217
|
namespace: string | null,
|
|
218
218
|
hidden: boolean,
|
|
219
219
|
): AttRec | null {
|
|
220
|
+
const cell = { line, namespace, hidden };
|
|
220
221
|
if (text.startsWith("=")) {
|
|
221
|
-
return att(column.name, column.list ? "List" : column.type, null, text,
|
|
222
|
+
return att(column.name, column.list ? "List" : column.type, null, text, true, cell);
|
|
222
223
|
}
|
|
223
224
|
if (!column.list) {
|
|
224
225
|
if (text.length === 0 && column.type !== "String") {
|
|
225
226
|
return null;
|
|
226
227
|
}
|
|
227
|
-
return att(column.name, column.type, null, text,
|
|
228
|
+
return att(column.name, column.type, null, text, false, cell);
|
|
228
229
|
}
|
|
229
230
|
if (text.length === 0 && column.type !== "String") {
|
|
230
231
|
return null;
|
|
231
232
|
}
|
|
232
|
-
const list = att(column.name, "List", column.type, null,
|
|
233
|
+
const list = att(column.name, "List", column.type, null, false, cell);
|
|
233
234
|
for (const item of javaSplitLines(text)) {
|
|
234
235
|
if (item.length === 0 && column.type !== "String") {
|
|
235
236
|
continue;
|
|
236
237
|
}
|
|
237
|
-
list.children.push(att(column.name, column.type, null, item,
|
|
238
|
+
list.children.push(att(column.name, column.type, null, item, false, cell));
|
|
238
239
|
}
|
|
239
240
|
return list;
|
|
240
241
|
}
|
|
@@ -262,10 +263,11 @@ function javaSplitLines(text: string): string[] {
|
|
|
262
263
|
* @param cyType - the Cytoscape type
|
|
263
264
|
* @param elementType - a list's item type, or null
|
|
264
265
|
* @param value - the value, or null
|
|
265
|
-
* @param line - the line
|
|
266
|
-
* @param namespace - the table namespace, or null
|
|
267
|
-
* @param hidden - a hidden column
|
|
268
266
|
* @param equation - the value is a formula
|
|
267
|
+
* @param cell - where the cell sits
|
|
268
|
+
* @param cell.line - the line
|
|
269
|
+
* @param cell.namespace - the table namespace, or null
|
|
270
|
+
* @param cell.hidden - a hidden column
|
|
269
271
|
* @returns the record
|
|
270
272
|
*/
|
|
271
273
|
function att(
|
|
@@ -273,11 +275,10 @@ function att(
|
|
|
273
275
|
cyType: string,
|
|
274
276
|
elementType: string | null,
|
|
275
277
|
value: string | null,
|
|
276
|
-
line: number,
|
|
277
|
-
namespace: string | null,
|
|
278
|
-
hidden: boolean,
|
|
279
278
|
equation: boolean,
|
|
279
|
+
cell: { readonly line: number; readonly namespace: string | null; readonly hidden: boolean },
|
|
280
280
|
): AttRec {
|
|
281
|
+
const { line, namespace, hidden } = cell;
|
|
281
282
|
return {
|
|
282
283
|
name,
|
|
283
284
|
type: null,
|
|
@@ -1225,7 +1225,8 @@ function lifetimeAttrs(roles: RoleColumns, row: number, plan: ExportPlan): strin
|
|
|
1225
1225
|
const text = timeText(column, roles.companions.get(column.meta.name) ?? null, row, timeFormat);
|
|
1226
1226
|
// GEXF 1.2: `startopen` / `endopen` hold the time of a non-inclusive bound (dynamics.xsd
|
|
1227
1227
|
// time-type) and replace `start` / `end`
|
|
1228
|
-
|
|
1228
|
+
const attribute = open ? `${name}open` : name;
|
|
1229
|
+
out += ` ${attribute}="${escapeXmlAttribute(text)}"`;
|
|
1229
1230
|
};
|
|
1230
1231
|
if (roles.timestamp?.isSet(row)) {
|
|
1231
1232
|
if (version === "1.3" && plan.timeRepresentation === "timestamp") {
|
|
@@ -1240,11 +1241,33 @@ function lifetimeAttrs(roles: RoleColumns, row: number, plan: ExportPlan): strin
|
|
|
1240
1241
|
}
|
|
1241
1242
|
if (version === "1.3" && roles.timestamps?.isSet(row)) {
|
|
1242
1243
|
const items = listItems(roles.timestamps, row).map((t) => formatTimeValue(t as number, timeFormat));
|
|
1243
|
-
|
|
1244
|
+
const stamps = `<[${items.join(", ")}]>`;
|
|
1245
|
+
out += ` timestamps="${escapeXmlAttribute(stamps)}"`;
|
|
1244
1246
|
}
|
|
1245
1247
|
return out;
|
|
1246
1248
|
}
|
|
1247
1249
|
|
|
1250
|
+
/**
|
|
1251
|
+
* The `start` and `end` attributes of one `<spell>`.
|
|
1252
|
+
* @param s - the start, or a non-finite value for an unbounded start
|
|
1253
|
+
* @param e - the end, or a non-finite value for an unbounded end
|
|
1254
|
+
* @param open - the open-bound bits
|
|
1255
|
+
* @param plan - the plan
|
|
1256
|
+
* @returns the attribute text, each with a leading space
|
|
1257
|
+
*/
|
|
1258
|
+
function spellBounds(s: number, e: number, open: number, plan: ExportPlan): string {
|
|
1259
|
+
let attrs = "";
|
|
1260
|
+
if (Number.isFinite(s)) {
|
|
1261
|
+
const name = (open & OPEN_START) === 0 ? "start" : "startopen";
|
|
1262
|
+
attrs += ` ${name}="${escapeXmlAttribute(formatTimeValue(s, plan.timeFormat))}"`;
|
|
1263
|
+
}
|
|
1264
|
+
if (Number.isFinite(e)) {
|
|
1265
|
+
const name = (open & OPEN_END) === 0 ? "end" : "endopen";
|
|
1266
|
+
attrs += ` ${name}="${escapeXmlAttribute(formatTimeValue(e, plan.timeFormat))}"`;
|
|
1267
|
+
}
|
|
1268
|
+
return attrs;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1248
1271
|
/**
|
|
1249
1272
|
* The `<spells>` element of one element: its spells column, plus (1.2) its timestamps as [t, t].
|
|
1250
1273
|
* In 1.2 a spell whose spells.open bits mark a bound open writes that bound as `startopen` /
|
|
@@ -1274,16 +1297,7 @@ function spellsElement(roles: RoleColumns, row: number, plan: ExportPlan, indent
|
|
|
1274
1297
|
}
|
|
1275
1298
|
let out = `${indent}<spells>\n`;
|
|
1276
1299
|
for (const [s, e, open] of pairs) {
|
|
1277
|
-
|
|
1278
|
-
if (Number.isFinite(s)) {
|
|
1279
|
-
const name = (open & OPEN_START) === 0 ? "start" : "startopen";
|
|
1280
|
-
attrs += ` ${name}="${escapeXmlAttribute(formatTimeValue(s, plan.timeFormat))}"`;
|
|
1281
|
-
}
|
|
1282
|
-
if (Number.isFinite(e)) {
|
|
1283
|
-
const name = (open & OPEN_END) === 0 ? "end" : "endopen";
|
|
1284
|
-
attrs += ` ${name}="${escapeXmlAttribute(formatTimeValue(e, plan.timeFormat))}"`;
|
|
1285
|
-
}
|
|
1286
|
-
out += `${indent} <spell${attrs}/>\n`;
|
|
1300
|
+
out += `${indent} <spell${spellBounds(s, e, open, plan)}/>\n`;
|
|
1287
1301
|
}
|
|
1288
1302
|
return `${out}${indent}</spells>\n`;
|
|
1289
1303
|
}
|
|
@@ -1674,23 +1688,12 @@ function metaElement(meta: GraphSnapshot["meta"]): string {
|
|
|
1674
1688
|
}
|
|
1675
1689
|
|
|
1676
1690
|
/**
|
|
1677
|
-
* The `<graph>`
|
|
1691
|
+
* The `idtype` attribute of the `<graph>` tag.
|
|
1678
1692
|
* @param snapshot - the snapshot
|
|
1679
|
-
* @
|
|
1680
|
-
* @returns the line
|
|
1693
|
+
* @returns the attribute text with a leading space, or an empty string
|
|
1681
1694
|
*/
|
|
1682
|
-
function
|
|
1683
|
-
|
|
1684
|
-
let attrs = ` defaultedgetype="${defaultEdgeType(snapshot)}"`;
|
|
1685
|
-
let mode = meta.mode ?? "static";
|
|
1686
|
-
if (plan.temporal && mode === "static") {
|
|
1687
|
-
mode = "dynamic";
|
|
1688
|
-
}
|
|
1689
|
-
if (plan.version === "1.2" && mode === "slice") {
|
|
1690
|
-
mode = "dynamic";
|
|
1691
|
-
}
|
|
1692
|
-
attrs += ` mode="${mode}"`;
|
|
1693
|
-
let { idType } = meta;
|
|
1695
|
+
function graphIdTypeAttr(snapshot: GraphSnapshot): string {
|
|
1696
|
+
let { idType } = snapshot.meta;
|
|
1694
1697
|
if (idType === null) {
|
|
1695
1698
|
const { kind } = snapshot.ids;
|
|
1696
1699
|
if (kind === "string") {
|
|
@@ -1699,17 +1702,16 @@ function graphStart(snapshot: GraphSnapshot, plan: ExportPlan): string {
|
|
|
1699
1702
|
idType = "integer";
|
|
1700
1703
|
}
|
|
1701
1704
|
}
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
}
|
|
1705
|
+
return idType === "integer" || idType === "string" ? ` idtype="${idType}"` : "";
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
/**
|
|
1709
|
+
* The `start`, `end` and `timestamp` attributes of the `<graph>` tag.
|
|
1710
|
+
* @param snapshot - the snapshot
|
|
1711
|
+
* @param plan - the plan
|
|
1712
|
+
* @returns the attribute text with leading spaces, or an empty string
|
|
1713
|
+
*/
|
|
1714
|
+
function graphHeaderAttrs(snapshot: GraphSnapshot, plan: ExportPlan): string {
|
|
1713
1715
|
const timestamp = graphExtraText(snapshot, "timestamp");
|
|
1714
1716
|
const header: Record<string, string | null> = {
|
|
1715
1717
|
start: graphExtraText(snapshot, "start"),
|
|
@@ -1722,11 +1724,42 @@ function graphStart(snapshot: GraphSnapshot, plan: ExportPlan): string {
|
|
|
1722
1724
|
header.end ??= timestamp;
|
|
1723
1725
|
header.timestamp = null;
|
|
1724
1726
|
}
|
|
1727
|
+
let attrs = "";
|
|
1725
1728
|
for (const [key, value] of Object.entries(header)) {
|
|
1726
1729
|
if (value !== null) {
|
|
1727
1730
|
attrs += ` ${key}="${escapeXmlAttribute(value)}"`;
|
|
1728
1731
|
}
|
|
1729
1732
|
}
|
|
1733
|
+
return attrs;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
/**
|
|
1737
|
+
* The `<graph>` start tag with its header attributes.
|
|
1738
|
+
* @param snapshot - the snapshot
|
|
1739
|
+
* @param plan - the plan
|
|
1740
|
+
* @returns the line
|
|
1741
|
+
*/
|
|
1742
|
+
function graphStart(snapshot: GraphSnapshot, plan: ExportPlan): string {
|
|
1743
|
+
const { meta } = snapshot;
|
|
1744
|
+
let attrs = ` defaultedgetype="${defaultEdgeType(snapshot)}"`;
|
|
1745
|
+
let mode = meta.mode ?? "static";
|
|
1746
|
+
if (plan.temporal && mode === "static") {
|
|
1747
|
+
mode = "dynamic";
|
|
1748
|
+
}
|
|
1749
|
+
if (plan.version === "1.2" && mode === "slice") {
|
|
1750
|
+
mode = "dynamic";
|
|
1751
|
+
}
|
|
1752
|
+
attrs += ` mode="${mode}"`;
|
|
1753
|
+
attrs += graphIdTypeAttr(snapshot);
|
|
1754
|
+
if (plan.timeFormat !== null) {
|
|
1755
|
+
attrs += ` timeformat="${plan.timeFormat}"`;
|
|
1756
|
+
} else if (plan.temporal) {
|
|
1757
|
+
attrs += ' timeformat="double"';
|
|
1758
|
+
}
|
|
1759
|
+
if (plan.version === "1.3" && plan.timeRepresentation !== null) {
|
|
1760
|
+
attrs += ` timerepresentation="${plan.timeRepresentation}"`;
|
|
1761
|
+
}
|
|
1762
|
+
attrs += graphHeaderAttrs(snapshot, plan);
|
|
1730
1763
|
return ` <graph${attrs}>\n`;
|
|
1731
1764
|
}
|
|
1732
1765
|
|