@graphty/graph-io 0.3.24 → 0.3.26
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/README.md +49 -414
- package/dist/chunks/children-CL3Cy0ez.js.map +1 -1
- package/dist/chunks/choose-BbJI55dx.js +56 -0
- package/dist/chunks/choose-BbJI55dx.js.map +1 -0
- package/dist/chunks/constants-BCsFsMj4.js +231 -0
- package/dist/chunks/constants-BCsFsMj4.js.map +1 -0
- package/dist/chunks/{escape-B-tkk_Cl.js → escape-D0g6NNUy.js} +382 -92
- package/dist/chunks/escape-D0g6NNUy.js.map +1 -0
- package/dist/chunks/export-BTI4yQgy.js +4333 -0
- package/dist/chunks/export-BTI4yQgy.js.map +1 -0
- package/dist/chunks/exporter-BMqk5oxW.js +3465 -0
- package/dist/chunks/exporter-BMqk5oxW.js.map +1 -0
- package/dist/chunks/{importer-eUREhBGj.js → exporter-Cra2GYcN.js} +4969 -3201
- package/dist/chunks/exporter-Cra2GYcN.js.map +1 -0
- package/dist/chunks/{exporter-BGrsWamJ.js → exporter-Dxh231ZF.js} +38 -229
- package/dist/chunks/exporter-Dxh231ZF.js.map +1 -0
- package/dist/chunks/{exporter-DIeGJXAZ.js → exporter-tne475KK.js} +22 -20
- package/dist/chunks/exporter-tne475KK.js.map +1 -0
- package/dist/chunks/importer-90YWhJoF.js +2811 -0
- package/dist/chunks/importer-90YWhJoF.js.map +1 -0
- package/dist/chunks/{importer-D_e7e7LX.js → importer-Bcl1d4xH.js} +398 -145
- package/dist/chunks/importer-Bcl1d4xH.js.map +1 -0
- package/dist/chunks/{importer-BWY2FFc5.js → importer-BfIsp1f9.js} +255 -77
- package/dist/chunks/importer-BfIsp1f9.js.map +1 -0
- package/dist/chunks/{importer-zGLo8Dg_.js → importer-C_TEA1zJ.js} +484 -157
- package/dist/chunks/importer-C_TEA1zJ.js.map +1 -0
- package/dist/chunks/{importer-DET1tg6s.js → importer-DCjOvO7U.js} +1687 -310
- package/dist/chunks/importer-DCjOvO7U.js.map +1 -0
- package/dist/chunks/{importer-vi3bSdtw.js → importer-DfVu6j59.js} +509 -76
- package/dist/chunks/importer-DfVu6j59.js.map +1 -0
- package/dist/chunks/{importer-DIrbbnAf.js → importer-Dku_VAIY.js} +585 -226
- package/dist/chunks/importer-Dku_VAIY.js.map +1 -0
- package/dist/chunks/json-elements-3tnyt5uZ.js +1225 -0
- package/dist/chunks/json-elements-3tnyt5uZ.js.map +1 -0
- package/dist/chunks/ontology-export-B9Tcf37D.js +357 -0
- package/dist/chunks/ontology-export-B9Tcf37D.js.map +1 -0
- package/dist/chunks/{records-dbRkxwaq.js → records-CQXT__mY.js} +298 -114
- package/dist/chunks/records-CQXT__mY.js.map +1 -0
- package/dist/chunks/{text-Dr0Ifpag.js → text-DmcUs5_Q.js} +29 -11
- package/dist/chunks/text-DmcUs5_Q.js.map +1 -0
- package/dist/chunks/{writer-C7flM-Ih.js → writer-DxjgHwSe.js} +19 -1
- package/dist/chunks/writer-DxjgHwSe.js.map +1 -0
- package/dist/csv.js +951 -261
- package/dist/csv.js.map +1 -1
- package/dist/cx.js +6 -3
- package/dist/cx2.js +2 -2
- package/dist/cys.js +6 -3
- package/dist/dot.js +1 -1
- package/dist/gexf.js +554 -122
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +342 -85
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +1105 -213
- package/dist/graph-io.js.map +1 -1
- package/dist/graphml.js +3 -2
- package/dist/graphml.js.map +1 -1
- package/dist/json.js +3 -2
- package/dist/neo4j.js +564 -157
- package/dist/neo4j.js.map +1 -1
- package/dist/obo.js +6 -3
- package/dist/pajek.js +1 -1
- package/dist/src/children.d.ts +18 -6
- package/dist/src/children.d.ts.map +1 -1
- package/dist/src/children.js +6 -3
- package/dist/src/children.js.map +1 -1
- package/dist/src/common/attributes.d.ts +29 -11
- package/dist/src/common/attributes.d.ts.map +1 -1
- package/dist/src/common/attributes.js +25 -7
- package/dist/src/common/attributes.js.map +1 -1
- package/dist/src/common/cell-budget.d.ts +9 -2
- package/dist/src/common/cell-budget.d.ts.map +1 -1
- package/dist/src/common/cell-budget.js +11 -3
- package/dist/src/common/cell-budget.js.map +1 -1
- package/dist/src/common/choose.d.ts +19 -0
- package/dist/src/common/choose.d.ts.map +1 -0
- package/dist/src/common/choose.js +72 -0
- package/dist/src/common/choose.js.map +1 -0
- package/dist/src/common/codes.d.ts +456 -66
- package/dist/src/common/codes.d.ts.map +1 -1
- package/dist/src/common/codes.js +458 -66
- package/dist/src/common/codes.js.map +1 -1
- package/dist/src/common/compare.d.ts +81 -0
- package/dist/src/common/compare.d.ts.map +1 -0
- package/dist/src/common/compare.js +394 -0
- package/dist/src/common/compare.js.map +1 -0
- package/dist/src/common/cx-export.d.ts +123 -0
- package/dist/src/common/cx-export.d.ts.map +1 -0
- package/dist/src/common/cx-export.js +334 -0
- package/dist/src/common/cx-export.js.map +1 -0
- package/dist/src/common/declared-types.d.ts +22 -2
- package/dist/src/common/declared-types.d.ts.map +1 -1
- package/dist/src/common/declared-types.js +19 -1
- package/dist/src/common/declared-types.js.map +1 -1
- package/dist/src/common/direction.d.ts +74 -50
- package/dist/src/common/direction.d.ts.map +1 -1
- package/dist/src/common/direction.js +47 -27
- package/dist/src/common/direction.js.map +1 -1
- package/dist/src/common/escape.d.ts +21 -9
- package/dist/src/common/escape.d.ts.map +1 -1
- package/dist/src/common/escape.js +33 -17
- package/dist/src/common/escape.js.map +1 -1
- package/dist/src/common/export.d.ts +144 -32
- package/dist/src/common/export.d.ts.map +1 -1
- package/dist/src/common/export.js +216 -42
- package/dist/src/common/export.js.map +1 -1
- package/dist/src/common/format.d.ts +23 -1
- package/dist/src/common/format.d.ts.map +1 -1
- package/dist/src/common/format.js +26 -1
- package/dist/src/common/format.js.map +1 -1
- package/dist/src/common/ids.d.ts +9 -2
- package/dist/src/common/ids.d.ts.map +1 -1
- package/dist/src/common/ids.js +6 -2
- package/dist/src/common/ids.js.map +1 -1
- package/dist/src/common/input.d.ts +122 -28
- package/dist/src/common/input.d.ts.map +1 -1
- package/dist/src/common/input.js +890 -124
- package/dist/src/common/input.js.map +1 -1
- package/dist/src/common/json-elements.d.ts +144 -20
- package/dist/src/common/json-elements.d.ts.map +1 -1
- package/dist/src/common/json-elements.js +868 -188
- package/dist/src/common/json-elements.js.map +1 -1
- package/dist/src/common/line-format.d.ts +90 -0
- package/dist/src/common/line-format.d.ts.map +1 -0
- package/dist/src/common/line-format.js +185 -0
- package/dist/src/common/line-format.js.map +1 -0
- package/dist/src/common/lists.d.ts +6 -1
- package/dist/src/common/lists.d.ts.map +1 -1
- package/dist/src/common/lists.js +2 -0
- package/dist/src/common/lists.js.map +1 -1
- package/dist/src/common/ontology-export.d.ts +167 -0
- package/dist/src/common/ontology-export.d.ts.map +1 -0
- package/dist/src/common/ontology-export.js +359 -0
- package/dist/src/common/ontology-export.js.map +1 -0
- package/dist/src/common/ontology.d.ts +25 -3
- package/dist/src/common/ontology.d.ts.map +1 -1
- package/dist/src/common/ontology.js +30 -9
- package/dist/src/common/ontology.js.map +1 -1
- package/dist/src/common/options.d.ts +62 -12
- package/dist/src/common/options.d.ts.map +1 -1
- package/dist/src/common/options.js +89 -11
- package/dist/src/common/options.js.map +1 -1
- package/dist/src/common/plural.d.ts +31 -0
- package/dist/src/common/plural.d.ts.map +1 -0
- package/dist/src/common/plural.js +46 -0
- package/dist/src/common/plural.js.map +1 -0
- package/dist/src/common/report.d.ts +72 -14
- package/dist/src/common/report.d.ts.map +1 -1
- package/dist/src/common/report.js +146 -22
- package/dist/src/common/report.js.map +1 -1
- package/dist/src/common/temporal.d.ts +22 -5
- package/dist/src/common/temporal.d.ts.map +1 -1
- package/dist/src/common/temporal.js +13 -2
- package/dist/src/common/temporal.js.map +1 -1
- package/dist/src/common/text.d.ts +39 -23
- package/dist/src/common/text.d.ts.map +1 -1
- package/dist/src/common/text.js +51 -21
- package/dist/src/common/text.js.map +1 -1
- package/dist/src/common/weights.d.ts +37 -4
- package/dist/src/common/weights.d.ts.map +1 -1
- package/dist/src/common/weights.js +61 -2
- package/dist/src/common/weights.js.map +1 -1
- package/dist/src/common/writer.d.ts +19 -3
- package/dist/src/common/writer.d.ts.map +1 -1
- package/dist/src/common/writer.js +30 -2
- package/dist/src/common/writer.js.map +1 -1
- package/dist/src/common/xml.d.ts +125 -9
- package/dist/src/common/xml.d.ts.map +1 -1
- package/dist/src/common/xml.js +400 -83
- package/dist/src/common/xml.js.map +1 -1
- package/dist/src/common/zip.d.ts +44 -4
- package/dist/src/common/zip.d.ts.map +1 -1
- package/dist/src/common/zip.js +134 -5
- package/dist/src/common/zip.js.map +1 -1
- package/dist/src/formats/csv/exporter.d.ts +80 -24
- package/dist/src/formats/csv/exporter.d.ts.map +1 -1
- package/dist/src/formats/csv/exporter.js +145 -32
- package/dist/src/formats/csv/exporter.js.map +1 -1
- package/dist/src/formats/csv/header.d.ts +38 -9
- package/dist/src/formats/csv/header.d.ts.map +1 -1
- package/dist/src/formats/csv/header.js +46 -10
- package/dist/src/formats/csv/header.js.map +1 -1
- package/dist/src/formats/csv/importer.d.ts +187 -37
- package/dist/src/formats/csv/importer.d.ts.map +1 -1
- package/dist/src/formats/csv/importer.js +835 -204
- package/dist/src/formats/csv/importer.js.map +1 -1
- package/dist/src/formats/csv/index.d.ts +100 -19
- package/dist/src/formats/csv/index.d.ts.map +1 -1
- package/dist/src/formats/csv/index.js +94 -21
- package/dist/src/formats/csv/index.js.map +1 -1
- package/dist/src/formats/csv/records.d.ts +95 -10
- package/dist/src/formats/csv/records.d.ts.map +1 -1
- package/dist/src/formats/csv/records.js +333 -122
- package/dist/src/formats/csv/records.js.map +1 -1
- package/dist/src/formats/csv/values.d.ts +1 -0
- package/dist/src/formats/csv/values.d.ts.map +1 -1
- package/dist/src/formats/csv/values.js +1 -0
- package/dist/src/formats/csv/values.js.map +1 -1
- package/dist/src/formats/cx/exporter.d.ts +102 -0
- package/dist/src/formats/cx/exporter.d.ts.map +1 -0
- package/dist/src/formats/cx/exporter.js +1217 -0
- package/dist/src/formats/cx/exporter.js.map +1 -0
- package/dist/src/formats/cx/importer.d.ts +79 -32
- package/dist/src/formats/cx/importer.d.ts.map +1 -1
- package/dist/src/formats/cx/importer.js +680 -285
- package/dist/src/formats/cx/importer.js.map +1 -1
- package/dist/src/formats/cx/index.d.ts +4 -3
- package/dist/src/formats/cx/index.d.ts.map +1 -1
- package/dist/src/formats/cx/index.js +4 -3
- package/dist/src/formats/cx/index.js.map +1 -1
- package/dist/src/formats/cx2/exporter.d.ts +29 -11
- package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
- package/dist/src/formats/cx2/exporter.js +39 -268
- package/dist/src/formats/cx2/exporter.js.map +1 -1
- package/dist/src/formats/cx2/importer.d.ts +78 -28
- package/dist/src/formats/cx2/importer.d.ts.map +1 -1
- package/dist/src/formats/cx2/importer.js +226 -79
- package/dist/src/formats/cx2/importer.js.map +1 -1
- package/dist/src/formats/cx2/index.d.ts +2 -1
- package/dist/src/formats/cx2/index.d.ts.map +1 -1
- package/dist/src/formats/cx2/index.js +2 -1
- package/dist/src/formats/cx2/index.js.map +1 -1
- package/dist/src/formats/cys/constants.d.ts +66 -17
- package/dist/src/formats/cys/constants.d.ts.map +1 -1
- package/dist/src/formats/cys/constants.js +59 -18
- package/dist/src/formats/cys/constants.js.map +1 -1
- package/dist/src/formats/cys/exporter.d.ts +118 -0
- package/dist/src/formats/cys/exporter.d.ts.map +1 -0
- package/dist/src/formats/cys/exporter.js +1072 -0
- package/dist/src/formats/cys/exporter.js.map +1 -0
- package/dist/src/formats/cys/importer.d.ts +22 -8
- package/dist/src/formats/cys/importer.d.ts.map +1 -1
- package/dist/src/formats/cys/importer.js +510 -149
- package/dist/src/formats/cys/importer.js.map +1 -1
- package/dist/src/formats/cys/index.d.ts +4 -2
- package/dist/src/formats/cys/index.d.ts.map +1 -1
- package/dist/src/formats/cys/index.js +4 -2
- package/dist/src/formats/cys/index.js.map +1 -1
- package/dist/src/formats/cys/session.d.ts +46 -7
- package/dist/src/formats/cys/session.d.ts.map +1 -1
- package/dist/src/formats/cys/session.js +98 -24
- package/dist/src/formats/cys/session.js.map +1 -1
- package/dist/src/formats/cys/tables.d.ts +9 -2
- package/dist/src/formats/cys/tables.d.ts.map +1 -1
- package/dist/src/formats/cys/tables.js +29 -7
- package/dist/src/formats/cys/tables.js.map +1 -1
- package/dist/src/formats/dot/exporter.d.ts +42 -14
- package/dist/src/formats/dot/exporter.d.ts.map +1 -1
- package/dist/src/formats/dot/exporter.js +56 -29
- package/dist/src/formats/dot/exporter.js.map +1 -1
- package/dist/src/formats/dot/importer.d.ts +91 -29
- package/dist/src/formats/dot/importer.d.ts.map +1 -1
- package/dist/src/formats/dot/importer.js +190 -39
- package/dist/src/formats/dot/importer.js.map +1 -1
- package/dist/src/formats/dot/index.d.ts +2 -1
- package/dist/src/formats/dot/index.d.ts.map +1 -1
- package/dist/src/formats/dot/index.js +2 -1
- package/dist/src/formats/dot/index.js.map +1 -1
- package/dist/src/formats/dot/names.d.ts +44 -11
- package/dist/src/formats/dot/names.d.ts.map +1 -1
- package/dist/src/formats/dot/names.js +44 -11
- package/dist/src/formats/dot/names.js.map +1 -1
- package/dist/src/formats/dot/tokenizer.d.ts +9 -2
- package/dist/src/formats/dot/tokenizer.d.ts.map +1 -1
- package/dist/src/formats/dot/tokenizer.js +20 -5
- package/dist/src/formats/dot/tokenizer.js.map +1 -1
- package/dist/src/formats/gexf/exporter.d.ts +46 -15
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
- package/dist/src/formats/gexf/exporter.js +45 -15
- package/dist/src/formats/gexf/exporter.js.map +1 -1
- package/dist/src/formats/gexf/importer.d.ts +87 -23
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +471 -90
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gexf/index.d.ts +84 -21
- package/dist/src/formats/gexf/index.d.ts.map +1 -1
- package/dist/src/formats/gexf/index.js +80 -23
- package/dist/src/formats/gexf/index.js.map +1 -1
- package/dist/src/formats/gexf/schema.d.ts +67 -15
- package/dist/src/formats/gexf/schema.d.ts.map +1 -1
- package/dist/src/formats/gexf/schema.js +59 -13
- package/dist/src/formats/gexf/schema.js.map +1 -1
- package/dist/src/formats/gml/exporter.d.ts +69 -21
- package/dist/src/formats/gml/exporter.d.ts.map +1 -1
- package/dist/src/formats/gml/exporter.js +74 -29
- package/dist/src/formats/gml/exporter.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts +116 -25
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +290 -61
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/index.d.ts +87 -24
- package/dist/src/formats/gml/index.d.ts.map +1 -1
- package/dist/src/formats/gml/index.js +79 -26
- package/dist/src/formats/gml/index.js.map +1 -1
- package/dist/src/formats/gml/syntax.d.ts +67 -18
- package/dist/src/formats/gml/syntax.d.ts.map +1 -1
- package/dist/src/formats/gml/syntax.js +108 -26
- package/dist/src/formats/gml/syntax.js.map +1 -1
- package/dist/src/formats/graphml/constants.d.ts +174 -44
- package/dist/src/formats/graphml/constants.d.ts.map +1 -1
- package/dist/src/formats/graphml/constants.js +165 -44
- package/dist/src/formats/graphml/constants.js.map +1 -1
- package/dist/src/formats/graphml/exporter.d.ts +17 -9
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
- package/dist/src/formats/graphml/exporter.js +33 -12
- package/dist/src/formats/graphml/exporter.js.map +1 -1
- package/dist/src/formats/graphml/importer.d.ts +19 -7
- package/dist/src/formats/graphml/importer.d.ts.map +1 -1
- package/dist/src/formats/graphml/importer.js +466 -39
- package/dist/src/formats/graphml/importer.js.map +1 -1
- package/dist/src/formats/graphml/index.d.ts +2 -1
- package/dist/src/formats/graphml/index.d.ts.map +1 -1
- package/dist/src/formats/graphml/index.js +2 -1
- package/dist/src/formats/graphml/index.js.map +1 -1
- package/dist/src/formats/graphml/tree.d.ts +6 -2
- package/dist/src/formats/graphml/tree.d.ts.map +1 -1
- package/dist/src/formats/graphml/tree.js +6 -2
- package/dist/src/formats/graphml/tree.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts +8 -3
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +36 -15
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/dialect.d.ts +88 -24
- package/dist/src/formats/json/dialect.d.ts.map +1 -1
- package/dist/src/formats/json/dialect.js +109 -19
- package/dist/src/formats/json/dialect.js.map +1 -1
- package/dist/src/formats/json/exporter.d.ts +134 -22
- package/dist/src/formats/json/exporter.d.ts.map +1 -1
- package/dist/src/formats/json/exporter.js +135 -54
- package/dist/src/formats/json/exporter.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts +173 -52
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +899 -220
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/index.d.ts +3 -2
- package/dist/src/formats/json/index.d.ts.map +1 -1
- package/dist/src/formats/json/index.js +3 -2
- package/dist/src/formats/json/index.js.map +1 -1
- package/dist/src/formats/json/obographs-export.d.ts +68 -0
- package/dist/src/formats/json/obographs-export.d.ts.map +1 -0
- package/dist/src/formats/json/obographs-export.js +941 -0
- package/dist/src/formats/json/obographs-export.js.map +1 -0
- package/dist/src/formats/json/obographs.d.ts +26 -0
- package/dist/src/formats/json/obographs.d.ts.map +1 -1
- package/dist/src/formats/json/obographs.js +297 -80
- package/dist/src/formats/json/obographs.js.map +1 -1
- package/dist/src/formats/neo4j/exporter.d.ts +109 -21
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
- package/dist/src/formats/neo4j/exporter.js +238 -85
- package/dist/src/formats/neo4j/exporter.js.map +1 -1
- package/dist/src/formats/neo4j/header.d.ts +17 -4
- package/dist/src/formats/neo4j/header.d.ts.map +1 -1
- package/dist/src/formats/neo4j/header.js +36 -4
- package/dist/src/formats/neo4j/header.js.map +1 -1
- package/dist/src/formats/neo4j/importer.d.ts +107 -27
- package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
- package/dist/src/formats/neo4j/importer.js +415 -63
- package/dist/src/formats/neo4j/importer.js.map +1 -1
- package/dist/src/formats/neo4j/index.d.ts +73 -23
- package/dist/src/formats/neo4j/index.d.ts.map +1 -1
- package/dist/src/formats/neo4j/index.js +65 -25
- package/dist/src/formats/neo4j/index.js.map +1 -1
- package/dist/src/formats/obo/exporter.d.ts +150 -0
- package/dist/src/formats/obo/exporter.d.ts.map +1 -0
- package/dist/src/formats/obo/exporter.js +1736 -0
- package/dist/src/formats/obo/exporter.js.map +1 -0
- package/dist/src/formats/obo/importer.d.ts +95 -19
- package/dist/src/formats/obo/importer.d.ts.map +1 -1
- package/dist/src/formats/obo/importer.js +475 -104
- package/dist/src/formats/obo/importer.js.map +1 -1
- package/dist/src/formats/obo/index.d.ts +6 -3
- package/dist/src/formats/obo/index.d.ts.map +1 -1
- package/dist/src/formats/obo/index.js +6 -3
- package/dist/src/formats/obo/index.js.map +1 -1
- package/dist/src/formats/obo/syntax.d.ts +108 -4
- package/dist/src/formats/obo/syntax.d.ts.map +1 -1
- package/dist/src/formats/obo/syntax.js +211 -34
- package/dist/src/formats/obo/syntax.js.map +1 -1
- package/dist/src/formats/pajek/exporter.d.ts +54 -16
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +66 -34
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts +84 -22
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +303 -83
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/index.d.ts +2 -1
- package/dist/src/formats/pajek/index.d.ts.map +1 -1
- package/dist/src/formats/pajek/index.js +2 -1
- package/dist/src/formats/pajek/index.js.map +1 -1
- package/dist/src/formats/pajek/syntax.d.ts +77 -14
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +183 -51
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/columns.d.ts +1 -0
- package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
- package/dist/src/formats/xgmml/columns.js +4 -0
- package/dist/src/formats/xgmml/columns.js.map +1 -1
- package/dist/src/formats/xgmml/constants.d.ts +186 -56
- package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
- package/dist/src/formats/xgmml/constants.js +181 -57
- package/dist/src/formats/xgmml/constants.js.map +1 -1
- package/dist/src/formats/xgmml/document.d.ts +65 -11
- package/dist/src/formats/xgmml/document.d.ts.map +1 -1
- package/dist/src/formats/xgmml/document.js +157 -28
- package/dist/src/formats/xgmml/document.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts +61 -3
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +129 -26
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/exporter.d.ts +13 -6
- package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
- package/dist/src/formats/xgmml/exporter.js +20 -21
- package/dist/src/formats/xgmml/exporter.js.map +1 -1
- package/dist/src/formats/xgmml/importer.d.ts +33 -12
- package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
- package/dist/src/formats/xgmml/importer.js +35 -72
- package/dist/src/formats/xgmml/importer.js.map +1 -1
- package/dist/src/formats/xgmml/index.d.ts +1 -0
- package/dist/src/formats/xgmml/index.d.ts.map +1 -1
- package/dist/src/formats/xgmml/index.js +1 -0
- package/dist/src/formats/xgmml/index.js.map +1 -1
- package/dist/src/formats/xgmml/values.d.ts +8 -1
- package/dist/src/formats/xgmml/values.d.ts.map +1 -1
- package/dist/src/formats/xgmml/values.js +7 -2
- package/dist/src/formats/xgmml/values.js.map +1 -1
- package/dist/src/index.d.ts +23 -18
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +28 -16
- package/dist/src/index.js.map +1 -1
- package/dist/src/registry.d.ts +478 -92
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +807 -88
- package/dist/src/registry.js.map +1 -1
- package/dist/src/sniff.d.ts +61 -19
- package/dist/src/sniff.d.ts.map +1 -1
- package/dist/src/sniff.js +121 -29
- package/dist/src/sniff.js.map +1 -1
- package/dist/src/types.d.ts +362 -112
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +28 -7
- package/dist/src/types.js.map +1 -1
- package/dist/xgmml.js +2 -2
- package/docs/samples/README.md +25 -0
- package/docs/samples/got-edges.csv +353 -0
- package/docs/samples/got-network.graphml +1384 -0
- package/docs/samples/got-nodes.csv +108 -0
- package/docs/samples/got.cx +930 -0
- package/docs/samples/got.cx2 +465 -0
- package/docs/samples/got.gexf +471 -0
- package/docs/samples/got.gml +2650 -0
- package/docs/samples/got.json +2546 -0
- package/docs/samples/got.net +461 -0
- package/docs/samples/movies-nodes.csv +9 -0
- package/docs/samples/movies-rels.csv +8 -0
- package/docs/samples/networks.cys +0 -0
- package/docs/samples/proteins.xgmml +9 -0
- package/docs/samples/teams.gv +24 -0
- package/docs/samples/vehicles.obo +25 -0
- package/package.json +9 -4
- package/src/children.ts +18 -6
- package/src/common/attributes.ts +29 -11
- package/src/common/cell-budget.ts +11 -3
- package/src/common/choose.ts +89 -0
- package/src/common/codes.ts +490 -66
- package/src/common/compare.ts +472 -0
- package/src/common/cx-export.ts +432 -0
- package/src/common/declared-types.ts +38 -4
- package/src/common/direction.ts +78 -51
- package/src/common/escape.ts +33 -17
- package/src/common/export.ts +284 -47
- package/src/common/format.ts +30 -1
- package/src/common/ids.ts +14 -3
- package/src/common/input.ts +1057 -134
- package/src/common/json-elements.ts +1034 -198
- package/src/common/line-format.ts +249 -0
- package/src/common/lists.ts +6 -1
- package/src/common/ontology-export.ts +461 -0
- package/src/common/ontology.ts +31 -9
- package/src/common/options.ts +110 -15
- package/src/common/plural.ts +49 -0
- package/src/common/report.ts +168 -25
- package/src/common/temporal.ts +22 -5
- package/src/common/text.ts +62 -23
- package/src/common/weights.ts +74 -4
- package/src/common/writer.ts +37 -3
- package/src/common/xml.ts +477 -88
- package/src/common/zip.ts +169 -7
- package/src/formats/csv/exporter.ts +201 -46
- package/src/formats/csv/header.ts +50 -11
- package/src/formats/csv/importer.ts +1133 -245
- package/src/formats/csv/index.ts +106 -19
- package/src/formats/csv/records.ts +396 -122
- package/src/formats/csv/values.ts +1 -0
- package/src/formats/cx/exporter.ts +1483 -0
- package/src/formats/cx/importer.ts +888 -300
- package/src/formats/cx/index.ts +4 -3
- package/src/formats/cx2/exporter.ts +61 -338
- package/src/formats/cx2/importer.ts +317 -86
- package/src/formats/cx2/index.ts +2 -1
- package/src/formats/cys/constants.ts +62 -17
- package/src/formats/cys/exporter.ts +1368 -0
- package/src/formats/cys/importer.ts +659 -157
- package/src/formats/cys/index.ts +4 -2
- package/src/formats/cys/session.ts +139 -31
- package/src/formats/cys/tables.ts +47 -9
- package/src/formats/dot/exporter.ts +83 -37
- package/src/formats/dot/importer.ts +248 -54
- package/src/formats/dot/index.ts +2 -1
- package/src/formats/dot/names.ts +44 -11
- package/src/formats/dot/tokenizer.ts +25 -6
- package/src/formats/gexf/exporter.ts +57 -18
- package/src/formats/gexf/importer.ts +636 -105
- package/src/formats/gexf/index.ts +90 -21
- package/src/formats/gexf/schema.ts +67 -15
- package/src/formats/gml/exporter.ts +90 -36
- package/src/formats/gml/importer.ts +388 -67
- package/src/formats/gml/index.ts +85 -24
- package/src/formats/gml/syntax.ts +111 -26
- package/src/formats/graphml/constants.ts +178 -44
- package/src/formats/graphml/exporter.ts +52 -18
- package/src/formats/graphml/importer.ts +640 -60
- package/src/formats/graphml/index.ts +2 -1
- package/src/formats/graphml/tree.ts +6 -2
- package/src/formats/graphml/yfiles.ts +37 -15
- package/src/formats/json/dialect.ts +128 -29
- package/src/formats/json/exporter.ts +205 -68
- package/src/formats/json/importer.ts +1163 -253
- package/src/formats/json/index.ts +3 -1
- package/src/formats/json/obographs-export.ts +1197 -0
- package/src/formats/json/obographs.ts +353 -92
- package/src/formats/neo4j/exporter.ts +320 -101
- package/src/formats/neo4j/header.ts +44 -6
- package/src/formats/neo4j/importer.ts +512 -73
- package/src/formats/neo4j/index.ts +75 -23
- package/src/formats/obo/exporter.ts +2210 -0
- package/src/formats/obo/importer.ts +622 -109
- package/src/formats/obo/index.ts +6 -3
- package/src/formats/obo/syntax.ts +232 -37
- package/src/formats/pajek/exporter.ts +89 -41
- package/src/formats/pajek/importer.ts +407 -93
- package/src/formats/pajek/index.ts +2 -1
- package/src/formats/pajek/syntax.ts +213 -52
- package/src/formats/xgmml/columns.ts +10 -0
- package/src/formats/xgmml/constants.ts +183 -56
- package/src/formats/xgmml/document.ts +212 -34
- package/src/formats/xgmml/emit.ts +175 -34
- package/src/formats/xgmml/exporter.ts +35 -27
- package/src/formats/xgmml/importer.ts +64 -86
- package/src/formats/xgmml/index.ts +1 -0
- package/src/formats/xgmml/values.ts +11 -3
- package/src/index.ts +81 -11
- package/src/registry.ts +1121 -135
- package/src/sniff.ts +163 -32
- package/src/types.ts +385 -114
- package/dist/chunks/escape-B-tkk_Cl.js.map +0 -1
- package/dist/chunks/export-Bh60Dv-n.js +0 -582
- package/dist/chunks/export-Bh60Dv-n.js.map +0 -1
- package/dist/chunks/exporter-BGrsWamJ.js.map +0 -1
- package/dist/chunks/exporter-DIeGJXAZ.js.map +0 -1
- package/dist/chunks/importer-BWY2FFc5.js.map +0 -1
- package/dist/chunks/importer-DD-xv9_Y.js +0 -1471
- package/dist/chunks/importer-DD-xv9_Y.js.map +0 -1
- package/dist/chunks/importer-DET1tg6s.js.map +0 -1
- package/dist/chunks/importer-DEcKhsmQ.js +0 -1614
- package/dist/chunks/importer-DEcKhsmQ.js.map +0 -1
- package/dist/chunks/importer-DIrbbnAf.js.map +0 -1
- package/dist/chunks/importer-D_e7e7LX.js.map +0 -1
- package/dist/chunks/importer-eUREhBGj.js.map +0 -1
- package/dist/chunks/importer-vi3bSdtw.js.map +0 -1
- package/dist/chunks/importer-zGLo8Dg_.js.map +0 -1
- package/dist/chunks/json-elements-DDS8N2Dd.js +0 -779
- package/dist/chunks/json-elements-DDS8N2Dd.js.map +0 -1
- package/dist/chunks/ontology-BnrJ4I98.js +0 -113
- package/dist/chunks/ontology-BnrJ4I98.js.map +0 -1
- package/dist/chunks/records-dbRkxwaq.js.map +0 -1
- package/dist/chunks/report-B1z4WT9e.js +0 -2372
- package/dist/chunks/report-B1z4WT9e.js.map +0 -1
- package/dist/chunks/text-Dr0Ifpag.js.map +0 -1
- package/dist/chunks/weights-Dzba96G3.js +0 -176
- package/dist/chunks/weights-Dzba96G3.js.map +0 -1
- package/dist/chunks/writer-C7flM-Ih.js.map +0 -1
|
@@ -22,40 +22,47 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { INVALID_INDEX, } from "@graphty/graph-format";
|
|
24
24
|
import { declareResolved, uniqueColumnName } from "../../common/attributes.js";
|
|
25
|
-
import { AMBIGUOUS_GRAPH_NAME_CODE, ASPECT_ORDER_CODE, BAD_ASPECT_BLOCK_CODE, BAD_VALUE_CODE, COLUMN_RENAMED_CODE, COUNT_MISMATCH_CODE, DANGLING_REFERENCE_CODE, DIRECTION_FORCED_CODE, DIRECTION_REFUSED_CODE, DUPLICATE_ATTRIBUTE_CODE, DUPLICATE_EDGE_ID_CODE, DUPLICATE_NODE_CODE, EMPTY_INPUT_CODE, ENCODING_FALLBACK_CODE, GRAPH_NOT_FOUND_CODE, ID_MERGED_CODE, ID_TEXT_TYPE_CODE, INVALID_ENCODING_CODE, INVALID_UTF8_CODE, MISSING_ENDPOINT_CODE, MISSING_ID_CODE, MULTIPLE_GRAPHS_CODE, NO_GRAPH_CODE, OPTION_IGNORED_CODE, PARENT_CYCLE_CODE, PRECISION_CODE, ROLE_TAKEN_CODE, SINK_OPTION_CODE, STATUS_FAILED_CODE, STATUS_WARNING_CODE, STYLES_NOT_IMPORTED_CODE, SYNTAX_CODE, TOO_LARGE_CODE, UNKNOWN_ATTR_TYPE_CODE, UNKNOWN_ENCODING_CODE, UNKNOWN_PARENT_CODE, WIDENED_CODE, } from "../../common/codes.js";
|
|
25
|
+
import { AMBIGUOUS_GRAPH_NAME_CODE, ASPECT_ORDER_CODE, BAD_ASPECT_BLOCK_CODE, BAD_VALUE_CODE, COLUMN_RENAMED_CODE, COUNT_MISMATCH_CODE, DANGLING_REFERENCE_CODE, DIRECTION_FORCED_CODE, DIRECTION_REFUSED_CODE, DUPLICATE_ATTRIBUTE_CODE, DUPLICATE_EDGE_ID_CODE, DUPLICATE_NODE_CODE, ELEMENT_ISSUE, EMPTY_INPUT_CODE, ENCODING_FALLBACK_CODE, GRAPH_NOT_FOUND_CODE, ID_MERGED_CODE, ID_TEXT_TYPE_CODE, INPUT_ISSUE, INVALID_ENCODING_CODE, INVALID_UTF8_CODE, JSON_NONSTANDARD_NUMBER_CODE, MISSING_ENDPOINT_CODE, MISSING_ID_CODE, MULTI_ASPECT_FRAGMENT_CODE, MULTIPLE_GRAPHS_CODE, NO_GRAPH_CODE, OPTION_IGNORED_CODE, PARENT_CYCLE_CODE, PRECISION_CODE, ROLE_TAKEN_CODE, SINGLE_OBJECT_ASPECT_CODE, SINK_OPTION_CODE, STATUS_FAILED_CODE, STATUS_WARNING_CODE, STYLES_NOT_IMPORTED_CODE, SYNTAX_CODE, TOO_LARGE_CODE, UNKNOWN_ATTR_TYPE_CODE, UNKNOWN_ELEMENT_CODE, UNKNOWN_ENCODING_CODE, UNKNOWN_PARENT_CODE, WIDENED_CODE, } from "../../common/codes.js";
|
|
26
26
|
import { DirectionResolver } from "../../common/direction.js";
|
|
27
27
|
import { IdCoercer } from "../../common/ids.js";
|
|
28
28
|
import { textChunks, throwIfAborted } from "../../common/input.js";
|
|
29
|
-
import { cxId, CxStructure, declareFresh, ExactInteger, flipY, inexactLiteral, isRecord, JsonScanError, plainJson, positionDecl, reportTooDeep, scanAspects, zDecl, } from "../../common/json-elements.js";
|
|
30
|
-
import { chooseGraph, reportSinkOptions, reportUnusedOptions, resolveImportOptions, } from "../../common/options.js";
|
|
29
|
+
import { cxId, CxStructure, declareFresh, ExactInteger, fitsF32, flipY, headText, inexactLiteral, isRecord, JsonScanError, keptPrecision, plainJson, positionDecl, reportSharedBlock, reportTooDeep, scanAspects, zDecl, } from "../../common/json-elements.js";
|
|
30
|
+
import { chooseGraph, graphChosen, reportSinkOptions, reportUnusedOptions, resolveImportOptions, } from "../../common/options.js";
|
|
31
|
+
import { agree, plural } from "../../common/plural.js";
|
|
31
32
|
import { ImportReportBuilder } from "../../common/report.js";
|
|
32
33
|
import { weightFromValue } from "../../common/weights.js";
|
|
33
|
-
import { zAsOption } from "../cx2/importer.js";
|
|
34
|
+
import { ORIGINAL_ID_ATTRIBUTE, zAsOption } from "../cx2/importer.js";
|
|
34
35
|
/** The format name. */
|
|
35
36
|
const CX_FORMAT = "cx";
|
|
36
37
|
/** The origin namespace of the per-element visual property columns. */
|
|
37
38
|
const CX_BYPASS_NAMESPACE = "cx.bypass";
|
|
38
39
|
/**
|
|
39
|
-
* The issue codes the CX importer records
|
|
40
|
-
* the other importers
|
|
40
|
+
* The issue codes the CX importer records, by name: the codes shared with
|
|
41
|
+
* the other importers and the CX-specific ones. A key is the code without
|
|
41
42
|
* its severity and format prefixes.
|
|
43
|
+
* @category Built-in formats
|
|
42
44
|
*/
|
|
43
45
|
export const CX_ISSUE = Object.freeze({
|
|
44
|
-
|
|
46
|
+
...INPUT_ISSUE,
|
|
47
|
+
...ELEMENT_ISSUE,
|
|
48
|
+
/** The input is empty. The import stops. */
|
|
45
49
|
EMPTY_INPUT: EMPTY_INPUT_CODE,
|
|
46
|
-
/** The text is not JSON
|
|
50
|
+
/** The text is not valid JSON. The import stops. */
|
|
47
51
|
SYNTAX: SYNTAX_CODE,
|
|
48
|
-
/**
|
|
52
|
+
/** The input is not valid UTF-8. The import stops. */
|
|
49
53
|
INVALID_UTF8: INVALID_UTF8_CODE,
|
|
50
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Some bytes are not valid in the encoding that was chosen (by a byte order mark or the `encoding` option). The
|
|
56
|
+
* import stops.
|
|
57
|
+
*/
|
|
51
58
|
INVALID_ENCODING: INVALID_ENCODING_CODE,
|
|
52
59
|
/** Bytes that are not UTF-8 were read as windows-1252. */
|
|
53
60
|
ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,
|
|
54
61
|
/** An encoding the platform cannot decode was ignored. */
|
|
55
62
|
UNKNOWN_ENCODING: UNKNOWN_ENCODING_CODE,
|
|
56
|
-
/** The document is not a CX array, or it is CX2 (
|
|
63
|
+
/** The document is not a CX array, or it is CX2 (the message says so; read it as `cx2`). The import stops. */
|
|
57
64
|
NOT_CX: "E_CX_NOT_CX",
|
|
58
|
-
/**
|
|
65
|
+
/** NumberVerification holds another value than 2^48 - 1, or comes twice. */
|
|
59
66
|
NUMBER_VERIFICATION: "W_CX_NUMBER_VERIFICATION",
|
|
60
67
|
/** An old Cytoscape aspect name (visualProperties, subNetworks, ...) read under its cy name. */
|
|
61
68
|
OLD_ASPECT_NAME: "W_CX_OLD_ASPECT_NAME",
|
|
@@ -63,11 +70,14 @@ export const CX_ISSUE = Object.freeze({
|
|
|
63
70
|
GROUP_NODE_ADDED: "W_CX_GROUP_NODE_ADDED",
|
|
64
71
|
/** Nodes or edges of the root network that no subnetwork holds: Cytoscape shows them in no network; not read. */
|
|
65
72
|
ROOT_ONLY: "W_CX_ROOT_ONLY",
|
|
66
|
-
/** The
|
|
73
|
+
/** The program that wrote the file marked it as failed, so it is incomplete. The import stops. */
|
|
67
74
|
STATUS_FAILED: STATUS_FAILED_CODE,
|
|
68
|
-
/** The
|
|
75
|
+
/** The program that wrote the file marked it as successful but added an error message; the message is shown. */
|
|
69
76
|
STATUS_WARNING: STATUS_WARNING_CODE,
|
|
70
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* A member of the file's top-level array that is not a block with one key (`{"nodes": [...]}`), or an element of a
|
|
79
|
+
* block that is not an object. It is skipped; the rest of the file is read.
|
|
80
|
+
*/
|
|
71
81
|
BAD_ASPECT_BLOCK: BAD_ASPECT_BLOCK_CODE,
|
|
72
82
|
/** An aspect after the post-metadata or after the status, a third metaData. */
|
|
73
83
|
ASPECT_ORDER: ASPECT_ORDER_CODE,
|
|
@@ -75,7 +85,10 @@ export const CX_ISSUE = Object.freeze({
|
|
|
75
85
|
COUNT_MISMATCH: COUNT_MISMATCH_CODE,
|
|
76
86
|
/** A value that does not parse as its data type; the cell is unset. */
|
|
77
87
|
BAD_VALUE: BAD_VALUE_CODE,
|
|
78
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* An attribute's type was widened because a later value did not fit: an integer above 2^31 in an integer column,
|
|
90
|
+
* or two declared types for one attribute.
|
|
91
|
+
*/
|
|
79
92
|
WIDENED: WIDENED_CODE,
|
|
80
93
|
/** A data type CX does not define; the value is kept as text. */
|
|
81
94
|
UNKNOWN_ATTR_TYPE: UNKNOWN_ATTR_TYPE_CODE,
|
|
@@ -87,9 +100,9 @@ export const CX_ISSUE = Object.freeze({
|
|
|
87
100
|
UNKNOWN_PARENT: UNKNOWN_PARENT_CODE,
|
|
88
101
|
/** A group membership that would close a parent cycle; dropped. */
|
|
89
102
|
PARENT_CYCLE: PARENT_CYCLE_CODE,
|
|
90
|
-
/** The style rules of cyVisualProperties are not applied
|
|
103
|
+
/** The style rules of cyVisualProperties are not applied; they are kept so a CX export writes them back. */
|
|
91
104
|
STYLES_NOT_IMPORTED: STYLES_NOT_IMPORTED_CODE,
|
|
92
|
-
/** A node without an
|
|
105
|
+
/** A node without an id (its `@id` key). The node is skipped. */
|
|
93
106
|
MISSING_ID: MISSING_ID_CODE,
|
|
94
107
|
/** An edge without s or t. */
|
|
95
108
|
MISSING_ENDPOINT: MISSING_ENDPOINT_CODE,
|
|
@@ -99,37 +112,63 @@ export const CX_ISSUE = Object.freeze({
|
|
|
99
112
|
DUPLICATE_EDGE_ID: DUPLICATE_EDGE_ID_CODE,
|
|
100
113
|
/** An id that is not an integer. */
|
|
101
114
|
INVALID_ID: "E_INVALID_ID",
|
|
115
|
+
/**
|
|
116
|
+
* A node or edge key CX does not define, an aspect graph-io keeps for writing back, or a table CX does not define;
|
|
117
|
+
* it is skipped.
|
|
118
|
+
*/
|
|
119
|
+
UNKNOWN_ELEMENT: UNKNOWN_ELEMENT_CODE,
|
|
120
|
+
/** A member holding several aspects; each array-valued key is read as its own fragment. */
|
|
121
|
+
MULTI_ASPECT_FRAGMENT: MULTI_ASPECT_FRAGMENT_CODE,
|
|
122
|
+
/** An aspect written as one object, not an array of elements; read as one element. */
|
|
123
|
+
SINGLE_OBJECT_ASPECT: SINGLE_OBJECT_ASPECT_CODE,
|
|
124
|
+
/** The bare tokens NaN / Infinity / -Infinity (Python's json writes them), read as numbers. */
|
|
125
|
+
JSON_NONSTANDARD_NUMBER: JSON_NONSTANDARD_NUMBER_CODE,
|
|
102
126
|
/** An edge endpoint naming no node of the graph (addMissingNodes false, the default). */
|
|
103
127
|
UNKNOWN_NODE: "E_UNKNOWN_NODE",
|
|
104
128
|
/** A weight that is not a number. */
|
|
105
129
|
INVALID_WEIGHT: "E_INVALID_WEIGHT",
|
|
106
130
|
/** An id spelled as a string or a non-integer literal; read as the integer. */
|
|
107
131
|
ID_TEXT_TYPE: ID_TEXT_TYPE_CODE,
|
|
108
|
-
/** An integer beyond 2^53
|
|
132
|
+
/** An integer beyond 2^53 was stored as the nearest 64-bit float; pass `long: "string"` to keep every digit. */
|
|
109
133
|
PRECISION: PRECISION_CODE,
|
|
110
134
|
/** Two id texts merged under `ids: "number"`. */
|
|
111
135
|
ID_MERGED: ID_MERGED_CODE,
|
|
112
|
-
/**
|
|
136
|
+
/**
|
|
137
|
+
* The file holds several graphs and only the first was read. It is not added when `graphIndex` or `graphName` chose the graph.
|
|
138
|
+
* `importAllGraphs()` reads every one.
|
|
139
|
+
*/
|
|
113
140
|
MULTIPLE_GRAPHS: MULTIPLE_GRAPHS_CODE,
|
|
114
|
-
/** graphIndex or graphName
|
|
141
|
+
/** `graphIndex` or `graphName` matches no subnetwork. The import stops. */
|
|
115
142
|
GRAPH_NOT_FOUND: GRAPH_NOT_FOUND_CODE,
|
|
116
|
-
/** graphName
|
|
143
|
+
/** `graphName` matches several subnetworks. The import stops. */
|
|
117
144
|
AMBIGUOUS_GRAPH_NAME: AMBIGUOUS_GRAPH_NAME_CODE,
|
|
118
|
-
/** The input holds no graph
|
|
145
|
+
/** The input holds no graph. The import stops. */
|
|
119
146
|
NO_GRAPH: NO_GRAPH_CODE,
|
|
120
|
-
/**
|
|
147
|
+
/**
|
|
148
|
+
* An attribute was renamed `<name>#<suffix>` because another attribute already has its name, for example
|
|
149
|
+
* two attributes declared with the same name.
|
|
150
|
+
*/
|
|
121
151
|
COLUMN_RENAMED: COLUMN_RENAMED_CODE,
|
|
122
|
-
/**
|
|
152
|
+
/**
|
|
153
|
+
* You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as
|
|
154
|
+
* a plain attribute.
|
|
155
|
+
*/
|
|
123
156
|
ROLE_TAKEN: ROLE_TAKEN_CODE,
|
|
124
|
-
/**
|
|
157
|
+
/**
|
|
158
|
+
* You read into a graph builder whose direction is already set, or which already holds edges, so the file is read
|
|
159
|
+
* with the builder's direction instead of its own.
|
|
160
|
+
*/
|
|
125
161
|
DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,
|
|
126
|
-
/** Edges
|
|
162
|
+
/** Edges of the other direction were read with the direction `onMixedDirection` chose. */
|
|
127
163
|
DIRECTION_FORCED: DIRECTION_FORCED_CODE,
|
|
128
|
-
/**
|
|
164
|
+
/** You set an option this format does not use; it had no effect. The message names the option. */
|
|
129
165
|
OPTION_IGNORED: OPTION_IGNORED_CODE,
|
|
130
|
-
/**
|
|
166
|
+
/**
|
|
167
|
+
* You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,
|
|
168
|
+
* `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.
|
|
169
|
+
*/
|
|
131
170
|
SINK_OPTION: SINK_OPTION_CODE,
|
|
132
|
-
/** The input is
|
|
171
|
+
/** The input is larger than graph-io's size limit. The import stops. */
|
|
133
172
|
TOO_LARGE: TOO_LARGE_CODE,
|
|
134
173
|
});
|
|
135
174
|
const USED_OPTIONS = new Set([
|
|
@@ -141,6 +180,7 @@ const USED_OPTIONS = new Set([
|
|
|
141
180
|
"weightFrom",
|
|
142
181
|
"weightDtype",
|
|
143
182
|
"long",
|
|
183
|
+
"restoreMangledIds",
|
|
144
184
|
"errorLimit",
|
|
145
185
|
"signal",
|
|
146
186
|
"onProgress",
|
|
@@ -156,12 +196,18 @@ const ABORT_CHECK_INTERVAL = 64;
|
|
|
156
196
|
/** The numberVerification values a reader accepts: 2^48 - 1 and Java's Long.MAX_VALUE. */
|
|
157
197
|
const NUMBER_VERIFICATION_VALUES = new Set(["281474976710655", "9223372036854775807"]);
|
|
158
198
|
/** Old Cytoscape aspect names and the names they are read under. */
|
|
159
|
-
const OLD_ASPECT_NAMES =
|
|
160
|
-
visualProperties
|
|
161
|
-
subNetworks
|
|
162
|
-
networkRelations
|
|
163
|
-
hiddenAttributes
|
|
164
|
-
|
|
199
|
+
const OLD_ASPECT_NAMES = new Map([
|
|
200
|
+
["visualProperties", "cyVisualProperties"],
|
|
201
|
+
["subNetworks", "cySubNetworks"],
|
|
202
|
+
["networkRelations", "cyNetworkRelations"],
|
|
203
|
+
["hiddenAttributes", "cyHiddenAttributes"],
|
|
204
|
+
]);
|
|
205
|
+
/** The keys a nodes element defines. */
|
|
206
|
+
const NODE_KEYS = new Set(["@id", "n", "r"]);
|
|
207
|
+
/** The keys an edges element defines. */
|
|
208
|
+
const EDGE_KEYS = new Set(["@id", "s", "t", "i"]);
|
|
209
|
+
/** The tables cyTableColumn applies to. */
|
|
210
|
+
const TABLES = new Set(["node_table", "edge_table", "network_table"]);
|
|
165
211
|
/** The aspects the importer reads into the graph (everything else is kept verbatim). */
|
|
166
212
|
const READ_ASPECTS = new Set([
|
|
167
213
|
"nodes",
|
|
@@ -200,6 +246,8 @@ const ALSO_KEPT = new Set([
|
|
|
200
246
|
]);
|
|
201
247
|
/** Cytoscape's table-cell style aspects (both spellings): style rules, kept and reported, never read. */
|
|
202
248
|
const TABLE_STYLE_ASPECTS = ["tableVisualProperties", "cyTableVisualProperties"];
|
|
249
|
+
/** The importer's own entries of meta.extra.cx, which an aspect of the same name would collide with. */
|
|
250
|
+
const OWN_EXTRA_KEYS = ["groups", "subnetwork"];
|
|
203
251
|
/** The first keys that make a head CX for sure, and the other aspect names a CX document may start with. */
|
|
204
252
|
const CX_FIRST_KEYS = ["numberVerification", "metaData"];
|
|
205
253
|
const CX_ASPECT_KEYS = [
|
|
@@ -228,14 +276,14 @@ const CX_ASPECT_KEYS = [
|
|
|
228
276
|
"functionTerms",
|
|
229
277
|
];
|
|
230
278
|
/** The CX data types, with the Java reader's float aliases. */
|
|
231
|
-
const SCALARS =
|
|
232
|
-
string
|
|
233
|
-
boolean
|
|
234
|
-
integer
|
|
235
|
-
long
|
|
236
|
-
double
|
|
237
|
-
float
|
|
238
|
-
|
|
279
|
+
const SCALARS = new Map([
|
|
280
|
+
["string", "string"],
|
|
281
|
+
["boolean", "boolean"],
|
|
282
|
+
["integer", "integer"],
|
|
283
|
+
["long", "long"],
|
|
284
|
+
["double", "double"],
|
|
285
|
+
["float", "double"],
|
|
286
|
+
]);
|
|
239
287
|
/**
|
|
240
288
|
* Resolve a `d` value.
|
|
241
289
|
* @param d - the data type (undefined: string)
|
|
@@ -249,7 +297,7 @@ function cxType(d) {
|
|
|
249
297
|
return null;
|
|
250
298
|
}
|
|
251
299
|
const list = d.startsWith("list_of_");
|
|
252
|
-
const scalar = SCALARS
|
|
300
|
+
const scalar = SCALARS.get(list ? d.slice("list_of_".length) : d);
|
|
253
301
|
return scalar === undefined ? null : { scalar, list };
|
|
254
302
|
}
|
|
255
303
|
/**
|
|
@@ -367,7 +415,15 @@ function parseScalar(value, scalar, long, onPrecision) {
|
|
|
367
415
|
if (value === "Infinity" || value === "-Infinity") {
|
|
368
416
|
return Number(value);
|
|
369
417
|
}
|
|
370
|
-
|
|
418
|
+
if (!DECIMAL_TEXT.test(value)) {
|
|
419
|
+
return BAD;
|
|
420
|
+
}
|
|
421
|
+
const n = Number(value);
|
|
422
|
+
if (!Number.isFinite(n)) {
|
|
423
|
+
// beyond the range of a double: Java's parseDouble gives Infinity too, but say so
|
|
424
|
+
onPrecision(value);
|
|
425
|
+
}
|
|
426
|
+
return n;
|
|
371
427
|
}
|
|
372
428
|
}
|
|
373
429
|
}
|
|
@@ -492,15 +548,16 @@ function aspect(doc, name) {
|
|
|
492
548
|
* The inexact-literal bits of an element.
|
|
493
549
|
* @param text - the element's JSON text
|
|
494
550
|
* @param keys - the id keys to check, bit 1, 2, 4 in order
|
|
551
|
+
* @param depth - the depth of the element's own keys in the text (2: a member's single object)
|
|
495
552
|
* @returns the bits
|
|
496
553
|
*/
|
|
497
|
-
function inexactBits(text, keys) {
|
|
554
|
+
function inexactBits(text, keys, depth = 1) {
|
|
498
555
|
if (!/[0-9][.eE]/.test(text)) {
|
|
499
556
|
return 0;
|
|
500
557
|
}
|
|
501
558
|
let bits = 0;
|
|
502
559
|
keys.forEach((key, i) => {
|
|
503
|
-
if (inexactLiteral(text, key)) {
|
|
560
|
+
if (inexactLiteral(text, key, depth)) {
|
|
504
561
|
bits |= 1 << i;
|
|
505
562
|
}
|
|
506
563
|
});
|
|
@@ -540,6 +597,85 @@ function checkNumberVerification(value, count, report, line) {
|
|
|
540
597
|
: `numberVerification holds ${shown(raw === undefined ? null : digits)}, not 281474976710655`, { line, element: "numberVerification" });
|
|
541
598
|
}
|
|
542
599
|
}
|
|
600
|
+
/**
|
|
601
|
+
* Whether an aspect name is one CX (or NDEx, or Cytoscape) defines.
|
|
602
|
+
* @param name - the aspect name as written
|
|
603
|
+
* @returns true for a known aspect
|
|
604
|
+
*/
|
|
605
|
+
function isCxAspect(name) {
|
|
606
|
+
return (READ_ASPECTS.has(name) ||
|
|
607
|
+
OLD_ASPECT_NAMES.has(name) ||
|
|
608
|
+
CX_ASPECT_KEYS.includes(name) ||
|
|
609
|
+
TABLE_STYLE_ASPECTS.includes(name));
|
|
610
|
+
}
|
|
611
|
+
/** The id keys of the aspects whose ids are checked for precision. */
|
|
612
|
+
const ID_KEYS = new Map([
|
|
613
|
+
["nodes", ["@id"]],
|
|
614
|
+
["edges", ["@id", "s", "t"]],
|
|
615
|
+
]);
|
|
616
|
+
/**
|
|
617
|
+
* File an attribute element by its attribute name n; one without a usable name is reported and skipped.
|
|
618
|
+
* @param table - the aspect's attribute table
|
|
619
|
+
* @param name - the aspect
|
|
620
|
+
* @param held - the element
|
|
621
|
+
* @param report - the report
|
|
622
|
+
*/
|
|
623
|
+
function fileAttribute(table, name, held, report) {
|
|
624
|
+
const { value, line } = held;
|
|
625
|
+
const n = isRecord(value) ? value.n : undefined;
|
|
626
|
+
if (n === undefined || n === null) {
|
|
627
|
+
report.error("missing-value", MISSING_ID_CODE, `a ${name} element has no attribute name n; skipped`, {
|
|
628
|
+
line,
|
|
629
|
+
element: name,
|
|
630
|
+
});
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
if (typeof n !== "string" || n === "") {
|
|
634
|
+
report.error("validation-error", BAD_VALUE_CODE, `a ${name} element has the attribute name n ${shown(n)}, not a non-empty string; skipped`, { line, element: name });
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
if (!table.has(n)) {
|
|
638
|
+
table.set(n, []);
|
|
639
|
+
}
|
|
640
|
+
table.get(n)?.push(held);
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* File one parsed aspect element: kept verbatim, checked (numberVerification), filed by attribute
|
|
644
|
+
* name (an attribute element without a usable name n is reported and skipped) or by aspect.
|
|
645
|
+
* @param state - the document being collected
|
|
646
|
+
* @param element - the element
|
|
647
|
+
*/
|
|
648
|
+
function collectElement(state, element) {
|
|
649
|
+
const { doc, report } = state;
|
|
650
|
+
const { name, value, text, line, exact, depth } = element;
|
|
651
|
+
doc.structure.element(name, value, line);
|
|
652
|
+
if (ALSO_KEPT.has(name) || !READ_ASPECTS.has(name)) {
|
|
653
|
+
if (!doc.kept.has(name)) {
|
|
654
|
+
doc.kept.set(name, []);
|
|
655
|
+
}
|
|
656
|
+
// numberVerification holds Java's Long.MAX_VALUE in every Cytoscape file: checked, never warned
|
|
657
|
+
const onPrecision = name === "numberVerification" ? undefined : state.onKeptPrecision;
|
|
658
|
+
doc.kept.get(name)?.push(exact ? plainJson(value, onPrecision) : value);
|
|
659
|
+
}
|
|
660
|
+
if (!READ_ASPECTS.has(name) || name === "metaData" || name === "status") {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
if (name === "numberVerification") {
|
|
664
|
+
checkNumberVerification(value, ++state.verifications, report, line);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const ids = ID_KEYS.get(name);
|
|
668
|
+
const held = { value, line, inexact: ids === undefined ? 0 : inexactBits(text, ids, depth) };
|
|
669
|
+
const table = attributeTable(doc, name);
|
|
670
|
+
if (table !== null) {
|
|
671
|
+
fileAttribute(table, name, held, report);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
if (!doc.aspects.has(name)) {
|
|
675
|
+
doc.aspects.set(name, []);
|
|
676
|
+
}
|
|
677
|
+
doc.aspects.get(name)?.push(held);
|
|
678
|
+
}
|
|
543
679
|
/**
|
|
544
680
|
* Read the whole document through the streaming scanner, checking its structure.
|
|
545
681
|
* @param input - the input
|
|
@@ -548,76 +684,56 @@ function checkNumberVerification(value, count, report, line) {
|
|
|
548
684
|
* @returns what was read
|
|
549
685
|
*/
|
|
550
686
|
async function readDocument(input, report, options) {
|
|
551
|
-
const structure = new CxStructure(report);
|
|
687
|
+
const structure = new CxStructure(report, (name) => OLD_ASPECT_NAMES.get(name) ?? name);
|
|
552
688
|
const doc = {
|
|
553
689
|
aspects: new Map(),
|
|
554
690
|
attributes: { node: new Map(), edge: new Map(), network: new Map() },
|
|
555
691
|
kept: new Map(),
|
|
556
692
|
structure,
|
|
557
693
|
};
|
|
694
|
+
const state = {
|
|
695
|
+
doc,
|
|
696
|
+
report,
|
|
697
|
+
verifications: 0,
|
|
698
|
+
onKeptPrecision: keptPrecision(report, "meta.extra.cx"),
|
|
699
|
+
};
|
|
558
700
|
let sinceCheck = 0;
|
|
559
|
-
let
|
|
701
|
+
let members = 0;
|
|
702
|
+
let known = 0;
|
|
703
|
+
const unknownNames = new Set();
|
|
704
|
+
const lastMember = { block: -1 };
|
|
705
|
+
const countMember = (block, name) => {
|
|
706
|
+
if (block !== lastMember.block) {
|
|
707
|
+
lastMember.block = block;
|
|
708
|
+
members++;
|
|
709
|
+
}
|
|
710
|
+
if (name !== null && isCxAspect(name)) {
|
|
711
|
+
known++;
|
|
712
|
+
}
|
|
713
|
+
else if (name !== null) {
|
|
714
|
+
unknownNames.add(name);
|
|
715
|
+
}
|
|
716
|
+
};
|
|
560
717
|
const canonical = (name, line) => {
|
|
561
|
-
const renamed = OLD_ASPECT_NAMES
|
|
718
|
+
const renamed = OLD_ASPECT_NAMES.get(name);
|
|
562
719
|
if (renamed === undefined) {
|
|
563
720
|
return name;
|
|
564
721
|
}
|
|
565
722
|
report.warnOnce("coercion", CX_ISSUE.OLD_ASPECT_NAME, `the old aspect name "${name}" is read as "${renamed}"`, { line, element: name }, `${CX_ISSUE.OLD_ASPECT_NAME}:${name}`);
|
|
566
723
|
return renamed;
|
|
567
724
|
};
|
|
568
|
-
const collect = (name, value, text, line, exact) => {
|
|
569
|
-
|
|
570
|
-
if (ALSO_KEPT.has(name) || !READ_ASPECTS.has(name)) {
|
|
571
|
-
if (!doc.kept.has(name)) {
|
|
572
|
-
doc.kept.set(name, []);
|
|
573
|
-
}
|
|
574
|
-
doc.kept.get(name)?.push(exact ? plainJson(value) : value);
|
|
575
|
-
}
|
|
576
|
-
if (!READ_ASPECTS.has(name) || name === "metaData" || name === "status") {
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
if (name === "numberVerification") {
|
|
580
|
-
checkNumberVerification(value, ++verifications, report, line);
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
let inexact = 0;
|
|
584
|
-
if (name === "nodes") {
|
|
585
|
-
inexact = inexactBits(text, ["@id"]);
|
|
586
|
-
}
|
|
587
|
-
else if (name === "edges") {
|
|
588
|
-
inexact = inexactBits(text, ["@id", "s", "t"]);
|
|
589
|
-
}
|
|
590
|
-
const held = { value, line, inexact };
|
|
591
|
-
const table = attributeTable(doc, name);
|
|
592
|
-
if (table !== null) {
|
|
593
|
-
if (!isRecord(value) || typeof value.n !== "string") {
|
|
594
|
-
report.error("missing-value", MISSING_ID_CODE, `a ${name} element has no attribute name n; skipped`, {
|
|
595
|
-
line,
|
|
596
|
-
element: name,
|
|
597
|
-
});
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
const key = value.n;
|
|
601
|
-
if (!table.has(key)) {
|
|
602
|
-
table.set(key, []);
|
|
603
|
-
}
|
|
604
|
-
table.get(key)?.push(held);
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
if (!doc.aspects.has(name)) {
|
|
608
|
-
doc.aspects.set(name, []);
|
|
609
|
-
}
|
|
610
|
-
doc.aspects.get(name)?.push(held);
|
|
725
|
+
const collect = (name, value, text, line, exact, depth = 1) => {
|
|
726
|
+
collectElement(state, { name, value, text, line, exact, depth });
|
|
611
727
|
};
|
|
612
728
|
try {
|
|
613
|
-
for await (const event of scanAspects(textChunks(input, report, options))) {
|
|
729
|
+
for await (const event of scanAspects(textChunks(input, report, options), report)) {
|
|
614
730
|
if (++sinceCheck >= ABORT_CHECK_INTERVAL) {
|
|
615
731
|
sinceCheck = 0;
|
|
616
732
|
throwIfAborted(options.signal);
|
|
617
733
|
}
|
|
618
734
|
switch (event.kind) {
|
|
619
735
|
case "root":
|
|
620
|
-
report.fail(CX_ISSUE.NOT_CX, `a CX document is a JSON array of aspects; found ${
|
|
736
|
+
report.fail(CX_ISSUE.NOT_CX, `a CX document is a JSON array of aspects; found ${event.object ? "an object" : shown(event.value)}`, { line: event.line });
|
|
621
737
|
break;
|
|
622
738
|
case "member": {
|
|
623
739
|
const { value } = event;
|
|
@@ -626,27 +742,35 @@ async function readDocument(input, report, options) {
|
|
|
626
742
|
}
|
|
627
743
|
const keys = isRecord(value) ? Object.keys(value) : [];
|
|
628
744
|
if (isRecord(value) && keys.length === 1 && isRecord(value[keys[0]])) {
|
|
745
|
+
countMember(event.block, keys[0]);
|
|
629
746
|
const name = canonical(keys[0], event.line);
|
|
747
|
+
if (isCxAspect(name)) {
|
|
748
|
+
report.warning("validation-error", CX_ISSUE.SINGLE_OBJECT_ASPECT, `the "${keys[0]}" aspect is one object, not an array of elements; read as one element`, { line: event.line, element: keys[0] });
|
|
749
|
+
}
|
|
630
750
|
structure.block(name, event.line);
|
|
631
|
-
collect(name, value[keys[0]],
|
|
751
|
+
collect(name, value[keys[0]], event.text, event.line, event.exact, 2);
|
|
632
752
|
break;
|
|
633
753
|
}
|
|
754
|
+
countMember(event.block, null);
|
|
634
755
|
report.error("parse-error", BAD_ASPECT_BLOCK_CODE, `a member of the document is ${keys.length > 1 ? `an object with ${keys.length} keys (${keys.join(", ")})` : shown(value)}, not a one-key aspect fragment; skipped`, { line: event.line, element: keys[0] ?? null });
|
|
635
756
|
break;
|
|
636
757
|
}
|
|
637
758
|
case "block":
|
|
759
|
+
countMember(event.block, event.aspect);
|
|
760
|
+
reportSharedBlock(report, event);
|
|
638
761
|
structure.block(canonical(event.aspect, event.line), event.line);
|
|
639
762
|
break;
|
|
640
763
|
case "element": {
|
|
641
|
-
const name = OLD_ASPECT_NAMES
|
|
764
|
+
const name = OLD_ASPECT_NAMES.get(event.aspect) ?? event.aspect;
|
|
642
765
|
collect(name, event.value, event.text, event.line, event.exact);
|
|
643
766
|
break;
|
|
644
767
|
}
|
|
645
768
|
case "deep":
|
|
769
|
+
countMember(event.block, event.aspect);
|
|
646
770
|
reportTooDeep(report, event);
|
|
647
771
|
break;
|
|
648
772
|
case "extraKeys":
|
|
649
|
-
report.error("parse-error", BAD_ASPECT_BLOCK_CODE, `the "${event.aspect}" fragment holds more keys (${event.keys.join(", ")}); a fragment has one key, the others are skipped`, { line: event.line, element: event.aspect });
|
|
773
|
+
report.error("parse-error", BAD_ASPECT_BLOCK_CODE, `the "${event.aspect}" fragment holds more keys (${event.keys.join(", ")}) whose values are not arrays; a fragment has one key, the others are skipped`, { line: event.line, element: event.aspect });
|
|
650
774
|
break;
|
|
651
775
|
default:
|
|
652
776
|
break;
|
|
@@ -659,6 +783,15 @@ async function readDocument(input, report, options) {
|
|
|
659
783
|
}
|
|
660
784
|
throw err;
|
|
661
785
|
}
|
|
786
|
+
if (members > 0 && known === 0) {
|
|
787
|
+
const found = [...unknownNames].slice(0, 5).map((n) => JSON.stringify(n));
|
|
788
|
+
const hint = unknownNames.has("data") ? ": Cytoscape.js elements?" : "";
|
|
789
|
+
const seen = found.length > 0 ? ` (found ${found.join(", ")}${hint})` : "";
|
|
790
|
+
report.fail(CX_ISSUE.NOT_CX, `no member of the array is a CX aspect${seen}; the input is not CX`);
|
|
791
|
+
}
|
|
792
|
+
if (structure.hasStatus && !structure.statusWellFormed()) {
|
|
793
|
+
report.error("validation-error", BAD_VALUE_CODE, `the status element is ${shown(structure.status ?? null)}, not an object with a boolean success; ignored`, { element: "status" });
|
|
794
|
+
}
|
|
662
795
|
structure.checkCounts();
|
|
663
796
|
return doc;
|
|
664
797
|
}
|
|
@@ -676,11 +809,13 @@ function refId(raw) {
|
|
|
676
809
|
}
|
|
677
810
|
}
|
|
678
811
|
/**
|
|
679
|
-
* A member list: the ids of an array, or null for "all".
|
|
812
|
+
* A member list: the ids of an array, or null for "all" (an absent list is empty).
|
|
680
813
|
* @param raw - the list
|
|
814
|
+
* @param onBad - told about a list that is neither an array nor "all" (BAD_VALUE) and about a
|
|
815
|
+
* member that is not an id (INVALID_ID)
|
|
681
816
|
* @returns the set, or null for every element
|
|
682
817
|
*/
|
|
683
|
-
function memberSet(raw) {
|
|
818
|
+
function memberSet(raw, onBad) {
|
|
684
819
|
if (raw === "all") {
|
|
685
820
|
return null;
|
|
686
821
|
}
|
|
@@ -688,86 +823,64 @@ function memberSet(raw) {
|
|
|
688
823
|
if (Array.isArray(raw)) {
|
|
689
824
|
for (const item of raw) {
|
|
690
825
|
const id = refId(item);
|
|
691
|
-
if (id
|
|
826
|
+
if (id === null) {
|
|
827
|
+
onBad?.(CX_ISSUE.INVALID_ID, `the member ${shown(item)} is not an id; ignored`);
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
692
830
|
out.add(id);
|
|
693
831
|
}
|
|
694
832
|
}
|
|
695
833
|
}
|
|
834
|
+
else if (raw !== undefined) {
|
|
835
|
+
onBad?.(BAD_VALUE_CODE, `the member list ${shown(raw)} is neither a list of ids nor "all"; read as empty`);
|
|
836
|
+
}
|
|
696
837
|
return out;
|
|
697
838
|
}
|
|
698
839
|
/**
|
|
699
|
-
* The
|
|
700
|
-
*
|
|
701
|
-
*
|
|
840
|
+
* The id of an element of a structural aspect (cySubNetworks, cyViews, cyNetworkRelations), or
|
|
841
|
+
* null with an issue: E_BAD_ASPECT_BLOCK for an element that is not an object, E_MISSING_ID or
|
|
842
|
+
* E_INVALID_ID for its id.
|
|
843
|
+
* @param held - the element
|
|
844
|
+
* @param name - the aspect
|
|
845
|
+
* @param key - the id key
|
|
846
|
+
* @param report - the report
|
|
847
|
+
* @returns the element and its id, or null
|
|
702
848
|
*/
|
|
703
|
-
function
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
const relationNames = new Map();
|
|
712
|
-
const order = [];
|
|
713
|
-
const views = new Map();
|
|
714
|
-
const addView = (sub, view) => {
|
|
715
|
-
const list = views.get(sub) ?? [];
|
|
716
|
-
if (!list.includes(view)) {
|
|
717
|
-
list.push(view);
|
|
718
|
-
}
|
|
719
|
-
views.set(sub, list);
|
|
720
|
-
};
|
|
721
|
-
for (const { value } of aspect(doc, "cyNetworkRelations")) {
|
|
722
|
-
if (!isRecord(value)) {
|
|
723
|
-
continue;
|
|
724
|
-
}
|
|
725
|
-
const child = refId(value.c);
|
|
726
|
-
if (child === null) {
|
|
727
|
-
continue;
|
|
728
|
-
}
|
|
729
|
-
if (value.r === "view") {
|
|
730
|
-
const parent = refId(value.p);
|
|
731
|
-
if (parent !== null) {
|
|
732
|
-
addView(parent, child);
|
|
733
|
-
}
|
|
734
|
-
continue;
|
|
735
|
-
}
|
|
736
|
-
if (typeof value.name === "string") {
|
|
737
|
-
relationNames.set(child, value.name);
|
|
738
|
-
}
|
|
739
|
-
if (subs.has(child) && !order.includes(child)) {
|
|
740
|
-
order.push(child);
|
|
741
|
-
}
|
|
849
|
+
function structuralId(held, name, key, report) {
|
|
850
|
+
const { value, line } = held;
|
|
851
|
+
if (!isRecord(value)) {
|
|
852
|
+
report.error("parse-error", BAD_ASPECT_BLOCK_CODE, `a ${name} element is ${shown(value)}, not an object; skipped`, {
|
|
853
|
+
line,
|
|
854
|
+
element: name,
|
|
855
|
+
});
|
|
856
|
+
return null;
|
|
742
857
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
858
|
+
if (value[key] === undefined || value[key] === null) {
|
|
859
|
+
report.error("missing-value", MISSING_ID_CODE, `a ${name} element has no ${key}; skipped`, {
|
|
860
|
+
line,
|
|
861
|
+
element: name,
|
|
862
|
+
});
|
|
863
|
+
return null;
|
|
749
864
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
865
|
+
const id = refId(value[key]);
|
|
866
|
+
if (id === null) {
|
|
867
|
+
report.error("validation-error", CX_ISSUE.INVALID_ID, `a ${name} element has the ${key} ${shown(value[key])}, not an integer id; skipped`, {
|
|
868
|
+
line,
|
|
869
|
+
element: name,
|
|
870
|
+
});
|
|
871
|
+
return null;
|
|
754
872
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
found ?? (found = value.v);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
return found;
|
|
770
|
-
};
|
|
873
|
+
return { value, id };
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* The graphs of a document, in the order Cytoscape opens them.
|
|
877
|
+
* @param doc - the document
|
|
878
|
+
* @param report - where malformed subnetworks, views and relations are recorded
|
|
879
|
+
* @returns one plan per graph (at least one)
|
|
880
|
+
*/
|
|
881
|
+
function graphPlans(doc, report) {
|
|
882
|
+
const subs = readSubnetworks(doc, report);
|
|
883
|
+
const { relationNames, order, views } = readRelations(doc, subs, report);
|
|
771
884
|
if (order.length <= 1) {
|
|
772
885
|
const sub = order.length === 1 ? order[0] : null;
|
|
773
886
|
const members = sub === null ? undefined : subs.get(sub);
|
|
@@ -778,10 +891,11 @@ function graphPlans(doc) {
|
|
|
778
891
|
if (graphViews.length === 0) {
|
|
779
892
|
graphViews = layoutViews(doc);
|
|
780
893
|
}
|
|
894
|
+
const relationName = sub === null ? null : (relationNames.get(sub) ?? null);
|
|
781
895
|
return [
|
|
782
896
|
{
|
|
783
897
|
index: 0,
|
|
784
|
-
name:
|
|
898
|
+
name: relationName ?? networkName(doc, sub),
|
|
785
899
|
subnetwork: sub,
|
|
786
900
|
nodes: members?.nodes ?? null,
|
|
787
901
|
edges: members?.edges ?? null,
|
|
@@ -791,13 +905,125 @@ function graphPlans(doc) {
|
|
|
791
905
|
}
|
|
792
906
|
return order.map((sub, index) => ({
|
|
793
907
|
index,
|
|
794
|
-
name: relationNames.get(sub) ?? networkName(sub),
|
|
908
|
+
name: relationNames.get(sub) ?? networkName(doc, sub),
|
|
795
909
|
subnetwork: sub,
|
|
796
910
|
nodes: subs.get(sub)?.nodes ?? null,
|
|
797
911
|
edges: subs.get(sub)?.edges ?? null,
|
|
798
912
|
views: views.get(sub) ?? [],
|
|
799
913
|
}));
|
|
800
914
|
}
|
|
915
|
+
/**
|
|
916
|
+
* The subnetworks a document declares (cySubNetworks), by id; a repeated id is reported and skipped.
|
|
917
|
+
* @param doc - the document
|
|
918
|
+
* @param report - the report
|
|
919
|
+
* @returns the subnetworks
|
|
920
|
+
*/
|
|
921
|
+
function readSubnetworks(doc, report) {
|
|
922
|
+
const subs = new Map();
|
|
923
|
+
for (const held of aspect(doc, "cySubNetworks")) {
|
|
924
|
+
const sub = structuralId(held, "cySubNetworks", "@id", report);
|
|
925
|
+
if (sub === null) {
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
const { value, id } = sub;
|
|
929
|
+
if (subs.has(id)) {
|
|
930
|
+
report.error("validation-error", BAD_ASPECT_BLOCK_CODE, `a second cySubNetworks element has the @id ${String(id)}; skipped, the first is used`, { line: held.line, element: "cySubNetworks" });
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
const onBad = (code, message) => {
|
|
934
|
+
report.error("validation-error", code, `subnetwork ${String(id)}: ${message}`, {
|
|
935
|
+
line: held.line,
|
|
936
|
+
element: "cySubNetworks",
|
|
937
|
+
});
|
|
938
|
+
};
|
|
939
|
+
subs.set(id, { nodes: memberSet(value.nodes, onBad), edges: memberSet(value.edges, onBad) });
|
|
940
|
+
}
|
|
941
|
+
return subs;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* The network relations of a document: each subnetwork's name, the order Cytoscape opens them in
|
|
945
|
+
* (the relations' order, then the other subnetworks), and each one's views.
|
|
946
|
+
* @param doc - the document
|
|
947
|
+
* @param subs - the subnetworks
|
|
948
|
+
* @param report - the report
|
|
949
|
+
* @returns the names, the order and the views
|
|
950
|
+
*/
|
|
951
|
+
function readRelations(doc, subs, report) {
|
|
952
|
+
const relationNames = new Map();
|
|
953
|
+
const order = [];
|
|
954
|
+
const views = new Map();
|
|
955
|
+
const addView = (sub, view) => {
|
|
956
|
+
const list = views.get(sub) ?? [];
|
|
957
|
+
if (!list.includes(view)) {
|
|
958
|
+
list.push(view);
|
|
959
|
+
}
|
|
960
|
+
views.set(sub, list);
|
|
961
|
+
};
|
|
962
|
+
for (const held of aspect(doc, "cyNetworkRelations")) {
|
|
963
|
+
const relation = structuralId(held, "cyNetworkRelations", "c", report);
|
|
964
|
+
if (relation !== null) {
|
|
965
|
+
fileRelation(relation, subs, { relationNames, order, addView });
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
for (const held of aspect(doc, "cyViews")) {
|
|
969
|
+
const view = structuralId(held, "cyViews", "@id", report);
|
|
970
|
+
const sub = view === null ? null : refId(view.value.s);
|
|
971
|
+
if (view !== null && sub !== null) {
|
|
972
|
+
addView(sub, view.id);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
order.push(...[...subs.keys()].filter((id) => !order.includes(id)));
|
|
976
|
+
return { relationNames, order, views };
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* File one network relation: a view of a subnetwork, or a subnetwork's name and its place in the order.
|
|
980
|
+
* @param relation - the relation element and its child id
|
|
981
|
+
* @param relation.value - the element
|
|
982
|
+
* @param relation.id - the child: a view or a subnetwork
|
|
983
|
+
* @param subs - the subnetworks
|
|
984
|
+
* @param out - where the relation is filed
|
|
985
|
+
* @param out.relationNames - each subnetwork's name
|
|
986
|
+
* @param out.order - the subnetworks in relation order
|
|
987
|
+
* @param out.addView - records a view of a subnetwork
|
|
988
|
+
*/
|
|
989
|
+
function fileRelation(relation, subs, out) {
|
|
990
|
+
const { value, id: child } = relation;
|
|
991
|
+
if (value.r === "view") {
|
|
992
|
+
const parent = refId(value.p);
|
|
993
|
+
if (parent !== null) {
|
|
994
|
+
out.addView(parent, child);
|
|
995
|
+
}
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
if (typeof value.name === "string") {
|
|
999
|
+
out.relationNames.set(child, value.name);
|
|
1000
|
+
}
|
|
1001
|
+
if (subs.has(child) && !out.order.includes(child)) {
|
|
1002
|
+
out.order.push(child);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* A subnetwork's name from the network attributes: the one scoped to it, else the unscoped one.
|
|
1007
|
+
* @param doc - the document
|
|
1008
|
+
* @param sub - the subnetwork, or null
|
|
1009
|
+
* @returns the name, or null
|
|
1010
|
+
*/
|
|
1011
|
+
function networkName(doc, sub) {
|
|
1012
|
+
let found = null;
|
|
1013
|
+
for (const { value } of doc.attributes.network.get("name") ?? []) {
|
|
1014
|
+
if (!isRecord(value) || typeof value.v !== "string") {
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
const scope = value.s === undefined ? null : refId(value.s);
|
|
1018
|
+
if (scope === sub) {
|
|
1019
|
+
return value.v;
|
|
1020
|
+
}
|
|
1021
|
+
if (scope === null) {
|
|
1022
|
+
found ?? (found = value.v);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
return found;
|
|
1026
|
+
}
|
|
801
1027
|
/**
|
|
802
1028
|
* The distinct views the layout entries name, in order of appearance.
|
|
803
1029
|
* @param doc - the document
|
|
@@ -838,10 +1064,14 @@ class CxReader {
|
|
|
838
1064
|
this.rootNodes = new Set();
|
|
839
1065
|
/** Every edge id of the root network. */
|
|
840
1066
|
this.rootEdges = new Set();
|
|
841
|
-
/** CX node id -> sink node index, for this graph. */
|
|
1067
|
+
/** CX node id (as written, before the `ids` option) -> sink node index, for this graph. */
|
|
842
1068
|
this.nodeRows = new Map();
|
|
843
|
-
/**
|
|
1069
|
+
/** Sink node id -> sink node index: two CX ids the `ids` option makes one are one node. */
|
|
1070
|
+
this.sinkRows = new Map();
|
|
1071
|
+
/** CX edge id (as written) -> sink edge index, for this graph. */
|
|
844
1072
|
this.edgeRows = new Map();
|
|
1073
|
+
/** CX node id -> the id given to the sink, for the nodes whose original id was restored. */
|
|
1074
|
+
this.sinkIds = new Map();
|
|
845
1075
|
/** The subnetwork ids of the document. */
|
|
846
1076
|
this.subnetworks = new Set();
|
|
847
1077
|
this.dangling = new Map();
|
|
@@ -854,8 +1084,8 @@ class CxReader {
|
|
|
854
1084
|
this.weighted = false;
|
|
855
1085
|
/** The core field values written per row (n, r), to detect a differing name attribute. */
|
|
856
1086
|
this.coreValues = new Map();
|
|
857
|
-
/** The weight attribute values of the edges, by edge id
|
|
858
|
-
this.weights =
|
|
1087
|
+
/** The weight attribute values of the edges, by edge id. */
|
|
1088
|
+
this.weights = new Map();
|
|
859
1089
|
this.sink = sink;
|
|
860
1090
|
this.report = report;
|
|
861
1091
|
this.options = options;
|
|
@@ -889,6 +1119,7 @@ class CxReader {
|
|
|
889
1119
|
this.direction.setHeader(true);
|
|
890
1120
|
this.checkMembers();
|
|
891
1121
|
this.checkRootOnly();
|
|
1122
|
+
this.checkTableColumns();
|
|
892
1123
|
const nodeColumns = this.declareAttributes("node");
|
|
893
1124
|
this.readNodes();
|
|
894
1125
|
this.readGroups();
|
|
@@ -901,7 +1132,7 @@ class CxReader {
|
|
|
901
1132
|
this.readVisualProperties();
|
|
902
1133
|
this.readProvenance();
|
|
903
1134
|
for (const [kind, count] of this.dangling) {
|
|
904
|
-
report.warning("validation-error", DANGLING_REFERENCE_CODE, `${count} ${kind}(
|
|
1135
|
+
report.warning("validation-error", DANGLING_REFERENCE_CODE, `${count} ${kind}${plural(count)} ${agree(count, "names", "name")} nothing; ignored`, {
|
|
905
1136
|
element: kind,
|
|
906
1137
|
});
|
|
907
1138
|
}
|
|
@@ -930,15 +1161,6 @@ class CxReader {
|
|
|
930
1161
|
inGraph(id) {
|
|
931
1162
|
return this.plan.nodes === null || this.plan.nodes.has(id);
|
|
932
1163
|
}
|
|
933
|
-
/**
|
|
934
|
-
* The key of the node and edge row maps for a CX id as an aspect writes it: the id the `ids`
|
|
935
|
-
* option makes of it, which is what idOf() stored the element under.
|
|
936
|
-
* @param raw - the CX id
|
|
937
|
-
* @returns the row key
|
|
938
|
-
*/
|
|
939
|
-
key(raw) {
|
|
940
|
-
return this.options.ids === "keep" ? raw : this.coercer.value(raw);
|
|
941
|
-
}
|
|
942
1164
|
/**
|
|
943
1165
|
* Whether a scoped value applies to this graph: unscoped, or scoped to its subnetwork. A scope
|
|
944
1166
|
* naming no subnetwork is counted as dangling.
|
|
@@ -959,41 +1181,97 @@ class CxReader {
|
|
|
959
1181
|
return 0;
|
|
960
1182
|
}
|
|
961
1183
|
/**
|
|
962
|
-
* The id of an element
|
|
1184
|
+
* The id of an element by the CX id rule, as the document's references name it (the `ids`
|
|
1185
|
+
* option applies only to the id the sink holds, sinkId()).
|
|
963
1186
|
* @param raw - the parsed id
|
|
964
1187
|
* @param inexact - whether the literal was not a plain integer
|
|
965
1188
|
* @param element - the element name for issues
|
|
1189
|
+
* @param line - the element's line
|
|
966
1190
|
* @param edgeId - whether this is an edge's own id (stored in the f64 id column, not kept as digits)
|
|
967
1191
|
* @returns the id, or null when it was reported
|
|
968
1192
|
*/
|
|
969
|
-
idOf(raw, inexact, element, edgeId = false) {
|
|
1193
|
+
idOf(raw, inexact, element, line, edgeId = false) {
|
|
970
1194
|
try {
|
|
971
1195
|
const parsed = cxId(raw, inexact);
|
|
972
1196
|
if (parsed.note === "text") {
|
|
973
|
-
this.report.warnOnce("coercion", ID_TEXT_TYPE_CODE, `${element}: the id ${shown(raw)} is not written as an integer; read as ${String(parsed.id)}`, { element });
|
|
1197
|
+
this.report.warnOnce("coercion", ID_TEXT_TYPE_CODE, `${element}: the id ${shown(raw)} is not written as an integer; read as ${String(parsed.id)}`, { line, element });
|
|
974
1198
|
}
|
|
975
1199
|
else if (parsed.note === "precision" && edgeId) {
|
|
976
1200
|
// the edge is told apart by its digits, but the id column is f64
|
|
977
|
-
this.report.warnOnce("precision", PRECISION_CODE, `${element}: the edge id ${String(parsed.id)} is beyond 2^53; the id column holds the nearest double`, { element }, `${PRECISION_CODE}:edge`);
|
|
1201
|
+
this.report.warnOnce("precision", PRECISION_CODE, `${element}: the edge id ${String(parsed.id)} is beyond 2^53; the id column holds the nearest double`, { line, element }, `${PRECISION_CODE}:edge`);
|
|
978
1202
|
}
|
|
979
1203
|
else if (parsed.note === "precision") {
|
|
980
1204
|
this.idType = "mixed";
|
|
981
|
-
this.report.warnOnce("precision", PRECISION_CODE, `${element}: the id ${String(parsed.id)} is beyond 2^53; kept as its digits (a string id)`, { element });
|
|
1205
|
+
this.report.warnOnce("precision", PRECISION_CODE, `${element}: the id ${String(parsed.id)} is beyond 2^53; kept as its digits (a string id)`, { line, element });
|
|
982
1206
|
}
|
|
983
|
-
return
|
|
1207
|
+
return parsed.id;
|
|
984
1208
|
}
|
|
985
1209
|
catch (err) {
|
|
986
|
-
this.report.recordError(err, { element });
|
|
1210
|
+
this.report.recordError(err, { line, element });
|
|
987
1211
|
return null;
|
|
988
1212
|
}
|
|
989
1213
|
}
|
|
1214
|
+
/**
|
|
1215
|
+
* The id the sink holds a CX node under: its restored original id, else the `ids` option applied.
|
|
1216
|
+
* @param id - the CX id
|
|
1217
|
+
* @returns the sink id
|
|
1218
|
+
*/
|
|
1219
|
+
sinkId(id) {
|
|
1220
|
+
const original = this.sinkIds.get(id);
|
|
1221
|
+
if (original !== undefined) {
|
|
1222
|
+
return original;
|
|
1223
|
+
}
|
|
1224
|
+
return this.options.ids === "keep" ? id : this.coercer.value(id);
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Add a node to the sink (once per sink id) and map its CX id to the row.
|
|
1228
|
+
* @param id - the CX id
|
|
1229
|
+
* @returns the row, and whether the sink id was new
|
|
1230
|
+
*/
|
|
1231
|
+
addNode(id) {
|
|
1232
|
+
const sinkId = this.sinkId(id);
|
|
1233
|
+
const existing = this.sinkRows.get(sinkId);
|
|
1234
|
+
if (existing !== undefined) {
|
|
1235
|
+
this.nodeRows.set(id, existing);
|
|
1236
|
+
return { row: existing, added: false };
|
|
1237
|
+
}
|
|
1238
|
+
const row = this.sink.addNode(sinkId);
|
|
1239
|
+
this.sinkRows.set(sinkId, row);
|
|
1240
|
+
this.nodeRows.set(id, row);
|
|
1241
|
+
return { row, added: true };
|
|
1242
|
+
}
|
|
990
1243
|
/**
|
|
991
1244
|
* Record a long value beyond 2^53 (once).
|
|
992
1245
|
* @param name - the attribute
|
|
993
1246
|
* @param digits - the value
|
|
994
1247
|
*/
|
|
995
1248
|
precision(name, digits) {
|
|
996
|
-
|
|
1249
|
+
const overflow = !Number.isFinite(Number(digits));
|
|
1250
|
+
this.report.warnOnce("precision", PRECISION_CODE, overflow
|
|
1251
|
+
? `"${name}": ${digits} is beyond the range of a double; stored as ${String(Number(digits))}`
|
|
1252
|
+
: `"${name}": ${digits} is beyond 2^53; stored as the nearest double`, { element: name }, `${PRECISION_CODE}:value`);
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Report the keys of a node or edge element CX does not define, once per key.
|
|
1256
|
+
* @param record - the element
|
|
1257
|
+
* @param known - the keys it defines
|
|
1258
|
+
* @param what - nodes or edges
|
|
1259
|
+
* @param line - its line
|
|
1260
|
+
*/
|
|
1261
|
+
unknownKeys(record, known, what, line) {
|
|
1262
|
+
for (const key of Object.keys(record)) {
|
|
1263
|
+
if (!known.has(key)) {
|
|
1264
|
+
this.report.warnOnce("unsupported", UNKNOWN_ELEMENT_CODE, `the ${what} key "${key}" is not defined by CX; its values are not read`, { line, element: key }, `${UNKNOWN_ELEMENT_CODE}:${what}:${key}`);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
/** Report cyTableColumn entries for a table CX does not define (once per table name). */
|
|
1269
|
+
checkTableColumns() {
|
|
1270
|
+
for (const { value, line } of aspect(this.doc, "cyTableColumn")) {
|
|
1271
|
+
if (isRecord(value) && typeof value.applies_to === "string" && !TABLES.has(value.applies_to)) {
|
|
1272
|
+
this.report.warnOnce("unsupported", UNKNOWN_ELEMENT_CODE, `a cyTableColumn entry applies to "${value.applies_to}", which is not node_table, edge_table or network_table; ignored`, { line, element: value.applies_to }, `${UNKNOWN_ELEMENT_CODE}:table:${value.applies_to}`);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
997
1275
|
}
|
|
998
1276
|
/** Count the subnetwork members that name no node or edge. */
|
|
999
1277
|
checkMembers() {
|
|
@@ -1037,7 +1315,7 @@ class CxReader {
|
|
|
1037
1315
|
const nodes = lost("nodes", this.rootNodes);
|
|
1038
1316
|
const edges = lost("edges", this.rootEdges);
|
|
1039
1317
|
if (nodes + edges > 0) {
|
|
1040
|
-
this.report.warning("unsupported", CX_ISSUE.ROOT_ONLY, `${nodes} node(
|
|
1318
|
+
this.report.warning("unsupported", CX_ISSUE.ROOT_ONLY, `${nodes} node${plural(nodes)} and ${edges} edge${plural(edges)} of the root network belong to no subnetwork (cySubNetworks); they are not read`, { element: "cySubNetworks" });
|
|
1041
1319
|
}
|
|
1042
1320
|
}
|
|
1043
1321
|
/**
|
|
@@ -1063,7 +1341,8 @@ class CxReader {
|
|
|
1063
1341
|
const declared = this.tableColumns(domain === "node" ? "node_table" : "edge_table");
|
|
1064
1342
|
const names = new Set([...declared.keys(), ...this.doc.attributes[domain].keys()]);
|
|
1065
1343
|
for (const name of names) {
|
|
1066
|
-
if (
|
|
1344
|
+
if ((domain === "edge" && name === this.options.weightFrom) ||
|
|
1345
|
+
(domain === "node" && name === ORIGINAL_ID_ATTRIBUTE && this.options.restoreMangledIds)) {
|
|
1067
1346
|
continue;
|
|
1068
1347
|
}
|
|
1069
1348
|
const types = [];
|
|
@@ -1144,7 +1423,10 @@ class CxReader {
|
|
|
1144
1423
|
continue;
|
|
1145
1424
|
}
|
|
1146
1425
|
const type = cxType(value.d);
|
|
1147
|
-
if (type
|
|
1426
|
+
if (type === null) {
|
|
1427
|
+
this.report.warnOnce("unsupported", UNKNOWN_ATTR_TYPE_CODE, `cyTableColumn declares "${value.n}" with the data type ${shown(value.d)}, which CX does not define; its values are typed by their own d`, { element: value.n }, `${UNKNOWN_ATTR_TYPE_CODE}:table:${table}:${value.n}`);
|
|
1428
|
+
}
|
|
1429
|
+
else {
|
|
1148
1430
|
out.set(value.n, type);
|
|
1149
1431
|
}
|
|
1150
1432
|
}
|
|
@@ -1196,14 +1478,6 @@ class CxReader {
|
|
|
1196
1478
|
if (scope === 0) {
|
|
1197
1479
|
continue;
|
|
1198
1480
|
}
|
|
1199
|
-
const target = refId(value.po);
|
|
1200
|
-
const row = target === null ? undefined : rows.get(this.key(target));
|
|
1201
|
-
if (row === undefined) {
|
|
1202
|
-
if (target === null || !root.has(target)) {
|
|
1203
|
-
this.dangle(`${domain} attribute target`);
|
|
1204
|
-
}
|
|
1205
|
-
continue;
|
|
1206
|
-
}
|
|
1207
1481
|
if (value.v === undefined) {
|
|
1208
1482
|
this.report.error("missing-value", BAD_VALUE_CODE, `a ${domain}Attributes element for "${column.name}" has no v; skipped`, {
|
|
1209
1483
|
line,
|
|
@@ -1211,18 +1485,29 @@ class CxReader {
|
|
|
1211
1485
|
});
|
|
1212
1486
|
continue;
|
|
1213
1487
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1488
|
+
// po names one element, or (cxio's shared values) a list of them
|
|
1489
|
+
for (const raw of Array.isArray(value.po) ? value.po : [value.po]) {
|
|
1490
|
+
const target = refId(raw);
|
|
1491
|
+
const row = target === null ? undefined : rows.get(target);
|
|
1492
|
+
if (row === undefined) {
|
|
1493
|
+
if (target === null || !root.has(target)) {
|
|
1494
|
+
this.dangle(`${domain} attribute target`);
|
|
1495
|
+
}
|
|
1496
|
+
continue;
|
|
1497
|
+
}
|
|
1498
|
+
if (value.v === null) {
|
|
1499
|
+
continue;
|
|
1500
|
+
}
|
|
1501
|
+
const previous = chosen.get(row);
|
|
1502
|
+
if (previous !== undefined && previous.scope > scope) {
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
if (previous?.scope === scope &&
|
|
1506
|
+
JSON.stringify(previous.value) !== JSON.stringify(plainJson(value.v))) {
|
|
1507
|
+
this.report.warnOnce("validation-error", DUPLICATE_ATTRIBUTE_CODE, `${domain} ${shown(raw)} has the attribute "${column.name}" twice; the later value wins`, { line, element: column.name }, `${DUPLICATE_ATTRIBUTE_CODE}:${domain}:${column.name}`);
|
|
1508
|
+
}
|
|
1509
|
+
chosen.set(row, { scope, value: value.v, d: value.d, line });
|
|
1224
1510
|
}
|
|
1225
|
-
chosen.set(row, { scope, value: value.v, d: value.d, line });
|
|
1226
1511
|
}
|
|
1227
1512
|
for (const [row, { value, d, line }] of chosen) {
|
|
1228
1513
|
this.writeCell(domain, column, row, value, d, line);
|
|
@@ -1264,6 +1549,10 @@ class CxReader {
|
|
|
1264
1549
|
}
|
|
1265
1550
|
let { handle } = column;
|
|
1266
1551
|
if (this.isStructural(domain, column.name)) {
|
|
1552
|
+
if (Array.isArray(raw)) {
|
|
1553
|
+
this.report.error("validation-error", BAD_VALUE_CODE, `${element}: ${shown(raw)} is a list; "${column.name}" holds one string, the cell is unset`, { line, element: column.name });
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1267
1556
|
handle = this.coreColumn(domain, column.name);
|
|
1268
1557
|
const prior = this.coreValue(domain, row, column.name);
|
|
1269
1558
|
if (prior !== undefined && prior !== value) {
|
|
@@ -1329,7 +1618,8 @@ class CxReader {
|
|
|
1329
1618
|
// ------------------------------------------------------------ nodes, groups, layout
|
|
1330
1619
|
/** Read the nodes of this graph. */
|
|
1331
1620
|
readNodes() {
|
|
1332
|
-
const { report
|
|
1621
|
+
const { report } = this;
|
|
1622
|
+
const originals = this.originalIds();
|
|
1333
1623
|
for (const held of aspect(this.doc, "nodes")) {
|
|
1334
1624
|
this.checkAbort();
|
|
1335
1625
|
const { value, line } = held;
|
|
@@ -1346,36 +1636,61 @@ class CxReader {
|
|
|
1346
1636
|
report.counts.skippedNodes++;
|
|
1347
1637
|
continue;
|
|
1348
1638
|
}
|
|
1639
|
+
this.unknownKeys(value, NODE_KEYS, "node", line);
|
|
1349
1640
|
const element = `node ${shown(value["@id"])}`;
|
|
1350
|
-
const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element);
|
|
1641
|
+
const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element, line);
|
|
1351
1642
|
if (id === null) {
|
|
1352
1643
|
report.counts.skippedNodes++;
|
|
1353
1644
|
continue;
|
|
1354
1645
|
}
|
|
1355
|
-
|
|
1356
|
-
if (!this.inGraph(refId(value["@id"]) ?? id)) {
|
|
1646
|
+
if (!this.inGraph(id)) {
|
|
1357
1647
|
continue;
|
|
1358
1648
|
}
|
|
1359
1649
|
let row = this.nodeRows.get(id);
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1650
|
+
let added = false;
|
|
1651
|
+
if (row === undefined) {
|
|
1652
|
+
const original = originals.get(refId(value["@id"]) ?? id);
|
|
1653
|
+
if (original !== undefined) {
|
|
1654
|
+
this.idType = "mixed";
|
|
1655
|
+
this.sinkIds.set(id, original);
|
|
1656
|
+
}
|
|
1364
1657
|
try {
|
|
1365
|
-
row =
|
|
1658
|
+
({ row, added } = this.addNode(id));
|
|
1366
1659
|
}
|
|
1367
1660
|
catch (err) {
|
|
1368
1661
|
report.recordError(err, { line, element });
|
|
1369
1662
|
report.counts.skippedNodes++;
|
|
1370
1663
|
continue;
|
|
1371
1664
|
}
|
|
1372
|
-
|
|
1665
|
+
}
|
|
1666
|
+
if (added) {
|
|
1373
1667
|
report.counts.nodes++;
|
|
1374
1668
|
}
|
|
1669
|
+
else {
|
|
1670
|
+
report.warning("merged", DUPLICATE_NODE_CODE, `${element} is declared more than once; its fields are merged (the later values win)`, { line, element });
|
|
1671
|
+
}
|
|
1375
1672
|
this.writeCore("node", row, "name", value.n, element, line);
|
|
1376
1673
|
this.writeCore("node", row, "represents", value.r, element, line);
|
|
1377
1674
|
}
|
|
1378
1675
|
}
|
|
1676
|
+
/**
|
|
1677
|
+
* The original ids the exporter's `sanitizeIds: "mangle"` kept in the `graphty:originalId`
|
|
1678
|
+
* attribute, when they are to be restored.
|
|
1679
|
+
* @returns CX node id -> original id
|
|
1680
|
+
*/
|
|
1681
|
+
originalIds() {
|
|
1682
|
+
const out = new Map();
|
|
1683
|
+
if (!this.options.restoreMangledIds) {
|
|
1684
|
+
return out;
|
|
1685
|
+
}
|
|
1686
|
+
for (const { value } of this.doc.attributes.node.get(ORIGINAL_ID_ATTRIBUTE) ?? []) {
|
|
1687
|
+
const po = isRecord(value) && this.scopePeek(value.s) !== 0 ? refId(value.po) : null;
|
|
1688
|
+
if (po !== null && isRecord(value) && typeof value.v === "string") {
|
|
1689
|
+
out.set(po, value.v);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return out;
|
|
1693
|
+
}
|
|
1379
1694
|
/**
|
|
1380
1695
|
* Write a core string field (n, r, i).
|
|
1381
1696
|
* @param domain - node or edge
|
|
@@ -1423,31 +1738,31 @@ class CxReader {
|
|
|
1423
1738
|
}
|
|
1424
1739
|
const parents = new Map();
|
|
1425
1740
|
const collapsed = [];
|
|
1426
|
-
for (const
|
|
1427
|
-
|
|
1741
|
+
for (const held of groups) {
|
|
1742
|
+
const group = structuralId(held, "cyGroups", "@id", this.report);
|
|
1743
|
+
if (group === null) {
|
|
1428
1744
|
continue;
|
|
1429
1745
|
}
|
|
1430
|
-
const id =
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
element: "cyGroups",
|
|
1435
|
-
});
|
|
1436
|
-
continue;
|
|
1746
|
+
const { value, id } = group;
|
|
1747
|
+
const { line } = held;
|
|
1748
|
+
if (value.nodes !== undefined && value.nodes !== null && !Array.isArray(value.nodes)) {
|
|
1749
|
+
this.report.error("validation-error", BAD_VALUE_CODE, `group ${String(id)}: nodes is ${shown(value.nodes)}, not a list of node ids; no member is read`, { line, element: String(id) });
|
|
1437
1750
|
}
|
|
1438
|
-
|
|
1751
|
+
if (value.collapsed !== undefined && value.collapsed !== null && typeof value.collapsed !== "boolean") {
|
|
1752
|
+
this.report.error("validation-error", BAD_VALUE_CODE, `group ${String(id)}: collapsed is ${shown(value.collapsed)}, not a boolean; unset`, { line, element: String(id) });
|
|
1753
|
+
}
|
|
1754
|
+
let groupRow = this.nodeRows.get(id);
|
|
1439
1755
|
if (groupRow === undefined) {
|
|
1440
1756
|
if (this.rootNodes.has(id) || !this.hasMemberIn(value.nodes)) {
|
|
1441
1757
|
continue;
|
|
1442
1758
|
}
|
|
1443
1759
|
try {
|
|
1444
|
-
groupRow = this.
|
|
1760
|
+
groupRow = this.addNode(id).row;
|
|
1445
1761
|
}
|
|
1446
1762
|
catch (err) {
|
|
1447
1763
|
this.report.recordError(err, { line, element: String(id) });
|
|
1448
1764
|
continue;
|
|
1449
1765
|
}
|
|
1450
|
-
this.nodeRows.set(this.key(id), groupRow);
|
|
1451
1766
|
this.report.counts.nodes++;
|
|
1452
1767
|
this.report.warning("coercion", CX_ISSUE.GROUP_NODE_ADDED, `group ${String(id)} is not a node; its group node is added`, { line, element: String(id) });
|
|
1453
1768
|
}
|
|
@@ -1459,7 +1774,7 @@ class CxReader {
|
|
|
1459
1774
|
}
|
|
1460
1775
|
for (const raw of Array.isArray(value.nodes) ? value.nodes : []) {
|
|
1461
1776
|
const member = refId(raw);
|
|
1462
|
-
const row = member === null ? undefined : this.nodeRows.get(
|
|
1777
|
+
const row = member === null ? undefined : this.nodeRows.get(member);
|
|
1463
1778
|
if (row === undefined) {
|
|
1464
1779
|
if (member === null || !this.rootNodes.has(member)) {
|
|
1465
1780
|
this.report.error("missing-value", UNKNOWN_PARENT_CODE, `group ${String(id)}: the member ${shown(raw)} is not a node`, { line, element: String(id) });
|
|
@@ -1499,7 +1814,7 @@ class CxReader {
|
|
|
1499
1814
|
return (Array.isArray(raw) &&
|
|
1500
1815
|
raw.some((item) => {
|
|
1501
1816
|
const id = refId(item);
|
|
1502
|
-
return id !== null && this.nodeRows.has(
|
|
1817
|
+
return id !== null && this.nodeRows.has(id);
|
|
1503
1818
|
}));
|
|
1504
1819
|
}
|
|
1505
1820
|
/**
|
|
@@ -1562,6 +1877,7 @@ class CxReader {
|
|
|
1562
1877
|
for (const { value, line } of entries) {
|
|
1563
1878
|
this.checkAbort();
|
|
1564
1879
|
if (!isRecord(value)) {
|
|
1880
|
+
this.report.error("parse-error", BAD_ASPECT_BLOCK_CODE, `a cartesianLayout element is ${shown(value)}, not an object; skipped`, { line, element: "cartesianLayout" });
|
|
1565
1881
|
continue;
|
|
1566
1882
|
}
|
|
1567
1883
|
const view = value.view === undefined || value.view === null ? null : refId(value.view);
|
|
@@ -1577,14 +1893,21 @@ class CxReader {
|
|
|
1577
1893
|
this.report.error("validation-error", BAD_VALUE_CODE, `a cartesianLayout element for node ${shown(value.node)} has no numeric x and y; skipped`, { line, element: "cartesianLayout" });
|
|
1578
1894
|
continue;
|
|
1579
1895
|
}
|
|
1896
|
+
if (!fitsF32(value.x) || !fitsF32(value.y)) {
|
|
1897
|
+
this.report.error("validation-error", BAD_VALUE_CODE, `a cartesianLayout element for node ${shown(value.node)} has the coordinates ${String(value.x)}, ${String(value.y)}, beyond what the f32 position column holds; skipped`, { line, element: "cartesianLayout" });
|
|
1898
|
+
continue;
|
|
1899
|
+
}
|
|
1580
1900
|
const node = refId(value.node);
|
|
1581
|
-
const row = node === null ? undefined : this.nodeRows.get(
|
|
1901
|
+
const row = node === null ? undefined : this.nodeRows.get(node);
|
|
1582
1902
|
if (row === undefined) {
|
|
1583
1903
|
if (node === null || !this.rootNodes.has(node)) {
|
|
1584
1904
|
this.dangle("layout entry");
|
|
1585
1905
|
}
|
|
1586
1906
|
continue;
|
|
1587
1907
|
}
|
|
1908
|
+
if (value.z !== undefined && value.z !== null && typeof value.z !== "number") {
|
|
1909
|
+
this.report.error("validation-error", BAD_VALUE_CODE, `a cartesianLayout element for node ${shown(value.node)} has the z ${shown(value.z)}, not a number; z is unset`, { line, element: "cartesianLayout" });
|
|
1910
|
+
}
|
|
1588
1911
|
const key = `${slot}:${row}`;
|
|
1589
1912
|
if (seen.has(key)) {
|
|
1590
1913
|
this.report.warnOnce("validation-error", DUPLICATE_ATTRIBUTE_CODE, `node ${shown(value.node)} has two layout entries for one view; the later wins`, { line, element: "cartesianLayout" }, `${DUPLICATE_ATTRIBUTE_CODE}:layout`);
|
|
@@ -1652,6 +1975,7 @@ class CxReader {
|
|
|
1652
1975
|
readEdges() {
|
|
1653
1976
|
const { report, sink } = this;
|
|
1654
1977
|
const members = this.plan.edges;
|
|
1978
|
+
this.weights = this.edgeWeights();
|
|
1655
1979
|
for (const held of aspect(this.doc, "edges")) {
|
|
1656
1980
|
this.checkAbort();
|
|
1657
1981
|
const { value, line } = held;
|
|
@@ -1668,13 +1992,14 @@ class CxReader {
|
|
|
1668
1992
|
report.counts.skippedEdges++;
|
|
1669
1993
|
continue;
|
|
1670
1994
|
}
|
|
1995
|
+
this.unknownKeys(value, EDGE_KEYS, "edge", line);
|
|
1671
1996
|
const element = `edge ${shown(value["@id"])}`;
|
|
1672
|
-
const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element, true);
|
|
1997
|
+
const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element, line, true);
|
|
1673
1998
|
if (id === null) {
|
|
1674
1999
|
report.counts.skippedEdges++;
|
|
1675
2000
|
continue;
|
|
1676
2001
|
}
|
|
1677
|
-
if (members !== null && !members.has(
|
|
2002
|
+
if (members !== null && !members.has(id)) {
|
|
1678
2003
|
continue;
|
|
1679
2004
|
}
|
|
1680
2005
|
if (value.s === undefined || value.s === null || value.t === undefined || value.t === null) {
|
|
@@ -1685,8 +2010,8 @@ class CxReader {
|
|
|
1685
2010
|
report.counts.skippedEdges++;
|
|
1686
2011
|
continue;
|
|
1687
2012
|
}
|
|
1688
|
-
const s = this.idOf(value.s, (held.inexact & 2) !== 0, element);
|
|
1689
|
-
const t = s === null ? null : this.idOf(value.t, (held.inexact & 4) !== 0, element);
|
|
2013
|
+
const s = this.idOf(value.s, (held.inexact & 2) !== 0, element, line);
|
|
2014
|
+
const t = s === null ? null : this.idOf(value.t, (held.inexact & 4) !== 0, element, line);
|
|
1690
2015
|
if (s === null || t === null) {
|
|
1691
2016
|
report.counts.skippedEdges++;
|
|
1692
2017
|
continue;
|
|
@@ -1712,7 +2037,7 @@ class CxReader {
|
|
|
1712
2037
|
const before = sink.edgeCount;
|
|
1713
2038
|
let edge;
|
|
1714
2039
|
try {
|
|
1715
|
-
edge = this.direction.addEdge(s, t, "directed", weight, { line, element });
|
|
2040
|
+
edge = this.direction.addEdge(this.sinkId(s), this.sinkId(t), "directed", weight, { line, element });
|
|
1716
2041
|
}
|
|
1717
2042
|
catch (err) {
|
|
1718
2043
|
report.recordError(err, { line, element });
|
|
@@ -1750,35 +2075,71 @@ class CxReader {
|
|
|
1750
2075
|
return false;
|
|
1751
2076
|
}
|
|
1752
2077
|
try {
|
|
1753
|
-
|
|
2078
|
+
if (this.addNode(id).added) {
|
|
2079
|
+
this.report.counts.nodes++;
|
|
2080
|
+
}
|
|
1754
2081
|
}
|
|
1755
2082
|
catch (err) {
|
|
1756
2083
|
this.report.recordError(err, { line, element });
|
|
1757
2084
|
return false;
|
|
1758
2085
|
}
|
|
1759
|
-
this.report.counts.nodes++;
|
|
1760
2086
|
return true;
|
|
1761
2087
|
}
|
|
1762
2088
|
/**
|
|
1763
|
-
* The
|
|
1764
|
-
*
|
|
1765
|
-
*
|
|
2089
|
+
* The weightFrom attribute values by edge id: this subnetwork's own value beats the unscoped
|
|
2090
|
+
* one, and of two in one scope the later wins (W_DUPLICATE_ATTRIBUTE when they differ), as
|
|
2091
|
+
* writeAttributes() rules every other attribute.
|
|
2092
|
+
* @returns the values (parsed JSON) by edge id
|
|
1766
2093
|
*/
|
|
1767
|
-
|
|
2094
|
+
edgeWeights() {
|
|
1768
2095
|
const { weightFrom } = this.options;
|
|
2096
|
+
const chosen = new Map();
|
|
1769
2097
|
if (weightFrom === null) {
|
|
1770
|
-
return
|
|
2098
|
+
return new Map();
|
|
1771
2099
|
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
const po = refId(value.po);
|
|
1777
|
-
if (po !== null && value.v !== undefined && value.v !== null) {
|
|
1778
|
-
this.weights.set(this.key(po), plainJson(value.v));
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
2100
|
+
for (const { value, line } of this.doc.attributes.edge.get(weightFrom) ?? []) {
|
|
2101
|
+
const scope = isRecord(value) ? this.scopePeek(value.s) : 0;
|
|
2102
|
+
if (!isRecord(value) || scope === 0 || value.v === undefined || value.v === null) {
|
|
2103
|
+
continue;
|
|
1781
2104
|
}
|
|
2105
|
+
const v = plainJson(value.v);
|
|
2106
|
+
for (const raw of Array.isArray(value.po) ? value.po : [value.po]) {
|
|
2107
|
+
this.chooseWeight(chosen, raw, { scope, value: v }, line, weightFrom);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
return new Map([...chosen].map(([id, { value }]) => [id, value]));
|
|
2111
|
+
}
|
|
2112
|
+
/**
|
|
2113
|
+
* Keep one weight attribute element's value for one edge, unless a more specific scope already
|
|
2114
|
+
* gave it one; a second value in the same scope wins, with a warning.
|
|
2115
|
+
* @param chosen - the value chosen so far per edge
|
|
2116
|
+
* @param raw - the edge reference as written
|
|
2117
|
+
* @param weight - the value and the scope it applies in
|
|
2118
|
+
* @param weight.scope - the scope: higher is more specific
|
|
2119
|
+
* @param weight.value - the value
|
|
2120
|
+
* @param line - the element's line
|
|
2121
|
+
* @param weightFrom - the attribute name
|
|
2122
|
+
*/
|
|
2123
|
+
chooseWeight(chosen, raw, weight, line, weightFrom) {
|
|
2124
|
+
const po = refId(raw);
|
|
2125
|
+
const previous = po === null ? undefined : chosen.get(po);
|
|
2126
|
+
if (po === null || (previous !== undefined && previous.scope > weight.scope)) {
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
if (previous?.scope === weight.scope && JSON.stringify(previous.value) !== JSON.stringify(weight.value)) {
|
|
2130
|
+
this.report.warnOnce("validation-error", DUPLICATE_ATTRIBUTE_CODE, `edge ${shown(raw)} has the attribute "${weightFrom}" twice; the later value wins`, { line, element: weightFrom }, `${DUPLICATE_ATTRIBUTE_CODE}:edge:${weightFrom}`);
|
|
2131
|
+
}
|
|
2132
|
+
chosen.set(po, weight);
|
|
2133
|
+
}
|
|
2134
|
+
/**
|
|
2135
|
+
* The weight of an edge: its weightFrom attribute, parsed as a number (the declared d is not
|
|
2136
|
+
* consulted: a value that is not a number is E_INVALID_WEIGHT whatever its type says).
|
|
2137
|
+
* @param id - the edge id
|
|
2138
|
+
* @returns the weight, or undefined; E_INVALID_WEIGHT for a non-number
|
|
2139
|
+
*/
|
|
2140
|
+
weightOf(id) {
|
|
2141
|
+
if (this.options.weightFrom === null) {
|
|
2142
|
+
return undefined;
|
|
1782
2143
|
}
|
|
1783
2144
|
const raw = this.weights.get(id);
|
|
1784
2145
|
if (typeof raw === "string" && isNullText(raw)) {
|
|
@@ -1792,17 +2153,29 @@ class CxReader {
|
|
|
1792
2153
|
for (const [name, elements] of this.doc.attributes.network) {
|
|
1793
2154
|
let chosen = null;
|
|
1794
2155
|
let chosenScope = 0;
|
|
1795
|
-
for (const { value } of elements) {
|
|
2156
|
+
for (const { value, line } of elements) {
|
|
1796
2157
|
if (!isRecord(value)) {
|
|
1797
2158
|
continue;
|
|
1798
2159
|
}
|
|
1799
2160
|
const scope = this.scopeOf(value.s);
|
|
1800
|
-
if (scope
|
|
2161
|
+
if (scope === 0) {
|
|
2162
|
+
continue;
|
|
2163
|
+
}
|
|
2164
|
+
if (value.v === undefined) {
|
|
2165
|
+
this.report.error("missing-value", BAD_VALUE_CODE, `a networkAttributes element for "${name}" has no v; skipped`, { line, element: name });
|
|
2166
|
+
continue;
|
|
2167
|
+
}
|
|
2168
|
+
if (chosen !== null &&
|
|
2169
|
+
scope === chosenScope &&
|
|
2170
|
+
JSON.stringify(plainJson(chosen.v)) !== JSON.stringify(plainJson(value.v))) {
|
|
2171
|
+
this.report.warnOnce("validation-error", DUPLICATE_ATTRIBUTE_CODE, `the network attribute "${name}" is given twice; the later value wins`, { line, element: name }, `${DUPLICATE_ATTRIBUTE_CODE}:network:${name}`);
|
|
2172
|
+
}
|
|
2173
|
+
if (scope >= chosenScope) {
|
|
1801
2174
|
chosen = value;
|
|
1802
2175
|
chosenScope = scope;
|
|
1803
2176
|
}
|
|
1804
2177
|
}
|
|
1805
|
-
if (chosen
|
|
2178
|
+
if (chosen?.v === undefined || chosen.v === null) {
|
|
1806
2179
|
continue;
|
|
1807
2180
|
}
|
|
1808
2181
|
const type = cxType(chosen.d);
|
|
@@ -1869,7 +2242,7 @@ class CxReader {
|
|
|
1869
2242
|
const domain = value.properties_of === "nodes" ? "node" : "edge";
|
|
1870
2243
|
const target = refId(value.applies_to);
|
|
1871
2244
|
const rows = domain === "node" ? this.nodeRows : this.edgeRows;
|
|
1872
|
-
const row = target === null ? undefined : rows.get(
|
|
2245
|
+
const row = target === null ? undefined : rows.get(target);
|
|
1873
2246
|
if (row === undefined) {
|
|
1874
2247
|
if (target === null || !(domain === "node" ? this.rootNodes : this.rootEdges).has(target)) {
|
|
1875
2248
|
this.dangle(`${domain} visual property entry`);
|
|
@@ -1899,7 +2272,7 @@ class CxReader {
|
|
|
1899
2272
|
name: property,
|
|
1900
2273
|
dtype: "string",
|
|
1901
2274
|
nullable: true,
|
|
1902
|
-
origin: { format: CX_FORMAT, id:
|
|
2275
|
+
origin: { format: CX_FORMAT, id: property, namespace: CX_BYPASS_NAMESPACE },
|
|
1903
2276
|
}, this.report);
|
|
1904
2277
|
for (const [row, text] of cells) {
|
|
1905
2278
|
if (domain === "node") {
|
|
@@ -1924,12 +2297,12 @@ class CxReader {
|
|
|
1924
2297
|
}
|
|
1925
2298
|
if (defaults + mappings + dependencies + tableStyles > 0) {
|
|
1926
2299
|
const parts = [
|
|
1927
|
-
`cyVisualProperties: ${defaults} default(
|
|
2300
|
+
`cyVisualProperties: ${defaults} default${plural(defaults)}, ${mappings} mapping${plural(mappings)}, ${dependencies} dependenc(ies)`,
|
|
1928
2301
|
];
|
|
1929
2302
|
if (tableStyles > 0) {
|
|
1930
|
-
parts.push(`${tableStyles} table style element(
|
|
2303
|
+
parts.push(`${tableStyles} table style element${plural(tableStyles)}`);
|
|
1931
2304
|
}
|
|
1932
|
-
this.report.warning("unsupported", STYLES_NOT_IMPORTED_CODE, `the file's style rules are not applied (${parts.join("; ")}); they are kept in meta.extra.cx
|
|
2305
|
+
this.report.warning("unsupported", STYLES_NOT_IMPORTED_CODE, `the file's style rules are not applied (${parts.join("; ")}); they are kept in meta.extra.cx`, { element: "cyVisualProperties" });
|
|
1933
2306
|
}
|
|
1934
2307
|
}
|
|
1935
2308
|
/**
|
|
@@ -1955,7 +2328,7 @@ class CxReader {
|
|
|
1955
2328
|
continue;
|
|
1956
2329
|
}
|
|
1957
2330
|
const po = refId(value.po);
|
|
1958
|
-
const row = po === null ? undefined : this.nodeRows.get(
|
|
2331
|
+
const row = po === null ? undefined : this.nodeRows.get(po);
|
|
1959
2332
|
if (row === undefined) {
|
|
1960
2333
|
if (po === null || !this.rootNodes.has(po)) {
|
|
1961
2334
|
this.dangle("functionTerms entry");
|
|
@@ -1973,8 +2346,8 @@ class CxReader {
|
|
|
1973
2346
|
}
|
|
1974
2347
|
const node = refId(value.node);
|
|
1975
2348
|
const edge = refId(value.edge);
|
|
1976
|
-
const row = node === null ? undefined : this.nodeRows.get(
|
|
1977
|
-
const edgeRow = edge === null ? undefined : this.edgeRows.get(
|
|
2349
|
+
const row = node === null ? undefined : this.nodeRows.get(node);
|
|
2350
|
+
const edgeRow = edge === null ? undefined : this.edgeRows.get(edge);
|
|
1978
2351
|
if (row === undefined || edgeRow === undefined) {
|
|
1979
2352
|
if (node === null || !this.rootNodes.has(node) || edge === null || !this.rootEdges.has(edge)) {
|
|
1980
2353
|
this.dangle("reifiedEdges entry");
|
|
@@ -2007,16 +2380,30 @@ class CxReader {
|
|
|
2007
2380
|
return;
|
|
2008
2381
|
}
|
|
2009
2382
|
const fields = [...new Set(records.flatMap((r) => Object.keys(r)).filter((k) => k !== "@id"))];
|
|
2383
|
+
const names = new Set(["id"]);
|
|
2010
2384
|
const decls = [
|
|
2011
2385
|
{ name: "id", dtype: "f64", nullable: true },
|
|
2012
|
-
...fields.map((field) =>
|
|
2386
|
+
...fields.map((field) => {
|
|
2387
|
+
const column = uniqueColumnName(field, null, (n) => names.has(n));
|
|
2388
|
+
names.add(column);
|
|
2389
|
+
if (column !== field) {
|
|
2390
|
+
this.report.warning("coercion", COLUMN_RENAMED_CODE, `${name} column "${field}" renamed to "${column}": the table's own id column holds that name`, { element: field });
|
|
2391
|
+
}
|
|
2392
|
+
return { name: column, dtype: "json", nullable: true };
|
|
2393
|
+
}),
|
|
2013
2394
|
];
|
|
2014
2395
|
const table = this.sink.addExtensionTable(name, decls);
|
|
2396
|
+
const precision = (digits) => {
|
|
2397
|
+
this.precision(name, digits);
|
|
2398
|
+
};
|
|
2015
2399
|
for (const record of records) {
|
|
2016
2400
|
const id = refId(record["@id"]);
|
|
2401
|
+
if (typeof id === "string") {
|
|
2402
|
+
precision(id);
|
|
2403
|
+
}
|
|
2017
2404
|
this.sink.addExtensionRow(table, [
|
|
2018
2405
|
id === null ? null : Number(id),
|
|
2019
|
-
...fields.map((field) => (record[field] === undefined ? null : plainJson(record[field]))),
|
|
2406
|
+
...fields.map((field) => (record[field] === undefined ? null : plainJson(record[field], precision))),
|
|
2020
2407
|
]);
|
|
2021
2408
|
}
|
|
2022
2409
|
}
|
|
@@ -2041,7 +2428,7 @@ class CxReader {
|
|
|
2041
2428
|
.map(Number);
|
|
2042
2429
|
for (const raw of targets) {
|
|
2043
2430
|
const target = refId(raw);
|
|
2044
|
-
const row = target === null ? undefined : rows.get(
|
|
2431
|
+
const row = target === null ? undefined : rows.get(target);
|
|
2045
2432
|
if (row === undefined) {
|
|
2046
2433
|
if (target === null || !root.has(target)) {
|
|
2047
2434
|
this.dangle(`${aspectName} entry`);
|
|
@@ -2101,12 +2488,19 @@ class CxReader {
|
|
|
2101
2488
|
};
|
|
2102
2489
|
const extra = Object.fromEntries(this.doc.kept);
|
|
2103
2490
|
const groups = aspect(this.doc, "cyGroups").map((h) => plainJson(h.value));
|
|
2491
|
+
const own = {};
|
|
2104
2492
|
if (groups.length > 0) {
|
|
2105
|
-
|
|
2493
|
+
own.groups = groups;
|
|
2106
2494
|
}
|
|
2107
2495
|
if (this.plan.subnetwork !== null) {
|
|
2108
|
-
|
|
2496
|
+
own.subnetwork = this.plan.subnetwork;
|
|
2497
|
+
}
|
|
2498
|
+
for (const key of OWN_EXTRA_KEYS) {
|
|
2499
|
+
if (key in own && this.doc.kept.has(key)) {
|
|
2500
|
+
this.report.warning("unsupported", UNKNOWN_ELEMENT_CODE, `the aspect "${key}" is not kept: meta.extra.cx.${key} holds the importer's own ${key === "groups" ? "cyGroups" : "subnetwork"} data`, { element: key });
|
|
2501
|
+
}
|
|
2109
2502
|
}
|
|
2503
|
+
Object.assign(extra, own);
|
|
2110
2504
|
const { weightFrom } = this.options;
|
|
2111
2505
|
const name = this.plan.name ?? describe("name");
|
|
2112
2506
|
const description = describe("description");
|
|
@@ -2139,7 +2533,7 @@ async function run(input, options, sinkFor, pick) {
|
|
|
2139
2533
|
const first = new ImportReportBuilder(CX_FORMAT, resolved.errorLimit);
|
|
2140
2534
|
reportUnusedOptions(options, first, USED_OPTIONS);
|
|
2141
2535
|
const doc = await readDocument(input, first, resolved);
|
|
2142
|
-
const plans = graphPlans(doc);
|
|
2536
|
+
const plans = graphPlans(doc, first);
|
|
2143
2537
|
const chosen = pick(plans, first);
|
|
2144
2538
|
const reports = [];
|
|
2145
2539
|
chosen.forEach((index, k) => {
|
|
@@ -2153,10 +2547,12 @@ async function run(input, options, sinkFor, pick) {
|
|
|
2153
2547
|
return reports;
|
|
2154
2548
|
}
|
|
2155
2549
|
/**
|
|
2156
|
-
* The CX version 1 importer plugin
|
|
2550
|
+
* The CX version 1 importer plugin.
|
|
2551
|
+
* @category Built-in formats
|
|
2157
2552
|
*/
|
|
2158
2553
|
export const cxImporter = Object.freeze({
|
|
2159
2554
|
format: CX_FORMAT,
|
|
2555
|
+
options: Object.freeze(["zAs"]),
|
|
2160
2556
|
extensions: Object.freeze([".cx"]),
|
|
2161
2557
|
mimeTypes: Object.freeze(["application/json"]),
|
|
2162
2558
|
/**
|
|
@@ -2167,8 +2563,7 @@ export const cxImporter = Object.freeze({
|
|
|
2167
2563
|
* @returns the confidence
|
|
2168
2564
|
*/
|
|
2169
2565
|
sniff(head) {
|
|
2170
|
-
const
|
|
2171
|
-
const match = /^\s*\[\s*\{\s*"((?:[^"\\]|\\.)*)"\s*:/.exec(text);
|
|
2566
|
+
const match = /^\s*\[\s*\{\s*"((?:[^"\\]|\\.)*)"\s*:/.exec(headText(head));
|
|
2172
2567
|
if (match === null) {
|
|
2173
2568
|
return 0;
|
|
2174
2569
|
}
|
|
@@ -2188,8 +2583,8 @@ export const cxImporter = Object.freeze({
|
|
|
2188
2583
|
async import(input, sink, options) {
|
|
2189
2584
|
const [report] = await run(input, options, () => sink, (plans, first) => {
|
|
2190
2585
|
const index = chooseGraph(plans.map((p) => p.name), options, first);
|
|
2191
|
-
if (plans.length > 1) {
|
|
2192
|
-
first.warning("unsupported", MULTIPLE_GRAPHS_CODE, `the collection holds ${plans.length} subnetworks; graph ${index} is read and ${plans.length - 1} skipped (
|
|
2586
|
+
if (plans.length > 1 && !graphChosen(options)) {
|
|
2587
|
+
first.warning("unsupported", MULTIPLE_GRAPHS_CODE, `the collection holds ${plans.length} subnetworks; graph ${index} is read and ${plans.length - 1} skipped (importAllGraphs() reads every one)`);
|
|
2193
2588
|
}
|
|
2194
2589
|
return [index];
|
|
2195
2590
|
});
|
|
@@ -2218,7 +2613,7 @@ export const cxImporter = Object.freeze({
|
|
|
2218
2613
|
const doc = await readDocument(input, report, resolved);
|
|
2219
2614
|
const nodes = aspect(doc, "nodes").length;
|
|
2220
2615
|
const edges = aspect(doc, "edges").length;
|
|
2221
|
-
return graphPlans(doc).map((plan) => Object.freeze({
|
|
2616
|
+
return graphPlans(doc, report).map((plan) => Object.freeze({
|
|
2222
2617
|
index: plan.index,
|
|
2223
2618
|
name: plan.name,
|
|
2224
2619
|
nodes: memberCount(plan.nodes, nodes),
|