@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
|
@@ -9,30 +9,37 @@
|
|
|
9
9
|
* groups, nested-network pointers); the subnetwork's CyCSV tables give its columns (LOCAL_ATTRS as
|
|
10
10
|
* they are, the SHARED_ATTRS columns `cytables.xml` joins in, HIDDEN and app tables as hidden
|
|
11
11
|
* columns under their namespace); its first view gives positions (y flipped) and the `graphics`
|
|
12
|
-
* column, further views `position@<n>` columns.
|
|
12
|
+
* column, further views `position@<n>` columns. A table's `name` is the label (what Cytoscape
|
|
13
|
+
* shows), in a label column named `name`, and ids an exporter mangled are restored from the node
|
|
14
|
+
* table's `graphty:originalId` (restoreMangledIds). 2.x: one full XGMML file per network, with
|
|
13
15
|
* selection and hidden state from `cysession.xml`. Everything is resolved by the XGMML emitter, so
|
|
14
16
|
* both readers share every column rule. Styles are not read (issue #706): W_STYLES_NOT_IMPORTED.
|
|
15
17
|
*/
|
|
16
18
|
import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
|
|
17
19
|
import { declareResolved } from "../../common/attributes.js";
|
|
18
20
|
import { IdCoercer } from "../../common/ids.js";
|
|
19
|
-
import { decodeEntryName,
|
|
20
|
-
import { chooseGraph, reportSinkOptions, reportUnusedOptions, resolveImportOptions, } from "../../common/options.js";
|
|
21
|
+
import { decodeEntryName, throwIfAborted } from "../../common/input.js";
|
|
22
|
+
import { chooseGraph, graphChosen, reportSinkOptions, reportUnusedOptions, resolveImportOptions, } from "../../common/options.js";
|
|
23
|
+
import { agree, elementCount, plural } from "../../common/plural.js";
|
|
21
24
|
import { ImportReportBuilder } from "../../common/report.js";
|
|
22
25
|
import { startsLikeZip } from "../../common/zip.js";
|
|
23
26
|
import { ImportError, } from "../../types.js";
|
|
27
|
+
import { ORIGINAL_ID_ATTRIBUTE } from "../graphml/constants.js";
|
|
24
28
|
import { XGMML_ISSUE, XGMML_ORIGIN_NAMESPACE } from "../xgmml/constants.js";
|
|
25
29
|
import { isCyTrue, } from "../xgmml/document.js";
|
|
26
|
-
import { dialectOf, membersOf, XgmmlEmitter, } from "../xgmml/emit.js";
|
|
30
|
+
import { dialectOf, membersOf, parseCoordinate, XgmmlEmitter, } from "../xgmml/emit.js";
|
|
27
31
|
import { parseXgmml, resolveSettings } from "../xgmml/importer.js";
|
|
28
32
|
import { CYS_ISSUE, CYTOSCAPE_NAMESPACE, DEFAULT_MAX_UNCOMPRESSED, EXTENSIONS, FORMAT, HIDDEN_COLUMN, META_KEY, MIME_TYPES, SELECTED_COLUMN, VIEW_POSITION_PREFIX, } from "./constants.js";
|
|
29
|
-
import { elementsNamed, listed, openSession, readXmlTree, urlDecode, } from "./session.js";
|
|
33
|
+
import { elementsNamed, entryLabel, listed, openSession, parseXmlTree, readXmlTree, urlDecode, } from "./session.js";
|
|
30
34
|
import { cellAtt, readCyTable, virtualColumnsOf } from "./tables.js";
|
|
31
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* The per-format defaults: SUIDs as written, edges directed unless they say otherwise, the edge
|
|
37
|
+
* `weight` column as the weight (as for XGMML, the format of the session's networks).
|
|
38
|
+
*/
|
|
32
39
|
const FORMAT_DEFAULTS = {
|
|
33
40
|
ids: "keep",
|
|
34
41
|
defaultDirected: true,
|
|
35
|
-
weightFrom:
|
|
42
|
+
weightFrom: "weight",
|
|
36
43
|
addMissingNodes: false,
|
|
37
44
|
};
|
|
38
45
|
/** The common options the session importer reads. */
|
|
@@ -46,6 +53,7 @@ const USED_OPTIONS = new Set([
|
|
|
46
53
|
"weightFrom",
|
|
47
54
|
"weightDtype",
|
|
48
55
|
"long",
|
|
56
|
+
"restoreMangledIds",
|
|
49
57
|
"errorLimit",
|
|
50
58
|
"signal",
|
|
51
59
|
"onProgress",
|
|
@@ -101,24 +109,65 @@ async function prepare(input, sink, options) {
|
|
|
101
109
|
* @returns the document and its dialect
|
|
102
110
|
*/
|
|
103
111
|
async function parseEntry(session, entry, report, inner) {
|
|
104
|
-
|
|
105
|
-
|
|
112
|
+
return (await parseOptionalEntry(session, entry, report, inner, false));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Read an XGMML entry, its issues and counts relayed into the import's report (the entry name
|
|
116
|
+
* in each message), parsing within what is left of the import's error limit. A fatal issue fails
|
|
117
|
+
* the import, unless the entry is optional (a view): then it is recorded as an error and the
|
|
118
|
+
* entry is skipped (null).
|
|
119
|
+
* @param session - the session
|
|
120
|
+
* @param entry - the entry
|
|
121
|
+
* @param report - the import's report
|
|
122
|
+
* @param inner - the options entries are read with
|
|
123
|
+
* @param optional - whether the import can go on without the entry
|
|
124
|
+
* @returns the document and its dialect, or null for an optional entry that cannot be read
|
|
125
|
+
*/
|
|
126
|
+
async function parseOptionalEntry(session, entry, report, inner, optional) {
|
|
127
|
+
const scratch = new ImportReportBuilder(FORMAT, Math.max(0, report.errorLimit - report.errorCount));
|
|
106
128
|
try {
|
|
129
|
+
// a damaged entry (bad CRC, truncated deflate) fails the scratch report like a parse error
|
|
130
|
+
const bytes = await session.read(entry, scratch);
|
|
107
131
|
const doc = await parseXgmml(bytes, scratch, inner, undefined);
|
|
108
132
|
const dialect = dialectOf(doc, scratch);
|
|
109
133
|
relay(scratch.issues, report, entry.name);
|
|
134
|
+
addCounts(scratch, report);
|
|
110
135
|
return { doc, dialect };
|
|
111
136
|
}
|
|
112
137
|
catch (err) {
|
|
113
138
|
if (!(err instanceof ImportError)) {
|
|
114
139
|
throw err;
|
|
115
140
|
}
|
|
141
|
+
addCounts(scratch, report);
|
|
116
142
|
const { issues } = scratch;
|
|
143
|
+
if (scratch.truncated) {
|
|
144
|
+
// the entry reached the import's error limit: relaying its errors aborts the import
|
|
145
|
+
relay(issues, report, entry.name);
|
|
146
|
+
}
|
|
117
147
|
const fatal = issues.at(-1);
|
|
118
148
|
relay(issues.slice(0, -1), report, entry.name);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
149
|
+
const message = `${entryLabel(entry.name)}: ${fatal?.message ?? err.message}`;
|
|
150
|
+
const where = { line: fatal?.line ?? null };
|
|
151
|
+
if (fatal?.code === CYS_ISSUE.TOO_LARGE) {
|
|
152
|
+
// the byte budget is a limit of the whole import, not damage to one entry
|
|
153
|
+
report.error("unsupported", fatal.code, message);
|
|
154
|
+
throw report.abort(message, { code: fatal.code });
|
|
155
|
+
}
|
|
156
|
+
if (optional) {
|
|
157
|
+
report.error("parse-error", fatal?.code ?? CYS_ISSUE.CORRUPT, `${message}; the entry is not read`, where);
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
return report.fail(fatal?.code ?? CYS_ISSUE.CORRUPT, message, where);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Add an entry's element counts (the nodes skipped while parsing it) to the import's.
|
|
165
|
+
* @param from - the entry's report
|
|
166
|
+
* @param to - the import's report
|
|
167
|
+
*/
|
|
168
|
+
function addCounts(from, to) {
|
|
169
|
+
for (const key of Object.keys(from.counts)) {
|
|
170
|
+
to.counts[key] += from.counts[key];
|
|
122
171
|
}
|
|
123
172
|
}
|
|
124
173
|
/**
|
|
@@ -131,10 +180,10 @@ function relay(issues, report, entry) {
|
|
|
131
180
|
for (const issue of issues) {
|
|
132
181
|
const where = { line: issue.line, element: issue.element };
|
|
133
182
|
if (issue.severity === "error") {
|
|
134
|
-
report.error(issue.category, issue.code, `${entry}: ${issue.message}`, where);
|
|
183
|
+
report.error(issue.category, issue.code, `${entryLabel(entry)}: ${issue.message}`, where);
|
|
135
184
|
}
|
|
136
185
|
else {
|
|
137
|
-
report.warning(issue.category, issue.code, `${entry}: ${issue.message}`, where);
|
|
186
|
+
report.warning(issue.category, issue.code, `${entryLabel(entry)}: ${issue.message}`, where);
|
|
138
187
|
}
|
|
139
188
|
}
|
|
140
189
|
}
|
|
@@ -152,13 +201,22 @@ async function networks3(session, report, inner, docs) {
|
|
|
152
201
|
const choices = [];
|
|
153
202
|
for (const network of layout.networks) {
|
|
154
203
|
const parsed = await parseEntry(session, network, report, inner);
|
|
204
|
+
if (parsed.dialect.view) {
|
|
205
|
+
report.fail(XGMML_ISSUE.VIEW_DOCUMENT, `${network.name}: this is a session view file (cy:view), not a network file; it holds no topology`, { line: parsed.doc.root.line });
|
|
206
|
+
}
|
|
155
207
|
docs.set(network.name, parsed);
|
|
156
208
|
for (const graph of registeredGraphs(parsed)) {
|
|
209
|
+
if (graph.id === null) {
|
|
210
|
+
const label = graph.label === null ? "" : ` ("${graph.label}")`;
|
|
211
|
+
report.error("missing-value", XGMML_ISSUE.MISSING_ID, `${network.name}: a registered subnetwork${label} has no id; its tables and views cannot be found and it is skipped`, { line: graph.line });
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
157
214
|
const members = membersOf(parsed.doc, graph);
|
|
158
215
|
choices.push({
|
|
159
216
|
era: "3",
|
|
160
217
|
network,
|
|
161
|
-
|
|
218
|
+
graph,
|
|
219
|
+
graphId: graph.id,
|
|
162
220
|
name: graph.label ?? graph.id,
|
|
163
221
|
nodes: new Set(members.nodes).size,
|
|
164
222
|
edges: members.edges.length,
|
|
@@ -171,12 +229,17 @@ async function networks3(session, report, inner, docs) {
|
|
|
171
229
|
!layout.networks.some((n) => n.suid === t.network) &&
|
|
172
230
|
!isGroupTable(t, session, docs));
|
|
173
231
|
if (strayViews.length > 0 || strayTables.length > 0) {
|
|
174
|
-
report.warning("validation-error", CYS_ISSUE.DANGLING_REFERENCE, `${strayViews.length + strayTables.length} view(
|
|
232
|
+
report.warning("validation-error", CYS_ISSUE.DANGLING_REFERENCE, `${strayViews.length + strayTables.length} view${plural(strayViews.length + strayTables.length)} or table${plural(strayViews.length + strayTables.length)} ${agree(strayViews.length + strayTables.length, "belongs", "belong")} to a network the session does not hold and were not read: ${listed([...strayViews, ...strayTables.map((t) => t.name)])}`);
|
|
175
233
|
}
|
|
176
234
|
if (layout.networkList === null) {
|
|
177
235
|
return choices;
|
|
178
236
|
}
|
|
179
|
-
const tree = await
|
|
237
|
+
const tree = await parseXmlTree(await session.read(layout.networkList, report), layout.networkList.name, report, inner);
|
|
238
|
+
if (typeof tree === "string") {
|
|
239
|
+
// the list only orders the networks: without it they keep the order of their entries
|
|
240
|
+
report.warning("unsupported", CYS_ISSUE.ENTRY_SKIPPED, `${layout.networkList.name}: ${tree}; it is not read and the networks are listed in entry order`);
|
|
241
|
+
return choices;
|
|
242
|
+
}
|
|
180
243
|
const order = new Map();
|
|
181
244
|
for (const node of elementsNamed(tree, "network")) {
|
|
182
245
|
const id = node.attrs.get("id");
|
|
@@ -213,7 +276,9 @@ function isGroupTable(table, session, docs) {
|
|
|
213
276
|
* @returns the graphs
|
|
214
277
|
*/
|
|
215
278
|
function registeredGraphs(parsed) {
|
|
216
|
-
|
|
279
|
+
const subnetworks = parsed.doc.root.subgraphs.filter((g) => isCyTrue(g.registered));
|
|
280
|
+
// a registered root with no subnetworks (another tool's network file) is the network itself
|
|
281
|
+
return subnetworks.length === 0 && isCyTrue(parsed.doc.root.registered) ? [parsed.doc.root] : subnetworks;
|
|
217
282
|
}
|
|
218
283
|
/**
|
|
219
284
|
* Import one registered subnetwork of a 3.x session.
|
|
@@ -223,97 +288,72 @@ function registeredGraphs(parsed) {
|
|
|
223
288
|
* @param parsed - its network file, parsed for this import (its records are filled in)
|
|
224
289
|
*/
|
|
225
290
|
async function import3(prepared, choice, sink, parsed) {
|
|
226
|
-
const {
|
|
227
|
-
const
|
|
228
|
-
const { doc, dialect } = parsed;
|
|
229
|
-
const graph = doc.root.subgraphs.find((g) => g.id === choice.graphId);
|
|
230
|
-
const members = membersOf(doc, graph);
|
|
291
|
+
const { graph } = choice;
|
|
292
|
+
const members = membersOf(parsed.doc, graph);
|
|
231
293
|
const nodes = byId(members.nodes);
|
|
232
294
|
const edges = byId(members.edges);
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const entry = layout.tables.find((t) => t.tablePath === path);
|
|
238
|
-
pending =
|
|
239
|
-
entry === undefined
|
|
240
|
-
? Promise.resolve(null)
|
|
241
|
-
: session.read(entry).then((bytes) => readCyTable(bytes, path, entry.name, report, inner));
|
|
242
|
-
cache.set(path, pending);
|
|
243
|
-
}
|
|
244
|
-
return pending;
|
|
245
|
-
};
|
|
246
|
-
const virtuals = layout.cytables === null ? [] : await readVirtuals(session, layout.cytables, report, inner);
|
|
247
|
-
// a row of an element the file declares outside this network (a collapsed group's member, a
|
|
248
|
-
// meta-edge) is Cytoscape's bookkeeping, not a stale row
|
|
249
|
-
const declared = new Set([
|
|
250
|
-
...doc.nodes.flatMap((n) => (n.id === null ? [] : [n.id])),
|
|
251
|
-
...doc.edges.flatMap((e) => (e.id === null ? [] : [e.id])),
|
|
252
|
-
]);
|
|
253
|
-
let unmatched = 0;
|
|
254
|
-
for (const entry of layout.tables.filter((t) => t.network === choice.graphId && t.element !== null)) {
|
|
255
|
-
throwIfAborted(inner.signal);
|
|
256
|
-
const table = await tableOf(entry.tablePath);
|
|
257
|
-
if (table === null) {
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
const local = entry.namespace === "LOCAL_ATTRS";
|
|
261
|
-
const shared = entry.namespace === "SHARED_ATTRS";
|
|
262
|
-
const namespace = local ? null : entry.namespace;
|
|
263
|
-
const hidden = !local && !shared;
|
|
264
|
-
const target = (key) => {
|
|
265
|
-
if (entry.element === "network") {
|
|
266
|
-
return key === choice.graphId ? graph.atts : undefined;
|
|
267
|
-
}
|
|
268
|
-
return (entry.element === "node" ? nodes : edges).get(key)?.atts;
|
|
269
|
-
};
|
|
270
|
-
for (const [key, cells] of table.rows) {
|
|
271
|
-
const atts = target(key);
|
|
272
|
-
if (atts === undefined) {
|
|
273
|
-
if (!declared.has(key)) {
|
|
274
|
-
unmatched++;
|
|
275
|
-
}
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
const line = table.lines.get(key) ?? 0;
|
|
279
|
-
for (let i = 1; i < table.columns.length && i < cells.length; i++) {
|
|
280
|
-
const att = cellAtt(table.columns[i], cells[i], line, namespace, hidden);
|
|
281
|
-
if (att !== null) {
|
|
282
|
-
atts.push(att);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
for (const virtual of await virtualColumnsOf(table, virtuals, tableOf, report)) {
|
|
287
|
-
// a shared column named like a local one is renamed; the local one keeps the name
|
|
288
|
-
const owned = table.columns.some((c) => c.name === virtual.column.name);
|
|
289
|
-
let ns = namespace;
|
|
290
|
-
if (local) {
|
|
291
|
-
ns = owned ? "SHARED_ATTRS" : null;
|
|
292
|
-
}
|
|
293
|
-
for (const [key, text] of virtual.values) {
|
|
294
|
-
const att = cellAtt(virtual.column, text, table.lines.get(key) ?? 0, ns, hidden);
|
|
295
|
-
if (att !== null) {
|
|
296
|
-
target(key)?.push(att);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
295
|
+
// the records are shared by every network of the file: what this import adds is undone after
|
|
296
|
+
const restore = saveRecords(graph, nodes, edges);
|
|
297
|
+
try {
|
|
298
|
+
await fill3(prepared, choice, sink, parsed, nodes, edges);
|
|
300
299
|
}
|
|
301
|
-
|
|
302
|
-
|
|
300
|
+
finally {
|
|
301
|
+
restore();
|
|
303
302
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* The state of the records an import fills in (atts, coordinates, graphics), and a function that
|
|
306
|
+
* puts it back.
|
|
307
|
+
* @param graph - the subnetwork
|
|
308
|
+
* @param nodes - its node records
|
|
309
|
+
* @param edges - its edge records
|
|
310
|
+
* @returns the restore function
|
|
311
|
+
*/
|
|
312
|
+
function saveRecords(graph, nodes, edges) {
|
|
313
|
+
const saved = [...nodes.values()].map((n) => ({
|
|
314
|
+
n,
|
|
315
|
+
atts: n.atts.length,
|
|
316
|
+
x: n.x,
|
|
317
|
+
y: n.y,
|
|
318
|
+
z: n.z,
|
|
319
|
+
graphics: n.graphics,
|
|
320
|
+
}));
|
|
321
|
+
const savedEdges = [...edges.values()].map((e) => ({ e, atts: e.atts.length, graphics: e.graphics }));
|
|
322
|
+
const graphAtts = graph.atts.length;
|
|
323
|
+
const graphGraphics = graph.graphics;
|
|
324
|
+
return () => {
|
|
325
|
+
for (const { n, atts, x, y, z, graphics } of saved) {
|
|
326
|
+
n.atts.length = atts;
|
|
327
|
+
Object.assign(n, { x, y, z, graphics });
|
|
312
328
|
}
|
|
313
|
-
|
|
314
|
-
|
|
329
|
+
for (const { e, atts, graphics } of savedEdges) {
|
|
330
|
+
e.atts.length = atts;
|
|
331
|
+
e.graphics = graphics;
|
|
315
332
|
}
|
|
333
|
+
graph.atts.length = graphAtts;
|
|
334
|
+
graph.graphics = graphGraphics;
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Fill one subnetwork's records from its tables and views and emit it.
|
|
339
|
+
* @param prepared - the prepared import
|
|
340
|
+
* @param choice - the subnetwork
|
|
341
|
+
* @param sink - the sink
|
|
342
|
+
* @param parsed - its network file
|
|
343
|
+
* @param nodes - its node records by id
|
|
344
|
+
* @param edges - its edge records by id
|
|
345
|
+
*/
|
|
346
|
+
async function fill3(prepared, choice, sink, parsed, nodes, edges) {
|
|
347
|
+
const { report, inner } = prepared;
|
|
348
|
+
const { doc, dialect } = parsed;
|
|
349
|
+
const { graph } = choice;
|
|
350
|
+
const members = membersOf(doc, graph);
|
|
351
|
+
const unmatched = await applyTables(prepared, choice, doc, nodes, edges);
|
|
352
|
+
if (unmatched > 0) {
|
|
353
|
+
report.warning("parse-error", CYS_ISSUE.TABLE_ROW, `${unmatched} table row${plural(unmatched)} of network ${choice.graphId} ${agree(unmatched, "names", "name")} no node, edge or network in it (left over from deleted elements) and ${agree(unmatched, "is", "are")} not read`);
|
|
316
354
|
}
|
|
355
|
+
const restoredIds = inner.restoreMangledIds ? restoreIds(members.nodes) : new Map();
|
|
356
|
+
const { visualStyle, extraViews, extraIds } = await readViews(prepared, choice, nodes, edges);
|
|
317
357
|
const cyMeta = sessionMeta(prepared, {
|
|
318
358
|
collection: doc.root.label ?? choice.network.title,
|
|
319
359
|
network: choice.graphId,
|
|
@@ -327,27 +367,212 @@ async function import3(prepared, choice, sink, parsed) {
|
|
|
327
367
|
entry: choice.network.name,
|
|
328
368
|
graphName: localName(graph) ?? choice.name,
|
|
329
369
|
metaExtra: { [META_KEY]: cyMeta },
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
370
|
+
labelFromName: true,
|
|
371
|
+
restoredIds,
|
|
372
|
+
groupNodes: groupNodeIds(members.nodes),
|
|
333
373
|
resolvePointer: pointerResolver(prepared, doc),
|
|
334
374
|
onMissingMember: (group, member) => {
|
|
335
|
-
|
|
336
|
-
if (record === undefined) {
|
|
337
|
-
groups.push({ group, members: [member] });
|
|
338
|
-
}
|
|
339
|
-
else if (!record.members.includes(member)) {
|
|
340
|
-
record.members.push(member);
|
|
341
|
-
}
|
|
375
|
+
addMissingMember(groups, group, member);
|
|
342
376
|
},
|
|
343
377
|
};
|
|
344
378
|
const settings = { labelAliases: false, cytoscapeEscapes: false, zAs: prepared.zAs };
|
|
345
379
|
new XgmmlEmitter(doc, dialect, sink, report, prepared.inner, settings, extras).emit(graph);
|
|
346
380
|
if (groups.length > 0) {
|
|
347
|
-
|
|
381
|
+
const missing = groups.reduce((n, g) => n + g.members.length, 0);
|
|
382
|
+
report.warning("unsupported", CYS_ISSUE.COLLAPSED_GROUP, `${groups.length} collapsed group${plural(groups.length)} ${agree(groups.length, "holds", "hold")} ${missing} member${plural(missing)} that are not in the network; they are listed in meta.extra.cytoscape.groups`);
|
|
348
383
|
}
|
|
349
384
|
reportRootOnly(parsed, report, choice.network.name);
|
|
350
|
-
writeViewPositions(sink, extraViews,
|
|
385
|
+
writeViewPositions(sink, extraViews, extraIds, report, prepared.inner, restoredIds);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* The ids of the nodes Cytoscape marks as groups.
|
|
389
|
+
* @param nodes - the network's node records
|
|
390
|
+
* @returns their ids
|
|
391
|
+
*/
|
|
392
|
+
function groupNodeIds(nodes) {
|
|
393
|
+
return new Set(nodes
|
|
394
|
+
.filter((n) => n.atts.some((a) => a.name === "__isGroup" && isCyTrue(a.value)))
|
|
395
|
+
.flatMap((n) => (n.id === null ? [] : [n.id])));
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Record a collapsed group's member that is not in the network.
|
|
399
|
+
* @param groups - the groups with missing members, added to
|
|
400
|
+
* @param group - the group's id
|
|
401
|
+
* @param member - the member's id
|
|
402
|
+
*/
|
|
403
|
+
function addMissingMember(groups, group, member) {
|
|
404
|
+
const record = groups.find((g) => g.group === group);
|
|
405
|
+
if (record === undefined) {
|
|
406
|
+
groups.push({ group, members: [member] });
|
|
407
|
+
}
|
|
408
|
+
else if (!record.members.includes(member)) {
|
|
409
|
+
record.members.push(member);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Add the cells of a subnetwork's tables to its node, edge and network records.
|
|
414
|
+
* @param prepared - the prepared import
|
|
415
|
+
* @param choice - the subnetwork
|
|
416
|
+
* @param doc - its network file
|
|
417
|
+
* @param nodes - its node records by id
|
|
418
|
+
* @param edges - its edge records by id
|
|
419
|
+
* @returns the number of rows that name no element of the network
|
|
420
|
+
*/
|
|
421
|
+
async function applyTables(prepared, choice, doc, nodes, edges) {
|
|
422
|
+
const { report, inner, session } = prepared;
|
|
423
|
+
const { layout } = session;
|
|
424
|
+
// each network reads its tables into its own report, so a shared table's issues, and a
|
|
425
|
+
// table that fails the import, land in every network that reads it.
|
|
426
|
+
// ponytail: a table shared by several networks is inflated and parsed once per network (the
|
|
427
|
+
// byte budget is charged once); cache the parse and relay its issues if importAll gets slow
|
|
428
|
+
const tables = new Map();
|
|
429
|
+
const tableOf = (path) => {
|
|
430
|
+
let pending = tables.get(path);
|
|
431
|
+
if (pending === undefined) {
|
|
432
|
+
const entry = layout.tables.find((t) => t.tablePath === path);
|
|
433
|
+
pending =
|
|
434
|
+
entry === undefined
|
|
435
|
+
? Promise.resolve(null)
|
|
436
|
+
: session.read(entry, report).then((bytes) => readCyTable(bytes, path, entry.name, report, inner));
|
|
437
|
+
tables.set(path, pending);
|
|
438
|
+
}
|
|
439
|
+
return pending;
|
|
440
|
+
};
|
|
441
|
+
const ctx = {
|
|
442
|
+
choice,
|
|
443
|
+
graph: choice.graph,
|
|
444
|
+
nodes,
|
|
445
|
+
edges,
|
|
446
|
+
// a row of an element the file declares outside this network (a collapsed group's member, a
|
|
447
|
+
// meta-edge) is Cytoscape's bookkeeping, not a stale row
|
|
448
|
+
declared: new Set([
|
|
449
|
+
...doc.nodes.flatMap((n) => (n.id === null ? [] : [n.id])),
|
|
450
|
+
...doc.edges.flatMap((e) => (e.id === null ? [] : [e.id])),
|
|
451
|
+
]),
|
|
452
|
+
virtuals: layout.cytables === null ? [] : await readVirtuals(session, layout.cytables, report, inner),
|
|
453
|
+
tableOf,
|
|
454
|
+
report,
|
|
455
|
+
};
|
|
456
|
+
let unmatched = 0;
|
|
457
|
+
for (const entry of layout.tables.filter((t) => t.network === choice.graphId && t.element !== null)) {
|
|
458
|
+
throwIfAborted(inner.signal);
|
|
459
|
+
const table = await tableOf(entry.tablePath);
|
|
460
|
+
if (table !== null) {
|
|
461
|
+
unmatched += await applyTable(ctx, entry, table);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return unmatched;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Add the cells of one table, and of the shared columns that extend it, to the records.
|
|
468
|
+
* @param ctx - the shared state
|
|
469
|
+
* @param entry - the table's entry
|
|
470
|
+
* @param table - the table
|
|
471
|
+
* @returns the number of rows that name no element of the network
|
|
472
|
+
*/
|
|
473
|
+
async function applyTable(ctx, entry, table) {
|
|
474
|
+
const local = entry.namespace === "LOCAL_ATTRS";
|
|
475
|
+
const namespace = local ? null : entry.namespace;
|
|
476
|
+
const hidden = !local && entry.namespace !== "SHARED_ATTRS";
|
|
477
|
+
const target = (key) => {
|
|
478
|
+
if (entry.element === "network") {
|
|
479
|
+
return key === ctx.choice.graphId ? ctx.graph.atts : undefined;
|
|
480
|
+
}
|
|
481
|
+
return (entry.element === "node" ? ctx.nodes : ctx.edges).get(key)?.atts;
|
|
482
|
+
};
|
|
483
|
+
let unmatched = 0;
|
|
484
|
+
for (const [key, cells] of table.rows) {
|
|
485
|
+
const atts = target(key);
|
|
486
|
+
if (atts !== undefined) {
|
|
487
|
+
atts.push(...rowAtts(table, key, cells, namespace, hidden));
|
|
488
|
+
}
|
|
489
|
+
else if (!ctx.declared.has(key)) {
|
|
490
|
+
unmatched++;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
for (const virtual of await virtualColumnsOf(table, ctx.virtuals, ctx.tableOf, ctx.report)) {
|
|
494
|
+
// a shared column named like a local one is renamed; the local one keeps the name
|
|
495
|
+
const owned = table.columns.some((c) => c.name === virtual.column.name);
|
|
496
|
+
const ns = local ? sharedNamespace(owned) : namespace;
|
|
497
|
+
for (const [key, text] of virtual.values) {
|
|
498
|
+
const atts = target(key);
|
|
499
|
+
if (atts !== undefined) {
|
|
500
|
+
pushAtt(atts, cellAtt(virtual.column, text, table.lines.get(key) ?? 0, ns, hidden));
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return unmatched;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* The attributes of one table row's cells (the first cell is the SUID key).
|
|
508
|
+
* @param table - the table
|
|
509
|
+
* @param key - the row's key
|
|
510
|
+
* @param cells - the row's cells
|
|
511
|
+
* @param namespace - the namespace the attributes are written in, or null
|
|
512
|
+
* @param hidden - whether the table is one of Cytoscape's hidden ones
|
|
513
|
+
* @returns the attributes of the cells that hold a value
|
|
514
|
+
*/
|
|
515
|
+
function rowAtts(table, key, cells, namespace, hidden) {
|
|
516
|
+
const line = table.lines.get(key) ?? 0;
|
|
517
|
+
const out = [];
|
|
518
|
+
for (let i = 1; i < table.columns.length && i < cells.length; i++) {
|
|
519
|
+
pushAtt(out, cellAtt(table.columns[i], cells[i], line, namespace, hidden));
|
|
520
|
+
}
|
|
521
|
+
return out;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* The namespace of a shared column in a local table: renamed when a local column has its name.
|
|
525
|
+
* @param owned - whether a local column has the shared column's name
|
|
526
|
+
* @returns "SHARED_ATTRS", or null for none
|
|
527
|
+
*/
|
|
528
|
+
function sharedNamespace(owned) {
|
|
529
|
+
return owned ? "SHARED_ATTRS" : null;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Add an attribute to a record's list, unless there is none.
|
|
533
|
+
* @param atts - the record's attributes
|
|
534
|
+
* @param att - the attribute, or null
|
|
535
|
+
*/
|
|
536
|
+
function pushAtt(atts, att) {
|
|
537
|
+
if (att !== null) {
|
|
538
|
+
atts.push(att);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Read a subnetwork's views: the first (the lowest view SUID, whatever the order of the entries)
|
|
543
|
+
* fills the records' coordinates and graphics, each further one gives positions only. A view that
|
|
544
|
+
* cannot be read is recorded and skipped.
|
|
545
|
+
* @param prepared - the prepared import
|
|
546
|
+
* @param choice - the subnetwork
|
|
547
|
+
* @param nodes - its node records by id
|
|
548
|
+
* @param edges - its edge records by id
|
|
549
|
+
* @returns the first view's style, and the positions and SUIDs of the further views
|
|
550
|
+
*/
|
|
551
|
+
async function readViews(prepared, choice, nodes, edges) {
|
|
552
|
+
const { report, inner, session } = prepared;
|
|
553
|
+
const views = session.layout.views
|
|
554
|
+
.filter((v) => v.network === choice.graphId)
|
|
555
|
+
.sort((a, b) => a.view.length - b.view.length || (a.view < b.view ? -1 : Number(a.view > b.view)));
|
|
556
|
+
let visualStyle = null;
|
|
557
|
+
const extraViews = [];
|
|
558
|
+
const extraIds = [];
|
|
559
|
+
let first = true;
|
|
560
|
+
for (const view of views) {
|
|
561
|
+
const viewDoc = (await parseOptionalEntry(session, view, report, inner, true))?.doc;
|
|
562
|
+
if (viewDoc === undefined) {
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (first) {
|
|
566
|
+
first = false;
|
|
567
|
+
visualStyle = viewDoc.root.attrs.get("cy:visualStyle") ?? null;
|
|
568
|
+
applyView(viewDoc, choice.graph, nodes, edges, report, view.name);
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
extraViews.push(viewPositions(viewDoc, prepared.zAs, nodes, report, view.name));
|
|
572
|
+
extraIds.push(view.view);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return { visualStyle, extraViews, extraIds };
|
|
351
576
|
}
|
|
352
577
|
/**
|
|
353
578
|
* The virtual columns of `tables/cytables.xml`.
|
|
@@ -358,9 +583,20 @@ async function import3(prepared, choice, sink, parsed) {
|
|
|
358
583
|
* @returns the virtual columns in document order
|
|
359
584
|
*/
|
|
360
585
|
async function readVirtuals(session, entry, report, inner) {
|
|
361
|
-
const tree = await
|
|
586
|
+
const tree = await parseXmlTree(await session.read(entry, report), entry.name, report, inner);
|
|
587
|
+
if (typeof tree === "string") {
|
|
588
|
+
// only the shared columns are lost: every table's own columns are still read
|
|
589
|
+
report.error("parse-error", CYS_ISSUE.TABLE, `${entryLabel(entry.name)}: ${tree}; its virtual columns are not read`);
|
|
590
|
+
return [];
|
|
591
|
+
}
|
|
362
592
|
const out = [];
|
|
363
593
|
for (const node of elementsNamed(tree, "virtualColumn")) {
|
|
594
|
+
const missing = ["name", "targetTable", "sourceTable", "sourceColumn"].filter((k) => !node.attrs.has(k));
|
|
595
|
+
if (missing.length > 0) {
|
|
596
|
+
const named = node.attrs.has("name") ? ` "${node.attrs.get("name") ?? ""}"` : "";
|
|
597
|
+
report.error("parse-error", CYS_ISSUE.TABLE, `${entryLabel(entry.name)}: a virtual column${named} has no ${missing.join(", ")}; the column is not read`);
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
364
600
|
const a = (key) => node.attrs.get(key) ?? "";
|
|
365
601
|
out.push({
|
|
366
602
|
name: a("name"),
|
|
@@ -400,12 +636,14 @@ function byId(records) {
|
|
|
400
636
|
*/
|
|
401
637
|
function applyView(view, graph, nodes, edges, report, entry) {
|
|
402
638
|
let dangling = 0;
|
|
639
|
+
const seen = new Set();
|
|
403
640
|
for (const node of view.nodes) {
|
|
404
641
|
const target = node.viewId === null ? undefined : nodes.get(node.viewId);
|
|
405
642
|
if (target === undefined) {
|
|
406
643
|
dangling++;
|
|
407
644
|
continue;
|
|
408
645
|
}
|
|
646
|
+
duplicateViewNode(seen, node, report, entry);
|
|
409
647
|
target.x = node.x;
|
|
410
648
|
target.y = node.y;
|
|
411
649
|
target.z = node.z;
|
|
@@ -427,26 +665,71 @@ function applyView(view, graph, nodes, edges, report, entry) {
|
|
|
427
665
|
graph.graphics = { ...(graph.graphics ?? {}), ...view.root.graphics };
|
|
428
666
|
}
|
|
429
667
|
if (dangling > 0) {
|
|
430
|
-
report.warning("validation-error", CYS_ISSUE.DANGLING_REFERENCE, `${entry}: ${dangling} view element(
|
|
668
|
+
report.warning("validation-error", CYS_ISSUE.DANGLING_REFERENCE, `${entryLabel(entry)}: ${dangling} view element${plural(dangling)} ${agree(dangling, "names", "name")} no node or edge of the network; they are not read`);
|
|
431
669
|
}
|
|
432
670
|
}
|
|
433
671
|
/**
|
|
434
|
-
*
|
|
435
|
-
*
|
|
672
|
+
* W_DUPLICATE_NODE for a view element that repeats a node an earlier one of the view gave.
|
|
673
|
+
* @param seen - the model node ids seen so far in the view
|
|
674
|
+
* @param node - the view element
|
|
675
|
+
* @param report - the report
|
|
676
|
+
* @param entry - the view entry name
|
|
677
|
+
*/
|
|
678
|
+
function duplicateViewNode(seen, node, report, entry) {
|
|
679
|
+
const id = node.viewId;
|
|
680
|
+
if (seen.has(id)) {
|
|
681
|
+
report.warning("validation-error", XGMML_ISSUE.DUPLICATE_NODE, `${entryLabel(entry)}: the view gives node "${id}" twice; the later element is used`, { line: node.line, element: id });
|
|
682
|
+
}
|
|
683
|
+
seen.add(id);
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* The positions of a further view, by model node id, read by the first view's rules: y flipped
|
|
687
|
+
* to y-up, z in the position only under zAs "position", a coordinate that does not parse
|
|
688
|
+
* E_BAD_VALUE (the position is unset; a bad z is 0), an element naming no node of the network
|
|
689
|
+
* counted in one W_DANGLING_REFERENCE.
|
|
436
690
|
* @param view - the view document
|
|
437
691
|
* @param zAs - where z goes
|
|
692
|
+
* @param nodes - the network's node records by id
|
|
693
|
+
* @param report - the report
|
|
694
|
+
* @param entry - the view entry name
|
|
438
695
|
* @returns the positions
|
|
439
696
|
*/
|
|
440
|
-
function viewPositions(view, zAs) {
|
|
697
|
+
function viewPositions(view, zAs, nodes, report, entry) {
|
|
441
698
|
const out = new Map();
|
|
699
|
+
const seen = new Set();
|
|
700
|
+
let dangling = 0;
|
|
701
|
+
const coordinate = (text, node) => {
|
|
702
|
+
if (text === null) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
const n = parseCoordinate(text);
|
|
706
|
+
if (n === null) {
|
|
707
|
+
report.error("validation-error", XGMML_ISSUE.BAD_VALUE, `${entryLabel(entry)}: graphics coordinate "${text}" is not a number`, {
|
|
708
|
+
line: node.line,
|
|
709
|
+
element: node.viewId,
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
return n;
|
|
713
|
+
};
|
|
442
714
|
for (const node of view.nodes) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
715
|
+
if (node.viewId === null || !nodes.has(node.viewId)) {
|
|
716
|
+
dangling++;
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
duplicateViewNode(seen, node, report, entry);
|
|
720
|
+
const x = coordinate(node.x, node);
|
|
721
|
+
const y = coordinate(node.y, node);
|
|
722
|
+
const z = zAs === "position" ? coordinate(node.z, node) : null;
|
|
723
|
+
if (x !== null && y !== null) {
|
|
724
|
+
out.set(node.viewId, [x, y === 0 ? 0 : -y, z ?? 0]);
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
out.delete(node.viewId);
|
|
448
728
|
}
|
|
449
729
|
}
|
|
730
|
+
if (dangling > 0) {
|
|
731
|
+
report.warning("validation-error", CYS_ISSUE.DANGLING_REFERENCE, `${entryLabel(entry)}: ${dangling} view element${plural(dangling)} ${agree(dangling, "names", "name")} no node of the network; they are not read`);
|
|
732
|
+
}
|
|
450
733
|
return out;
|
|
451
734
|
}
|
|
452
735
|
/**
|
|
@@ -456,8 +739,9 @@ function viewPositions(view, zAs) {
|
|
|
456
739
|
* @param viewIds - each view's SUID
|
|
457
740
|
* @param report - the report
|
|
458
741
|
* @param common - the id rule
|
|
742
|
+
* @param restoredIds - the original ids of mangled nodes, by written id
|
|
459
743
|
*/
|
|
460
|
-
function writeViewPositions(sink, views, viewIds, report, common) {
|
|
744
|
+
function writeViewPositions(sink, views, viewIds, report, common, restoredIds) {
|
|
461
745
|
const coercer = new IdCoercer(common.ids);
|
|
462
746
|
views.forEach((positions, i) => {
|
|
463
747
|
const name = `${VIEW_POSITION_PREFIX}${i + 2}`;
|
|
@@ -478,7 +762,7 @@ function writeViewPositions(sink, views, viewIds, report, common) {
|
|
|
478
762
|
for (const [id, xyz] of positions) {
|
|
479
763
|
let index;
|
|
480
764
|
try {
|
|
481
|
-
index = sink.indexOf(coercer.text(id));
|
|
765
|
+
index = sink.indexOf(coercer.text(restoredIds.get(id) ?? id));
|
|
482
766
|
}
|
|
483
767
|
catch {
|
|
484
768
|
continue; // an id the id rule refuses was refused, and recorded, as a node already
|
|
@@ -489,6 +773,26 @@ function writeViewPositions(sink, views, viewIds, report, common) {
|
|
|
489
773
|
}
|
|
490
774
|
});
|
|
491
775
|
}
|
|
776
|
+
/**
|
|
777
|
+
* The original ids an exporter's `sanitizeIds: "mangle"` kept in the node table's
|
|
778
|
+
* `graphty:originalId` column; each such cell is taken out of the node's columns.
|
|
779
|
+
* @param nodes - the node records, table cells merged in
|
|
780
|
+
* @returns the original id by written id
|
|
781
|
+
*/
|
|
782
|
+
function restoreIds(nodes) {
|
|
783
|
+
const out = new Map();
|
|
784
|
+
for (const node of nodes) {
|
|
785
|
+
const at = node.atts.findIndex((a) => a.name === ORIGINAL_ID_ATTRIBUTE &&
|
|
786
|
+
(a.namespace ?? null) === null &&
|
|
787
|
+
a.value !== null &&
|
|
788
|
+
a.children.length === 0);
|
|
789
|
+
if (node.id !== null && at >= 0) {
|
|
790
|
+
out.set(node.id, node.atts[at].value);
|
|
791
|
+
node.atts.splice(at, 1);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return out;
|
|
795
|
+
}
|
|
492
796
|
/**
|
|
493
797
|
* The network's name from its own table (the LOCAL `name` att), else null.
|
|
494
798
|
* @param graph - the subnetwork with its table atts
|
|
@@ -556,7 +860,7 @@ function reportRootOnly(parsed, report, entry) {
|
|
|
556
860
|
const nodes = parsed.doc.nodes.filter((n) => !held.has(n)).length;
|
|
557
861
|
const edges = parsed.doc.edges.filter((e) => !held.has(e)).length;
|
|
558
862
|
if (nodes + edges > 0) {
|
|
559
|
-
report.warning("unsupported", XGMML_ISSUE.ROOT_ONLY_ELEMENTS, `${entry}: ${nodes}
|
|
863
|
+
report.warning("unsupported", XGMML_ISSUE.ROOT_ONLY_ELEMENTS, `${entryLabel(entry)}: ${elementCount(nodes, edges)} ${agree(nodes + edges, "belongs", "belong")} to none of the session's networks (group meta-edges or collapsed group members) and ${agree(nodes + edges, "was", "were")} not read`);
|
|
560
864
|
}
|
|
561
865
|
}
|
|
562
866
|
// ---------------------------------------------------------------------------------------- 2.x
|
|
@@ -573,8 +877,11 @@ async function networks2(session, report, inner) {
|
|
|
573
877
|
if (layout.cysession === null) {
|
|
574
878
|
return report.fail(CYS_ISSUE.NOT_SESSION, "the session has no cysession.xml");
|
|
575
879
|
}
|
|
576
|
-
const tree = await readXmlTree(await session.read(layout.cysession), layout.cysession.name, report, inner);
|
|
880
|
+
const tree = await readXmlTree(await session.read(layout.cysession, report), layout.cysession.name, report, inner);
|
|
577
881
|
const documentVersion = tree.attrs.get("documentVersion") ?? "";
|
|
882
|
+
if (documentVersion.length > 0 && !/^\d+(\.\d+)*$/.test(documentVersion.trim())) {
|
|
883
|
+
report.warning("validation-error", XGMML_ISSUE.DOCUMENT_VERSION, `${layout.cysession.name}: documentVersion "${documentVersion}" does not parse; the session is read as Cytoscape 2.x`);
|
|
884
|
+
}
|
|
578
885
|
if (documentVersion.startsWith("3")) {
|
|
579
886
|
report.fail(CYS_ISSUE.VERSION, `cysession.xml has documentVersion ${documentVersion} and no version marker: the 2011 Cytoscape 3.0 pre-release layout, which no released Cytoscape reads`);
|
|
580
887
|
}
|
|
@@ -588,16 +895,28 @@ async function networks2(session, report, inner) {
|
|
|
588
895
|
const missing = [];
|
|
589
896
|
const named = new Set();
|
|
590
897
|
for (const record of elementsNamed(tree, "network")) {
|
|
591
|
-
const
|
|
592
|
-
const filename = record.attrs.get("filename") ?? `${
|
|
593
|
-
if (
|
|
898
|
+
const given = record.attrs.get("id");
|
|
899
|
+
const filename = record.attrs.get("filename") ?? (given === undefined ? undefined : `${given}.xgmml`);
|
|
900
|
+
if (given === "Network Root") {
|
|
594
901
|
continue;
|
|
595
902
|
}
|
|
903
|
+
if (filename === undefined) {
|
|
904
|
+
sessionRecord(report, layout.cysession.name, "a <network> has neither an id nor a filename; it is not read");
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
const id = given ?? urlDecode(filename.replace(/\.xgmml$/i, ""));
|
|
908
|
+
if (given === undefined) {
|
|
909
|
+
sessionRecord(report, layout.cysession.name, `a <network> for ${filename} has no id; it is named "${id}" after its file`);
|
|
910
|
+
}
|
|
596
911
|
const file = byDecoded.get(filename);
|
|
597
912
|
if (file === undefined) {
|
|
598
913
|
missing.push(filename);
|
|
599
914
|
continue;
|
|
600
915
|
}
|
|
916
|
+
if (named.has(file)) {
|
|
917
|
+
sessionRecord(report, layout.cysession.name, `the <network> "${id}" names ${filename}, which an earlier record already names; it is not read a second time`);
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
601
920
|
named.add(file);
|
|
602
921
|
const parent = record.children.find((c) => c.name === "parent")?.attrs.get("id") ?? null;
|
|
603
922
|
choices.push({
|
|
@@ -609,14 +928,24 @@ async function networks2(session, report, inner) {
|
|
|
609
928
|
});
|
|
610
929
|
}
|
|
611
930
|
if (missing.length > 0) {
|
|
612
|
-
report.warning("validation-error", CYS_ISSUE.DANGLING_REFERENCE, `cysession.xml names ${missing.length} network file(
|
|
931
|
+
report.warning("validation-error", CYS_ISSUE.DANGLING_REFERENCE, `cysession.xml names ${missing.length} network file${plural(missing.length)} the session does not hold: ${listed(missing)}`);
|
|
613
932
|
}
|
|
614
933
|
const unnamed = order.filter((e) => !named.has(e)).map((e) => e.name);
|
|
615
934
|
if (unnamed.length > 0) {
|
|
616
|
-
report.warning("unsupported", CYS_ISSUE.ENTRY_SKIPPED, `${unnamed.length} network file(
|
|
935
|
+
report.warning("unsupported", CYS_ISSUE.ENTRY_SKIPPED, `${unnamed.length} network file${plural(unnamed.length)} ${agree(unnamed.length, "is", "are")} not in cysession.xml's network tree and were not read: ${listed(unnamed)}`);
|
|
617
936
|
}
|
|
618
937
|
return choices.sort((a, b) => order.indexOf(a.file) - order.indexOf(b.file));
|
|
619
938
|
}
|
|
939
|
+
/**
|
|
940
|
+
* W_CYS_SESSION_RECORD: a cysession.xml network record without an id, or one naming a file an
|
|
941
|
+
* earlier record names.
|
|
942
|
+
* @param report - the report
|
|
943
|
+
* @param entry - the cysession.xml entry name
|
|
944
|
+
* @param message - what is wrong and what is done
|
|
945
|
+
*/
|
|
946
|
+
function sessionRecord(report, entry, message) {
|
|
947
|
+
report.warning("validation-error", CYS_ISSUE.SESSION_RECORD, `${entryLabel(entry)}: ${message}`);
|
|
948
|
+
}
|
|
620
949
|
/**
|
|
621
950
|
* Import one network of a 2.x session.
|
|
622
951
|
* @param prepared - the prepared import
|
|
@@ -626,17 +955,21 @@ async function networks2(session, report, inner) {
|
|
|
626
955
|
async function import2(prepared, choice, sink) {
|
|
627
956
|
const { report, inner, session } = prepared;
|
|
628
957
|
const { doc, dialect } = await parseEntry(session, choice.file, report, inner);
|
|
958
|
+
let unmatched = 0;
|
|
629
959
|
for (const [list, column] of [
|
|
630
960
|
["selectedNodes", SELECTED_COLUMN],
|
|
631
961
|
["hiddenNodes", HIDDEN_COLUMN],
|
|
632
962
|
]) {
|
|
633
|
-
mark(doc.nodes, idsIn(choice.record, list), column);
|
|
963
|
+
unmatched += mark(doc.nodes, idsIn(choice.record, list), column);
|
|
634
964
|
}
|
|
635
965
|
for (const [list, column] of [
|
|
636
966
|
["selectedEdges", SELECTED_COLUMN],
|
|
637
967
|
["hiddenEdges", HIDDEN_COLUMN],
|
|
638
968
|
]) {
|
|
639
|
-
mark(doc.edges, idsIn(choice.record, list), column);
|
|
969
|
+
unmatched += mark(doc.edges, idsIn(choice.record, list), column);
|
|
970
|
+
}
|
|
971
|
+
if (unmatched > 0) {
|
|
972
|
+
report.warning("validation-error", CYS_ISSUE.DANGLING_REFERENCE, `cysession.xml lists ${unmatched} selected or hidden name${plural(unmatched)} that ${choice.file.name} does not hold; they are not read`);
|
|
640
973
|
}
|
|
641
974
|
const cyMeta = sessionMeta(prepared, {
|
|
642
975
|
collection: null,
|
|
@@ -677,12 +1010,20 @@ function idsIn(record, list) {
|
|
|
677
1010
|
* @param records - the node or edge records
|
|
678
1011
|
* @param ids - the listed names
|
|
679
1012
|
* @param column - the column
|
|
1013
|
+
* @returns how many listed names match no record
|
|
680
1014
|
*/
|
|
681
1015
|
function mark(records, ids, column) {
|
|
682
1016
|
if (ids.size === 0) {
|
|
683
|
-
return;
|
|
1017
|
+
return 0;
|
|
684
1018
|
}
|
|
1019
|
+
const matched = new Set();
|
|
685
1020
|
for (const record of records) {
|
|
1021
|
+
if (record.id !== null && ids.has(record.id)) {
|
|
1022
|
+
matched.add(record.id);
|
|
1023
|
+
}
|
|
1024
|
+
if (record.label !== null && ids.has(record.label)) {
|
|
1025
|
+
matched.add(record.label);
|
|
1026
|
+
}
|
|
686
1027
|
if ((record.id !== null && ids.has(record.id)) || (record.label !== null && ids.has(record.label))) {
|
|
687
1028
|
record.atts.push({
|
|
688
1029
|
name: column,
|
|
@@ -702,6 +1043,7 @@ function mark(records, ids, column) {
|
|
|
702
1043
|
});
|
|
703
1044
|
}
|
|
704
1045
|
}
|
|
1046
|
+
return ids.size - matched.size;
|
|
705
1047
|
}
|
|
706
1048
|
/**
|
|
707
1049
|
* The session facts of one network, and the once-per-import warnings about what is not read
|
|
@@ -718,10 +1060,10 @@ function sessionMeta(prepared, facts) {
|
|
|
718
1060
|
const { layout } = prepared.session;
|
|
719
1061
|
const { report } = prepared;
|
|
720
1062
|
if (layout.styles.length > 0) {
|
|
721
|
-
report.warning("unsupported", CYS_ISSUE.STYLES_NOT_IMPORTED, `the session's styles (${layout.styles.map((s) => s.path).join(", ")}) are not applied
|
|
1063
|
+
report.warning("unsupported", CYS_ISSUE.STYLES_NOT_IMPORTED, `the session's styles (${layout.styles.map((s) => s.path).join(", ")}) are not applied`);
|
|
722
1064
|
}
|
|
723
1065
|
if (layout.skipped.length > 0) {
|
|
724
|
-
report.warning("unsupported", CYS_ISSUE.ENTRY_SKIPPED, `${layout.skipped.length} entries hold no graph data and were not read (apps, global tables, properties, images): ${listed(layout.skipped)}`);
|
|
1066
|
+
report.warning("unsupported", CYS_ISSUE.ENTRY_SKIPPED, `${layout.skipped.length} ${layout.skipped.length === 1 ? "entry holds" : "entries hold"} no graph data and ${agree(layout.skipped.length, "was", "were")} not read (apps, global tables, properties, images): ${listed(layout.skipped)}`);
|
|
725
1067
|
}
|
|
726
1068
|
return {
|
|
727
1069
|
sessionVersion: layout.version,
|
|
@@ -758,10 +1100,12 @@ async function importChoice(prepared, index, sink) {
|
|
|
758
1100
|
async function importCys(input, sink, options) {
|
|
759
1101
|
const prepared = await prepare(input, sink, options);
|
|
760
1102
|
const index = chooseGraph(prepared.choices.map((c) => c.name), options, prepared.report);
|
|
761
|
-
if (prepared.choices.length > 1) {
|
|
762
|
-
prepared.report.warning("unsupported", CYS_ISSUE.MULTIPLE_GRAPHS, `the session holds ${prepared.choices.length} networks;
|
|
1103
|
+
if (prepared.choices.length > 1 && !graphChosen(options)) {
|
|
1104
|
+
prepared.report.warning("unsupported", CYS_ISSUE.MULTIPLE_GRAPHS, `the session holds ${prepared.choices.length} networks; only the first is read (importAllGraphs() reads every one; graphIndex or graphName chooses one)`);
|
|
763
1105
|
}
|
|
764
|
-
|
|
1106
|
+
const report = await importChoice(prepared, index, sink);
|
|
1107
|
+
prepared.session.done();
|
|
1108
|
+
return report;
|
|
765
1109
|
}
|
|
766
1110
|
/**
|
|
767
1111
|
* Import every network of a session, each into its own sink with its own report.
|
|
@@ -771,17 +1115,19 @@ async function importCys(input, sink, options) {
|
|
|
771
1115
|
* @returns one report per network
|
|
772
1116
|
*/
|
|
773
1117
|
async function importAllCys(input, sinkFor, options) {
|
|
774
|
-
|
|
775
|
-
|
|
1118
|
+
// the archive is opened and its network files parsed once: one byte budget, one progress,
|
|
1119
|
+
// and every network's report starts from what reading the session recorded
|
|
1120
|
+
const first = await prepare(input, null, options);
|
|
1121
|
+
const opened = first.report.fork();
|
|
776
1122
|
const reports = [];
|
|
777
1123
|
for (let i = 0; i < first.choices.length; i++) {
|
|
778
1124
|
const sink = sinkFor(i);
|
|
779
|
-
|
|
780
|
-
const prepared = i === 0 ? first : await prepare(bytes, null, options);
|
|
1125
|
+
const prepared = i === 0 ? first : { ...first, report: opened.fork() };
|
|
781
1126
|
reportSinkOptions(sink, options, prepared.report, true);
|
|
782
1127
|
reportUnusedOptions(options, prepared.report, USED_OPTIONS);
|
|
783
1128
|
reports.push(await importChoice(prepared, i, sink));
|
|
784
1129
|
}
|
|
1130
|
+
first.session.done();
|
|
785
1131
|
return reports;
|
|
786
1132
|
}
|
|
787
1133
|
/**
|
|
@@ -799,8 +1145,18 @@ async function listCysGraphs(input, options) {
|
|
|
799
1145
|
edges: choice.era === "3" ? choice.edges : null,
|
|
800
1146
|
}));
|
|
801
1147
|
}
|
|
802
|
-
/**
|
|
803
|
-
|
|
1148
|
+
/**
|
|
1149
|
+
* A session's marker or folder name in the head of the archive, or an entry only a session has
|
|
1150
|
+
* (Cytoscape's app state, a network, view or table file), for a session whose folder was renamed.
|
|
1151
|
+
* The head is decoded whole, so a name can sit anywhere in it.
|
|
1152
|
+
*/
|
|
1153
|
+
const SESSION_NAMES = [
|
|
1154
|
+
/CytoscapeSession|cysession\.xml/,
|
|
1155
|
+
/(?<!\d)\d+\.\d+\.\d+\.version/,
|
|
1156
|
+
/(?:^|\/)apps\/org\.cytoscape\./,
|
|
1157
|
+
/(?:^|\/)(?:networks|views)\/\d+-[^/]*\.xgmml/,
|
|
1158
|
+
/(?:^|\/)tables\/\d+-[^/]*\/[^/]+\.cytable/,
|
|
1159
|
+
];
|
|
804
1160
|
/**
|
|
805
1161
|
* Confidence that a head of bytes is a Cytoscape session: 0.95 for a zip whose head names the
|
|
806
1162
|
* session folder, its version marker or cysession.xml; 0 for any other zip, so an `.xlsx`, a
|
|
@@ -808,18 +1164,23 @@ const SESSION_NAME = /CytoscapeSession|cysession\.xml|\d+\.\d+\.\d+\.version/;
|
|
|
808
1164
|
* start after a short stub (a self-extracting archive) within the head.
|
|
809
1165
|
* @param head - the first bytes
|
|
810
1166
|
* @returns the confidence
|
|
1167
|
+
* @category Plugin helpers
|
|
811
1168
|
*/
|
|
812
1169
|
export function sniffCys(head) {
|
|
813
1170
|
for (let at = 0; at + 4 <= head.byteLength; at++) {
|
|
814
1171
|
if (startsLikeZip(head.subarray(at))) {
|
|
815
|
-
return
|
|
1172
|
+
return SESSION_NAMES.some((r) => r.test(decodeEntryName(head.subarray(at)))) ? 0.95 : 0;
|
|
816
1173
|
}
|
|
817
1174
|
}
|
|
818
1175
|
return 0;
|
|
819
1176
|
}
|
|
820
|
-
/**
|
|
1177
|
+
/**
|
|
1178
|
+
* The Cytoscape session importer.
|
|
1179
|
+
* @category Built-in formats
|
|
1180
|
+
*/
|
|
821
1181
|
export const cysImporter = Object.freeze({
|
|
822
1182
|
format: FORMAT,
|
|
1183
|
+
options: Object.freeze(["maxUncompressedBytes", "zAs"]),
|
|
823
1184
|
extensions: EXTENSIONS,
|
|
824
1185
|
mimeTypes: MIME_TYPES,
|
|
825
1186
|
sniff: sniffCys,
|