@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
|
@@ -1328,20 +1328,15 @@ class GexfReader implements XmlHandler {
|
|
|
1328
1328
|
}
|
|
1329
1329
|
|
|
1330
1330
|
/**
|
|
1331
|
-
*
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1334
|
-
* @param
|
|
1331
|
+
* Whether an attribute was already declared (warning that the first declaration is kept), and
|
|
1332
|
+
* a warning when values that use it came before its declaration.
|
|
1333
|
+
* @param domain - node or edge
|
|
1334
|
+
* @param id - the attribute id
|
|
1335
|
+
* @param where - the report location
|
|
1336
|
+
* @returns true when the declaration is a duplicate and must be skipped
|
|
1335
1337
|
*/
|
|
1336
|
-
private
|
|
1337
|
-
const { report
|
|
1338
|
-
const { domain, dynamic, attrs } = pending;
|
|
1339
|
-
const id = attrs.get("id");
|
|
1340
|
-
const where = { line: pending.line, element: id ?? null };
|
|
1341
|
-
if (id === undefined) {
|
|
1342
|
-
report.error("validation-error", ATTRIBUTE_ID_CODE, "<attribute> without an id", where);
|
|
1343
|
-
return;
|
|
1344
|
-
}
|
|
1338
|
+
private isDuplicateAttribute(domain: "node" | "edge", id: string, where: IssueLocation): boolean {
|
|
1339
|
+
const { report } = this;
|
|
1345
1340
|
const tracked = domain === "node" ? this.nodeAttrs : this.edgeAttrs;
|
|
1346
1341
|
if (tracked.has(id) || (domain === "edge" && this.weightAttr !== null && this.weightAttr.id === id)) {
|
|
1347
1342
|
report.warning(
|
|
@@ -1350,7 +1345,7 @@ class GexfReader implements XmlHandler {
|
|
|
1350
1345
|
`${domain} attribute "${id}" is declared twice; the first declaration is kept`,
|
|
1351
1346
|
where,
|
|
1352
1347
|
);
|
|
1353
|
-
return;
|
|
1348
|
+
return true;
|
|
1354
1349
|
}
|
|
1355
1350
|
if (this.unknownAttributes.has(`${domain}:${id}`)) {
|
|
1356
1351
|
report.warning(
|
|
@@ -1360,6 +1355,68 @@ class GexfReader implements XmlHandler {
|
|
|
1360
1355
|
where,
|
|
1361
1356
|
);
|
|
1362
1357
|
}
|
|
1358
|
+
return false;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/**
|
|
1362
|
+
* Whether an edge attribute becomes THE weight: the first attribute of the weight's title does
|
|
1363
|
+
* (declared here), a later one only warns and stays a plain column.
|
|
1364
|
+
* @param attribute - the attribute's domain, id, display name, title and type
|
|
1365
|
+
* @param attribute.domain - node or edge
|
|
1366
|
+
* @param attribute.id - the attribute id
|
|
1367
|
+
* @param attribute.name - the column name
|
|
1368
|
+
* @param attribute.title - the declared title, or null
|
|
1369
|
+
* @param attribute.type - the declared type, or null
|
|
1370
|
+
* @param where - the report location
|
|
1371
|
+
* @returns true when the attribute was declared as the weight
|
|
1372
|
+
*/
|
|
1373
|
+
private claimsWeight(
|
|
1374
|
+
attribute: { domain: "node" | "edge"; id: string; name: string; title: string | null; type: string | null },
|
|
1375
|
+
where: IssueLocation,
|
|
1376
|
+
): boolean {
|
|
1377
|
+
const { report, sink, options } = this;
|
|
1378
|
+
const { domain, id, name, title, type } = attribute;
|
|
1379
|
+
if (domain !== "edge" || !isWeightField(name, options.weightFrom)) {
|
|
1380
|
+
return false;
|
|
1381
|
+
}
|
|
1382
|
+
if (this.weightAttr !== null) {
|
|
1383
|
+
// the first attribute of that title stays THE weight; this one becomes a plain column
|
|
1384
|
+
report.warning(
|
|
1385
|
+
"validation-error",
|
|
1386
|
+
DUPLICATE_ATTRIBUTE_CODE,
|
|
1387
|
+
`edge attribute "${id}" is also titled ${name}; attribute "${this.weightAttr.id}" is the weight, "${id}" is kept as a column`,
|
|
1388
|
+
where,
|
|
1389
|
+
);
|
|
1390
|
+
return false;
|
|
1391
|
+
}
|
|
1392
|
+
const declared = declareAttribute({ format: GEXF_FORMAT, id, title, type, long: options.long });
|
|
1393
|
+
for (const issue of declared.issues) {
|
|
1394
|
+
report.warning(issue.category, issue.code, issue.message, where);
|
|
1395
|
+
}
|
|
1396
|
+
this.weightAttr = { id, spec: declared.spec, table: INVALID_INDEX as ExtensionHandle };
|
|
1397
|
+
sink.setMeta({ weightOrigin: { format: GEXF_FORMAT, id, title, type, namespace: null } });
|
|
1398
|
+
return true;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
/**
|
|
1402
|
+
* Declare one `<attribute id title type>` with its `<default>` and `<options>` (design
|
|
1403
|
+
* sections 5.1, 5.5, 5.6). The edge attribute titled `weightFrom` is the weight and gets no
|
|
1404
|
+
* column; its declaration is recorded in meta.weightOrigin.
|
|
1405
|
+
* @param pending - the attribute as read
|
|
1406
|
+
*/
|
|
1407
|
+
private declareOne(pending: PendingAttribute): void {
|
|
1408
|
+
const { report, sink, options } = this;
|
|
1409
|
+
const { domain, dynamic, attrs } = pending;
|
|
1410
|
+
const id = attrs.get("id");
|
|
1411
|
+
const where = { line: pending.line, element: id ?? null };
|
|
1412
|
+
if (id === undefined) {
|
|
1413
|
+
report.error("validation-error", ATTRIBUTE_ID_CODE, "<attribute> without an id", where);
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
const tracked = domain === "node" ? this.nodeAttrs : this.edgeAttrs;
|
|
1417
|
+
if (this.isDuplicateAttribute(domain, id, where)) {
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1363
1420
|
const title = attrs.get("title") ?? null;
|
|
1364
1421
|
const type = attrs.get("type") ?? null;
|
|
1365
1422
|
if (type === null) {
|
|
@@ -1371,21 +1428,7 @@ class GexfReader implements XmlHandler {
|
|
|
1371
1428
|
);
|
|
1372
1429
|
}
|
|
1373
1430
|
const name = title !== null && title.length > 0 ? title : id;
|
|
1374
|
-
if (
|
|
1375
|
-
// the first attribute of that title stays THE weight; this one becomes a plain column
|
|
1376
|
-
report.warning(
|
|
1377
|
-
"validation-error",
|
|
1378
|
-
DUPLICATE_ATTRIBUTE_CODE,
|
|
1379
|
-
`edge attribute "${id}" is also titled ${name}; attribute "${this.weightAttr.id}" is the weight, "${id}" is kept as a column`,
|
|
1380
|
-
where,
|
|
1381
|
-
);
|
|
1382
|
-
} else if (domain === "edge" && isWeightField(name, options.weightFrom)) {
|
|
1383
|
-
const declared = declareAttribute({ format: GEXF_FORMAT, id, title, type, long: options.long });
|
|
1384
|
-
for (const issue of declared.issues) {
|
|
1385
|
-
report.warning(issue.category, issue.code, issue.message, where);
|
|
1386
|
-
}
|
|
1387
|
-
this.weightAttr = { id, spec: declared.spec, table: INVALID_INDEX as ExtensionHandle };
|
|
1388
|
-
sink.setMeta({ weightOrigin: { format: GEXF_FORMAT, id, title, type, namespace: null } });
|
|
1431
|
+
if (this.claimsWeight({ domain, id, name, title, type }, where)) {
|
|
1389
1432
|
return;
|
|
1390
1433
|
}
|
|
1391
1434
|
const declaredNames = domain === "node" ? this.declaredNodeNames : this.declaredEdgeNames;
|
|
@@ -1717,6 +1760,15 @@ class GexfReader implements XmlHandler {
|
|
|
1717
1760
|
report.recordError(err, ref.where);
|
|
1718
1761
|
}
|
|
1719
1762
|
}
|
|
1763
|
+
this.resolveParentsLists(lists);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
/**
|
|
1767
|
+
* Resolve the `<parents>` lists read so far into the parents column.
|
|
1768
|
+
* @param lists - the queued `<parents>` lists
|
|
1769
|
+
*/
|
|
1770
|
+
private resolveParentsLists(lists: readonly (typeof this.parentsRefs)[number][]): void {
|
|
1771
|
+
const { sink, report } = this;
|
|
1720
1772
|
for (const ref of lists) {
|
|
1721
1773
|
const indices: number[] = [];
|
|
1722
1774
|
for (const id of ref.ids) {
|
|
@@ -1763,7 +1815,7 @@ class GexfReader implements XmlHandler {
|
|
|
1763
1815
|
* @param line - the line
|
|
1764
1816
|
*/
|
|
1765
1817
|
private beginEdge(attrs: ReadonlyMap<string, string>, line: number): void {
|
|
1766
|
-
const { report, sink
|
|
1818
|
+
const { report, sink } = this;
|
|
1767
1819
|
this.checkAbort();
|
|
1768
1820
|
this.ctx.push(Ctx.Edge);
|
|
1769
1821
|
this.edge = null;
|
|
@@ -1816,18 +1868,7 @@ class GexfReader implements XmlHandler {
|
|
|
1816
1868
|
try {
|
|
1817
1869
|
const source = this.coerceId(sourceText, where);
|
|
1818
1870
|
const target = this.coerceId(targetText, where);
|
|
1819
|
-
|
|
1820
|
-
if (sink.indexOf(source) === INVALID_INDEX) {
|
|
1821
|
-
throw new GraphFormatError("E_UNKNOWN_NODE", `edge references undeclared node "${sourceText}"`, {
|
|
1822
|
-
id: source,
|
|
1823
|
-
});
|
|
1824
|
-
}
|
|
1825
|
-
if (sink.indexOf(target) === INVALID_INDEX) {
|
|
1826
|
-
throw new GraphFormatError("E_UNKNOWN_NODE", `edge references undeclared node "${targetText}"`, {
|
|
1827
|
-
id: target,
|
|
1828
|
-
});
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1871
|
+
this.requireDeclaredEndpoints(source, target, sourceText, targetText);
|
|
1831
1872
|
const weight = this.xmlWeight(attrs, where);
|
|
1832
1873
|
const before = sink.edgeCount;
|
|
1833
1874
|
index = this.resolver.addEdge(source, target, kind, weight, where);
|
|
@@ -1846,6 +1887,47 @@ class GexfReader implements XmlHandler {
|
|
|
1846
1887
|
frame.mirror = this.resolver.lastMirror;
|
|
1847
1888
|
frame.xmlWeight = attrs.get("weight") ?? null;
|
|
1848
1889
|
this.edge = frame;
|
|
1890
|
+
this.writeEdgeColumns(attrs, index, idText, where);
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
/**
|
|
1894
|
+
* Throw when addMissingNodes is off and an endpoint names an undeclared node.
|
|
1895
|
+
* @param source - the coerced source id
|
|
1896
|
+
* @param target - the coerced target id
|
|
1897
|
+
* @param sourceText - the source as written
|
|
1898
|
+
* @param targetText - the target as written
|
|
1899
|
+
*/
|
|
1900
|
+
private requireDeclaredEndpoints(source: NodeId, target: NodeId, sourceText: string, targetText: string): void {
|
|
1901
|
+
const { sink, options } = this;
|
|
1902
|
+
if (options.addMissingNodes) {
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
if (sink.indexOf(source) === INVALID_INDEX) {
|
|
1906
|
+
throw new GraphFormatError("E_UNKNOWN_NODE", `edge references undeclared node "${sourceText}"`, {
|
|
1907
|
+
id: source,
|
|
1908
|
+
});
|
|
1909
|
+
}
|
|
1910
|
+
if (sink.indexOf(target) === INVALID_INDEX) {
|
|
1911
|
+
throw new GraphFormatError("E_UNKNOWN_NODE", `edge references undeclared node "${targetText}"`, {
|
|
1912
|
+
id: target,
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
/**
|
|
1918
|
+
* Write the id, label, kind and lifetime columns of an added edge; a failure is reported.
|
|
1919
|
+
* @param attrs - the edge's attributes
|
|
1920
|
+
* @param index - the edge's index
|
|
1921
|
+
* @param idText - the edge's id, or undefined
|
|
1922
|
+
* @param where - the edge's location
|
|
1923
|
+
*/
|
|
1924
|
+
private writeEdgeColumns(
|
|
1925
|
+
attrs: ReadonlyMap<string, string>,
|
|
1926
|
+
index: number,
|
|
1927
|
+
idText: string | undefined,
|
|
1928
|
+
where: IssueLocation,
|
|
1929
|
+
): void {
|
|
1930
|
+
const { report, sink } = this;
|
|
1849
1931
|
try {
|
|
1850
1932
|
if (idText !== undefined) {
|
|
1851
1933
|
this.edgeIdColumn.set(sink, report, index, idText, where);
|
|
@@ -2555,35 +2637,18 @@ class GexfReader implements XmlHandler {
|
|
|
2555
2637
|
case "color":
|
|
2556
2638
|
columns.color.set(sink, report, index, parseColor(attrs), where);
|
|
2557
2639
|
break;
|
|
2558
|
-
case "position":
|
|
2559
|
-
|
|
2560
|
-
const y = vizNumber(attrs, "y");
|
|
2561
|
-
const z = attrs.has("z") ? vizNumber(attrs, "z") : 0;
|
|
2562
|
-
this.positionColumn ??= new LazyColumn("node", {
|
|
2563
|
-
...NODE_DECLS.position,
|
|
2564
|
-
extra: { ...NODE_DECLS.position.extra, sourceDims: attrs.has("z") ? 3 : 2 },
|
|
2565
|
-
});
|
|
2566
|
-
this.positionColumn.set(sink, report, index, [x, y, z], where);
|
|
2640
|
+
case "position":
|
|
2641
|
+
this.writePosition(attrs, index, where);
|
|
2567
2642
|
break;
|
|
2568
|
-
}
|
|
2569
2643
|
case "size":
|
|
2570
2644
|
this.sizeColumn.set(sink, report, index, vizNumber(attrs, "value"), where);
|
|
2571
2645
|
break;
|
|
2572
2646
|
case "thickness":
|
|
2573
2647
|
this.thicknessColumn.set(sink, report, index, vizNumber(attrs, "value"), where);
|
|
2574
2648
|
break;
|
|
2575
|
-
case "shape":
|
|
2576
|
-
|
|
2577
|
-
if (value === undefined) {
|
|
2578
|
-
throw new GraphFormatError("E_COLUMN_TYPE", "<viz:shape> without a value", { element: name });
|
|
2579
|
-
}
|
|
2580
|
-
columns.shape.set(sink, report, index, value, where);
|
|
2581
|
-
const uri = attrs.get("uri");
|
|
2582
|
-
if (domain === "node" && uri !== undefined) {
|
|
2583
|
-
this.shapeUriColumn.set(sink, report, index, uri, where);
|
|
2584
|
-
}
|
|
2649
|
+
case "shape":
|
|
2650
|
+
this.writeShape(name, attrs, domain, index, where);
|
|
2585
2651
|
break;
|
|
2586
|
-
}
|
|
2587
2652
|
default:
|
|
2588
2653
|
break;
|
|
2589
2654
|
}
|
|
@@ -2595,6 +2660,51 @@ class GexfReader implements XmlHandler {
|
|
|
2595
2660
|
}
|
|
2596
2661
|
}
|
|
2597
2662
|
|
|
2663
|
+
/**
|
|
2664
|
+
* Write a `<viz:position>`.
|
|
2665
|
+
* @param attrs - its attributes
|
|
2666
|
+
* @param index - the node's index
|
|
2667
|
+
* @param where - the node's location
|
|
2668
|
+
*/
|
|
2669
|
+
private writePosition(attrs: ReadonlyMap<string, string>, index: number, where: IssueLocation): void {
|
|
2670
|
+
const x = vizNumber(attrs, "x");
|
|
2671
|
+
const y = vizNumber(attrs, "y");
|
|
2672
|
+
const z = attrs.has("z") ? vizNumber(attrs, "z") : 0;
|
|
2673
|
+
this.positionColumn ??= new LazyColumn("node", {
|
|
2674
|
+
...NODE_DECLS.position,
|
|
2675
|
+
extra: { ...NODE_DECLS.position.extra, sourceDims: attrs.has("z") ? 3 : 2 },
|
|
2676
|
+
});
|
|
2677
|
+
this.positionColumn.set(this.sink, this.report, index, [x, y, z], where);
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
/**
|
|
2681
|
+
* Write a `<viz:shape>` and, on a node, its `uri`.
|
|
2682
|
+
* @param name - the element name as written
|
|
2683
|
+
* @param attrs - its attributes
|
|
2684
|
+
* @param domain - node or edge
|
|
2685
|
+
* @param index - the element's index
|
|
2686
|
+
* @param where - the element's location
|
|
2687
|
+
*/
|
|
2688
|
+
private writeShape(
|
|
2689
|
+
name: string,
|
|
2690
|
+
attrs: ReadonlyMap<string, string>,
|
|
2691
|
+
domain: "node" | "edge",
|
|
2692
|
+
index: number,
|
|
2693
|
+
where: IssueLocation,
|
|
2694
|
+
): void {
|
|
2695
|
+
const { sink, report } = this;
|
|
2696
|
+
const columns = domain === "node" ? this.nodeColumns : this.edgeColumns;
|
|
2697
|
+
const value = attrs.get("value");
|
|
2698
|
+
if (value === undefined) {
|
|
2699
|
+
throw new GraphFormatError("E_COLUMN_TYPE", "<viz:shape> without a value", { element: name });
|
|
2700
|
+
}
|
|
2701
|
+
columns.shape.set(sink, report, index, value, where);
|
|
2702
|
+
const uri = attrs.get("uri");
|
|
2703
|
+
if (domain === "node" && uri !== undefined) {
|
|
2704
|
+
this.shapeUriColumn.set(sink, report, index, uri, where);
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2598
2708
|
// ---------------------------------------------------------------- helpers
|
|
2599
2709
|
|
|
2600
2710
|
/**
|
|
@@ -420,6 +420,20 @@ function note(code: string, message: string, column: string | null = null, count
|
|
|
420
420
|
return Object.freeze({ code, message, column, count });
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
+
/**
|
|
424
|
+
* Count a kind of json value once per row.
|
|
425
|
+
* @param seen - the flags already counted for this row
|
|
426
|
+
* @param flag - the flag of the kind
|
|
427
|
+
* @param stats - the counters
|
|
428
|
+
* @param counter - the counter of the kind
|
|
429
|
+
*/
|
|
430
|
+
function countOnce(seen: SeenFlags, flag: keyof SeenFlags, stats: JsonStats, counter: keyof JsonStats): void {
|
|
431
|
+
if (!seen[flag]) {
|
|
432
|
+
seen[flag] = true;
|
|
433
|
+
stats[counter]++;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
423
437
|
/**
|
|
424
438
|
* Inspect a JSON value for what GML cannot write exactly.
|
|
425
439
|
* @param value - the value
|
|
@@ -429,24 +443,15 @@ function note(code: string, message: string, column: string | null = null, count
|
|
|
429
443
|
*/
|
|
430
444
|
function inspectJson(value: unknown, stats: JsonStats, inArray: boolean, seen: SeenFlags): void {
|
|
431
445
|
if (value === null) {
|
|
432
|
-
|
|
433
|
-
seen.nulls = true;
|
|
434
|
-
stats.nulls++;
|
|
435
|
-
}
|
|
446
|
+
countOnce(seen, "nulls", stats, "nulls");
|
|
436
447
|
return;
|
|
437
448
|
}
|
|
438
449
|
switch (typeof value) {
|
|
439
450
|
case "number":
|
|
440
|
-
|
|
441
|
-
seen.numbers = true;
|
|
442
|
-
stats.numbers++;
|
|
443
|
-
}
|
|
451
|
+
countOnce(seen, "numbers", stats, "numbers");
|
|
444
452
|
return;
|
|
445
453
|
case "boolean":
|
|
446
|
-
|
|
447
|
-
seen.booleans = true;
|
|
448
|
-
stats.booleans++;
|
|
449
|
-
}
|
|
454
|
+
countOnce(seen, "booleans", stats, "booleans");
|
|
450
455
|
return;
|
|
451
456
|
case "string":
|
|
452
457
|
return;
|
|
@@ -454,9 +459,8 @@ function inspectJson(value: unknown, stats: JsonStats, inArray: boolean, seen: S
|
|
|
454
459
|
break;
|
|
455
460
|
}
|
|
456
461
|
if (Array.isArray(value)) {
|
|
457
|
-
if (inArray
|
|
458
|
-
seen
|
|
459
|
-
stats.nestedArrays++;
|
|
462
|
+
if (inArray) {
|
|
463
|
+
countOnce(seen, "nested", stats, "nestedArrays");
|
|
460
464
|
}
|
|
461
465
|
for (const item of value) {
|
|
462
466
|
inspectJson(item, stats, true, seen);
|
|
@@ -472,6 +476,29 @@ function inspectJson(value: unknown, stats: JsonStats, inArray: boolean, seen: S
|
|
|
472
476
|
}
|
|
473
477
|
}
|
|
474
478
|
|
|
479
|
+
/**
|
|
480
|
+
* A row count in words.
|
|
481
|
+
* @param count - the count
|
|
482
|
+
* @returns the count and the word row, pluralised
|
|
483
|
+
*/
|
|
484
|
+
function rowCount(count: number): string {
|
|
485
|
+
return `${count} row${plural(count)}`;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Record a note for a counter that is not zero.
|
|
490
|
+
* @param notes - where to record
|
|
491
|
+
* @param count - the counter
|
|
492
|
+
* @param code - the note code
|
|
493
|
+
* @param text - the note message
|
|
494
|
+
* @param column - the column name
|
|
495
|
+
*/
|
|
496
|
+
function pushCounted(notes: LossNote[], count: number, code: string, text: string, column: string): void {
|
|
497
|
+
if (count > 0) {
|
|
498
|
+
notes.push(note(code, text, column, count));
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
475
502
|
/**
|
|
476
503
|
* The GML-specific notes of one table's json columns.
|
|
477
504
|
* @param columns - the written columns
|
|
@@ -487,73 +514,42 @@ function jsonNotes(columns: readonly WrittenColumn[], label: string, plan: GmlEx
|
|
|
487
514
|
}
|
|
488
515
|
const { name } = column.meta;
|
|
489
516
|
const where = `${label} column "${name}"`;
|
|
490
|
-
|
|
491
|
-
notes
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
stats.arrays,
|
|
527
|
-
),
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
if (stats.nestedArrays > 0) {
|
|
531
|
-
notes.push(
|
|
532
|
-
note(
|
|
533
|
-
NESTED_ARRAY_CODE,
|
|
534
|
-
`${where} holds arrays nested in arrays in ${stats.nestedArrays} row${plural(stats.nestedArrays)}; GML cannot write them and the save fails`,
|
|
535
|
-
name,
|
|
536
|
-
stats.nestedArrays,
|
|
537
|
-
),
|
|
538
|
-
);
|
|
539
|
-
}
|
|
540
|
-
if (stats.invalidKeys > 0) {
|
|
541
|
-
notes.push(
|
|
542
|
-
plan.mangle
|
|
543
|
-
? note(
|
|
544
|
-
KEY_MANGLED_CODE,
|
|
545
|
-
`${where}: ${stats.invalidKeys} record key${plural(stats.invalidKeys)} ${agree(stats.invalidKeys, "is", "are")} not GML keys and are rewritten`,
|
|
546
|
-
name,
|
|
547
|
-
stats.invalidKeys,
|
|
548
|
-
)
|
|
549
|
-
: note(
|
|
550
|
-
INVALID_KEY_CODE,
|
|
551
|
-
`${where}: ${stats.invalidKeys} record key${plural(stats.invalidKeys)} ${agree(stats.invalidKeys, "is", "are")} not GML keys; the save fails unless sanitizeKeys is "mangle"`,
|
|
552
|
-
name,
|
|
553
|
-
stats.invalidKeys,
|
|
554
|
-
),
|
|
555
|
-
);
|
|
556
|
-
}
|
|
517
|
+
const counted = (count: number, code: string, text: string): void => {
|
|
518
|
+
pushCounted(notes, count, code, text, name);
|
|
519
|
+
};
|
|
520
|
+
counted(
|
|
521
|
+
stats.numbers,
|
|
522
|
+
RECORD_NUMBER_TYPE_CODE,
|
|
523
|
+
`${where} holds numbers in ${rowCount(stats.numbers)}; GML records cannot keep the int / real distinction`,
|
|
524
|
+
);
|
|
525
|
+
counted(
|
|
526
|
+
stats.booleans,
|
|
527
|
+
RECORD_BOOLEAN_CODE,
|
|
528
|
+
`${where} holds booleans in ${rowCount(stats.booleans)}; written as 1 / 0`,
|
|
529
|
+
);
|
|
530
|
+
counted(
|
|
531
|
+
stats.nulls,
|
|
532
|
+
RECORD_NULL_CODE,
|
|
533
|
+
`${where} holds nulls in ${rowCount(stats.nulls)}; GML has no null, the key is omitted`,
|
|
534
|
+
);
|
|
535
|
+
counted(
|
|
536
|
+
stats.arrays,
|
|
537
|
+
JSON_ARRAY_CODE,
|
|
538
|
+
`${where} holds arrays as values in ${rowCount(stats.arrays)}; written as repeated keys, they re-import as a list`,
|
|
539
|
+
);
|
|
540
|
+
counted(
|
|
541
|
+
stats.nestedArrays,
|
|
542
|
+
NESTED_ARRAY_CODE,
|
|
543
|
+
`${where} holds arrays nested in arrays in ${rowCount(stats.nestedArrays)}; GML cannot write them and the save fails`,
|
|
544
|
+
);
|
|
545
|
+
const keys = `${stats.invalidKeys} record key${plural(stats.invalidKeys)} ${agree(stats.invalidKeys, "is", "are")} not GML keys`;
|
|
546
|
+
counted(
|
|
547
|
+
stats.invalidKeys,
|
|
548
|
+
plan.mangle ? KEY_MANGLED_CODE : INVALID_KEY_CODE,
|
|
549
|
+
plan.mangle
|
|
550
|
+
? `${where}: ${keys} and are rewritten`
|
|
551
|
+
: `${where}: ${keys}; the save fails unless sanitizeKeys is "mangle"`,
|
|
552
|
+
);
|
|
557
553
|
}
|
|
558
554
|
}
|
|
559
555
|
|