@graphty/graph-io 0.3.25 → 0.3.27
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-DrXtu_96.js +56 -0
- package/dist/chunks/choose-DrXtu_96.js.map +1 -0
- package/dist/chunks/constants-ikG85dT8.js +231 -0
- package/dist/chunks/constants-ikG85dT8.js.map +1 -0
- package/dist/chunks/{escape-B-tkk_Cl.js → escape-D1HPTUrP.js} +390 -99
- package/dist/chunks/escape-D1HPTUrP.js.map +1 -0
- package/dist/chunks/export-CHd4tQE2.js +4333 -0
- package/dist/chunks/export-CHd4tQE2.js.map +1 -0
- package/dist/chunks/{importer-eUREhBGj.js → exporter-D23CKNqF.js} +4969 -3201
- package/dist/chunks/exporter-D23CKNqF.js.map +1 -0
- package/dist/chunks/{exporter-DIeGJXAZ.js → exporter-DfWTgrti.js} +23 -21
- package/dist/chunks/exporter-DfWTgrti.js.map +1 -0
- package/dist/chunks/exporter-DhtW6FxK.js +3465 -0
- package/dist/chunks/exporter-DhtW6FxK.js.map +1 -0
- package/dist/chunks/{exporter-BGrsWamJ.js → exporter-IHv4nkCm.js} +38 -229
- package/dist/chunks/exporter-IHv4nkCm.js.map +1 -0
- package/dist/chunks/{importer-BWY2FFc5.js → importer-B1wmPQjr.js} +255 -77
- package/dist/chunks/importer-B1wmPQjr.js.map +1 -0
- package/dist/chunks/{importer-D_e7e7LX.js → importer-B2W_58pz.js} +399 -146
- package/dist/chunks/importer-B2W_58pz.js.map +1 -0
- package/dist/chunks/importer-BSALvkui.js +2811 -0
- package/dist/chunks/importer-BSALvkui.js.map +1 -0
- package/dist/chunks/{importer-vi3bSdtw.js → importer-BVRE--Un.js} +510 -77
- package/dist/chunks/importer-BVRE--Un.js.map +1 -0
- package/dist/chunks/{importer-DET1tg6s.js → importer-CIAzt-wo.js} +1689 -312
- package/dist/chunks/importer-CIAzt-wo.js.map +1 -0
- package/dist/chunks/{importer-DIrbbnAf.js → importer-DpEW5zeG.js} +585 -226
- package/dist/chunks/importer-DpEW5zeG.js.map +1 -0
- package/dist/chunks/{importer-zGLo8Dg_.js → importer-z_FL4VMb.js} +488 -161
- package/dist/chunks/importer-z_FL4VMb.js.map +1 -0
- package/dist/chunks/json-elements-BAmNBZok.js +1225 -0
- package/dist/chunks/json-elements-BAmNBZok.js.map +1 -0
- package/dist/chunks/ontology-export-Bab4eUA8.js +357 -0
- package/dist/chunks/ontology-export-Bab4eUA8.js.map +1 -0
- package/dist/chunks/{records-dbRkxwaq.js → records-CdlvL4RO.js} +298 -114
- package/dist/chunks/records-CdlvL4RO.js.map +1 -0
- package/dist/chunks/{text-Dr0Ifpag.js → text-DzXjAiTl.js} +29 -11
- package/dist/chunks/text-DzXjAiTl.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 +952 -262
- 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 +344 -87
- 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 +565 -158
- 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 +21 -3
- 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 +39 -22
- 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 +869 -189
- 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 +3 -1
- 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 +402 -85
- 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 +146 -33
- 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 +682 -287
- 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 +227 -80
- 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 +101 -27
- 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 +110 -28
- 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 +239 -86
- 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 +67 -35
- 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 +186 -54
- 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 +21 -22
- 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 +8 -3
- 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 +40 -6
- package/src/common/direction.ts +78 -51
- package/src/common/escape.ts +39 -22
- 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 +1035 -199
- package/src/common/line-format.ts +249 -0
- package/src/common/lists.ts +7 -2
- 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 +479 -90
- package/src/common/zip.ts +169 -7
- package/src/formats/csv/exporter.ts +202 -47
- 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 +890 -302
- package/src/formats/cx/index.ts +4 -3
- package/src/formats/cx2/exporter.ts +62 -339
- package/src/formats/cx2/importer.ts +318 -87
- 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 +142 -34
- 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 +113 -28
- 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 +321 -102
- 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 +90 -42
- package/src/formats/pajek/importer.ts +407 -93
- package/src/formats/pajek/index.ts +2 -1
- package/src/formats/pajek/syntax.ts +216 -55
- 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 +36 -28
- package/src/formats/xgmml/importer.ts +64 -86
- package/src/formats/xgmml/index.ts +1 -0
- package/src/formats/xgmml/values.ts +12 -4
- 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
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
takenIn,
|
|
39
39
|
uniqueColumnName,
|
|
40
40
|
} from "../../common/attributes.js";
|
|
41
|
-
import { type DeclaredTypeSpec } from "../../common/declared-types.js";
|
|
41
|
+
import { type DeclaredTypeSpec, overflowsToInfinity } from "../../common/declared-types.js";
|
|
42
42
|
import { DirectionResolver, type EdgeKind } from "../../common/direction.js";
|
|
43
43
|
import { IdCoercer } from "../../common/ids.js";
|
|
44
44
|
import { textChunks, throwIfAborted } from "../../common/input.js";
|
|
@@ -50,13 +50,16 @@ import {
|
|
|
50
50
|
type ResolvedImportOptions,
|
|
51
51
|
resolveImportOptions,
|
|
52
52
|
} from "../../common/options.js";
|
|
53
|
+
import { agree, plural } from "../../common/plural.js";
|
|
53
54
|
import { ImportReportBuilder } from "../../common/report.js";
|
|
54
55
|
import { isWeightField, parseWeightText } from "../../common/weights.js";
|
|
55
56
|
import {
|
|
56
57
|
isWhitespace,
|
|
57
58
|
localName,
|
|
59
|
+
sniffXmlText,
|
|
58
60
|
tokenizeXml,
|
|
59
61
|
xmlDeclaredEncoding,
|
|
62
|
+
XmlEmptyInputError,
|
|
60
63
|
type XmlHandler,
|
|
61
64
|
XmlSyntaxError,
|
|
62
65
|
} from "../../common/xml.js";
|
|
@@ -85,9 +88,29 @@ import {
|
|
|
85
88
|
import { XmlTreeBuilder } from "./tree.js";
|
|
86
89
|
import { graphicsDecl, graphicsValues } from "./yfiles.js";
|
|
87
90
|
|
|
88
|
-
/** The XML attributes the importer reads on
|
|
91
|
+
/** The XML attributes the importer reads on each element; any other is reported (once per element and name). */
|
|
89
92
|
const GRAPH_ATTRIBUTES: ReadonlySet<string> = new Set(["id", "edgedefault", "parse.nodes", "parse.edges"]);
|
|
90
93
|
const NODE_ATTRIBUTES: ReadonlySet<string> = new Set(["id"]);
|
|
94
|
+
const KEY_ATTRIBUTES: ReadonlySet<string> = new Set([
|
|
95
|
+
"id",
|
|
96
|
+
"for",
|
|
97
|
+
"attr.name",
|
|
98
|
+
"attr.type",
|
|
99
|
+
"name",
|
|
100
|
+
"type",
|
|
101
|
+
"yfiles.type",
|
|
102
|
+
]);
|
|
103
|
+
const DATA_ATTRIBUTES: ReadonlySet<string> = new Set(["key"]);
|
|
104
|
+
const HYPEREDGE_ATTRIBUTES: ReadonlySet<string> = new Set(["id"]);
|
|
105
|
+
const ENDPOINT_ATTRIBUTES: ReadonlySet<string> = new Set(["node", "type"]);
|
|
106
|
+
const PORT_ATTRIBUTES: ReadonlySet<string> = new Set(["name"]);
|
|
107
|
+
const NO_ATTRIBUTES: ReadonlySet<string> = new Set();
|
|
108
|
+
|
|
109
|
+
/** Attributes the GraphML schema defines that the importer does not keep: reported as not kept, not as foreign. */
|
|
110
|
+
const SCHEMA_ATTRIBUTES: ReadonlySet<string> = new Set(["id", "port", "parse.order", "parse.nodeids", "parse.edgeids"]);
|
|
111
|
+
|
|
112
|
+
/** Distinct unknown attribute names reported one by one per element kind; the rest are counted in one issue. */
|
|
113
|
+
const MAX_UNKNOWN_ATTRIBUTE_NAMES = 16;
|
|
91
114
|
const EDGE_ATTRIBUTES: ReadonlySet<string> = new Set([
|
|
92
115
|
"id",
|
|
93
116
|
"source",
|
|
@@ -97,12 +120,21 @@ const EDGE_ATTRIBUTES: ReadonlySet<string> = new Set([
|
|
|
97
120
|
"targetport",
|
|
98
121
|
]);
|
|
99
122
|
|
|
100
|
-
/**
|
|
101
|
-
|
|
123
|
+
/**
|
|
124
|
+
* The format-specific options of the GraphML importer.
|
|
125
|
+
* @category Built-in formats
|
|
126
|
+
*/
|
|
127
|
+
export interface GraphmlImportOptions extends CommonImportOptions {
|
|
102
128
|
/**
|
|
103
|
-
* How keys with a `yfiles.type`
|
|
104
|
-
*
|
|
105
|
-
*
|
|
129
|
+
* How yEd graphics (keys with a `yfiles.type`) are read. "json" keeps each one as a JSON
|
|
130
|
+
* attribute named after the key (for example, `d0`) that holds its XML as a tree, which the GraphML
|
|
131
|
+
* exporter writes back, and also reads the shapes it knows into plain columns beside it:
|
|
132
|
+
* `yfiles.position`, `yfiles.width`, `yfiles.height`, `yfiles.color`, `yfiles.borderColor`,
|
|
133
|
+
* `yfiles.borderWidth`, `yfiles.label` and `yfiles.shape` for nodes, and the line color, width
|
|
134
|
+
* and arrows for edges. The report's `lossy` list then holds W_GRAPHML_YFILES_JSON, because the
|
|
135
|
+
* XML comes back with the same structure but not byte for byte. "skip" leaves the graphics out,
|
|
136
|
+
* with a W_GRAPHML_YFILES_SKIPPED warning per key.
|
|
137
|
+
* @defaultValue "json"
|
|
106
138
|
*/
|
|
107
139
|
yfiles?: "json" | "skip" | undefined;
|
|
108
140
|
}
|
|
@@ -149,6 +181,10 @@ const enum Ctx {
|
|
|
149
181
|
Data,
|
|
150
182
|
Capture,
|
|
151
183
|
Skip,
|
|
184
|
+
/** A `<port>` (ports nest; their names are collected for the edge port check). */
|
|
185
|
+
Port,
|
|
186
|
+
/** Inside a `<graph>` held by an `<edge>`: dropped, its nodes and edges counted as skipped. */
|
|
187
|
+
Dropped,
|
|
152
188
|
}
|
|
153
189
|
|
|
154
190
|
/** The element domains a key can be declared for. */
|
|
@@ -216,6 +252,8 @@ interface NodeState {
|
|
|
216
252
|
restoredId: string | null;
|
|
217
253
|
failed: boolean;
|
|
218
254
|
readonly line: number;
|
|
255
|
+
/** The keys of the `<data>` read so far, for the duplicate check; null before the first. */
|
|
256
|
+
keys: Set<string> | null;
|
|
219
257
|
}
|
|
220
258
|
|
|
221
259
|
/** The `<edge>` being read. */
|
|
@@ -230,6 +268,13 @@ interface EdgeState {
|
|
|
230
268
|
readonly pending: unknown[];
|
|
231
269
|
failed: boolean;
|
|
232
270
|
line: number;
|
|
271
|
+
/** The keys of the `<data>` read so far, for the duplicate check; null before the first. */
|
|
272
|
+
keys: Set<string> | null;
|
|
273
|
+
/** The weight key whose value was read, or null. */
|
|
274
|
+
weightKey: string | null;
|
|
275
|
+
/** The endpoint ids as written, for the nested-graph-id check. */
|
|
276
|
+
readonly sourceText: string | null;
|
|
277
|
+
readonly targetText: string | null;
|
|
233
278
|
}
|
|
234
279
|
|
|
235
280
|
/** One `<endpoint>` of a hyperedge. */
|
|
@@ -268,6 +313,12 @@ interface Where {
|
|
|
268
313
|
interface GraphState {
|
|
269
314
|
readonly directed: boolean;
|
|
270
315
|
readonly parent: number;
|
|
316
|
+
/** The `parse.nodes` / `parse.edges` hints of a top-level graph, checked at its end; null when absent. */
|
|
317
|
+
readonly hints: { readonly nodes: number | null; readonly edges: number | null } | null;
|
|
318
|
+
/** The `<node>` / `<edge>` elements read directly in this graph. */
|
|
319
|
+
nodes: number;
|
|
320
|
+
edges: number;
|
|
321
|
+
readonly line: number;
|
|
271
322
|
}
|
|
272
323
|
|
|
273
324
|
/**
|
|
@@ -334,6 +385,10 @@ class GraphmlReader implements XmlHandler {
|
|
|
334
385
|
private readonly declared = new IndexFlags();
|
|
335
386
|
|
|
336
387
|
private readonly edgeIds = new Set<string>();
|
|
388
|
+
/** The keys `<data>` referenced before any `<key>` declared them (E_GRAPHML_UNKNOWN_KEY). */
|
|
389
|
+
private readonly unknownKeyIds = new Set<string>();
|
|
390
|
+
/** The ids of the edges waiting in {@link deferred}. */
|
|
391
|
+
private readonly deferredEdgeIds = new Set<string>();
|
|
337
392
|
|
|
338
393
|
private elementsSinceCheck = 0;
|
|
339
394
|
|
|
@@ -361,6 +416,11 @@ class GraphmlReader implements XmlHandler {
|
|
|
361
416
|
|
|
362
417
|
private descText = "";
|
|
363
418
|
|
|
419
|
+
private descLine = 0;
|
|
420
|
+
|
|
421
|
+
/** The keys of the graph-level `<data>` read so far, for the duplicate check. */
|
|
422
|
+
private readonly graphKeys: { keys: Set<string> | null } = { keys: null };
|
|
423
|
+
|
|
364
424
|
private description: string | null = null;
|
|
365
425
|
|
|
366
426
|
private graphSeen = false;
|
|
@@ -390,6 +450,34 @@ class GraphmlReader implements XmlHandler {
|
|
|
390
450
|
|
|
391
451
|
private hubHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;
|
|
392
452
|
|
|
453
|
+
/** The hub nodes of star-expanded hyperedges, so a later `<node>` of the same id is reported as a clash. */
|
|
454
|
+
private readonly hubs = new IndexFlags();
|
|
455
|
+
|
|
456
|
+
/** Edges and hyperedges whose endpoints were not declared yet under addMissingNodes: false, added at the end. */
|
|
457
|
+
private readonly deferred: (EdgeState | HyperedgeState)[] = [];
|
|
458
|
+
|
|
459
|
+
/** The ids of the `<graph>` elements read so far, for the endpoint-names-a-graph check. */
|
|
460
|
+
private readonly graphIds = new Set<string>();
|
|
461
|
+
|
|
462
|
+
/** How many top-level `<graph>` elements were opened. */
|
|
463
|
+
private topGraphs = 0;
|
|
464
|
+
|
|
465
|
+
/** The namespace of the `<graphml>` root (children in it are GraphML even when it is not GraphML's). */
|
|
466
|
+
private rootNs: string | null = GRAPHML_NAMESPACE;
|
|
467
|
+
|
|
468
|
+
/** Whether a graph-level `<desc>` was kept, so a second one is reported. */
|
|
469
|
+
private graphDescSeen = false;
|
|
470
|
+
|
|
471
|
+
/** Unknown attribute names reported per element kind, and how many more were not listed. */
|
|
472
|
+
private readonly unknownNames = new Map<string, Set<string>>();
|
|
473
|
+
|
|
474
|
+
private readonly unknownOverflow = new Map<string, number>();
|
|
475
|
+
|
|
476
|
+
/** The port names each node declares, and the ports edges reference, checked at the end. */
|
|
477
|
+
private readonly ports = new Map<NodeId, Set<string>>();
|
|
478
|
+
|
|
479
|
+
private readonly portRefs: [NodeId, string][] = [];
|
|
480
|
+
|
|
393
481
|
/**
|
|
394
482
|
* Create a reader for one import call.
|
|
395
483
|
* @param sink - the sink
|
|
@@ -418,8 +506,9 @@ class GraphmlReader implements XmlHandler {
|
|
|
418
506
|
* @param name - the element name
|
|
419
507
|
* @param attrs - its attributes
|
|
420
508
|
* @param line - the line
|
|
509
|
+
* @param ns - the element's namespace (null: an undeclared prefix)
|
|
421
510
|
*/
|
|
422
|
-
start(name: string, attrs: ReadonlyMap<string, string>, line: number): void {
|
|
511
|
+
start(name: string, attrs: ReadonlyMap<string, string>, line: number, ns: string | null = ""): void {
|
|
423
512
|
const parent = this.ctx.length === 0 ? null : this.ctx[this.ctx.length - 1];
|
|
424
513
|
switch (parent) {
|
|
425
514
|
case Ctx.Capture:
|
|
@@ -433,10 +522,18 @@ class GraphmlReader implements XmlHandler {
|
|
|
433
522
|
case Ctx.Default:
|
|
434
523
|
this.captureStart(name, attrs);
|
|
435
524
|
return;
|
|
525
|
+
case Ctx.Dropped:
|
|
526
|
+
this.dropStart(localName(name));
|
|
527
|
+
return;
|
|
436
528
|
default:
|
|
437
529
|
break;
|
|
438
530
|
}
|
|
439
531
|
const local = localName(name);
|
|
532
|
+
if (parent !== null && ns !== GRAPHML_NAMESPACE && ns !== "" && ns !== this.rootNs) {
|
|
533
|
+
// a vendor element whose local name happens to be node, edge or data is not GraphML
|
|
534
|
+
this.unknownElement(name, line);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
440
537
|
switch (parent) {
|
|
441
538
|
case null:
|
|
442
539
|
if (local !== "graphml") {
|
|
@@ -444,6 +541,18 @@ class GraphmlReader implements XmlHandler {
|
|
|
444
541
|
line,
|
|
445
542
|
});
|
|
446
543
|
}
|
|
544
|
+
if (ns !== GRAPHML_NAMESPACE && ns !== "") {
|
|
545
|
+
this.report.warning(
|
|
546
|
+
"validation-error",
|
|
547
|
+
GRAPHML_ISSUE.NAMESPACE,
|
|
548
|
+
ns === null
|
|
549
|
+
? `the root <${name}> uses an undeclared prefix; read as GraphML`
|
|
550
|
+
: `the root <${name}> is in the namespace ${JSON.stringify(ns)}, not ${GRAPHML_NAMESPACE}; read as GraphML`,
|
|
551
|
+
{ line, element: ns },
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
this.rootNs = ns;
|
|
555
|
+
this.reportUnreadAttributes("graphml", attrs, NO_ATTRIBUTES, line);
|
|
447
556
|
this.readRoot(attrs);
|
|
448
557
|
this.ctx.push(Ctx.Graphml);
|
|
449
558
|
return;
|
|
@@ -451,7 +560,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
451
560
|
this.startInGraphml(name, local, attrs, line);
|
|
452
561
|
return;
|
|
453
562
|
case Ctx.Key:
|
|
454
|
-
this.startInKey(name, local, line);
|
|
563
|
+
this.startInKey(name, local, attrs, line);
|
|
455
564
|
return;
|
|
456
565
|
case Ctx.Graph:
|
|
457
566
|
this.startInGraph(name, local, attrs, line);
|
|
@@ -465,6 +574,9 @@ class GraphmlReader implements XmlHandler {
|
|
|
465
574
|
case Ctx.Hyperedge:
|
|
466
575
|
this.startInHyperedge(name, local, attrs, line);
|
|
467
576
|
return;
|
|
577
|
+
case Ctx.Port:
|
|
578
|
+
this.startInPort(name, local, attrs, line);
|
|
579
|
+
return;
|
|
468
580
|
default:
|
|
469
581
|
this.unknownElement(name, line);
|
|
470
582
|
}
|
|
@@ -483,6 +595,8 @@ class GraphmlReader implements XmlHandler {
|
|
|
483
595
|
return;
|
|
484
596
|
case Ctx.Skip:
|
|
485
597
|
case Ctx.Graphml:
|
|
598
|
+
case Ctx.Port:
|
|
599
|
+
case Ctx.Dropped:
|
|
486
600
|
case undefined:
|
|
487
601
|
return;
|
|
488
602
|
case Ctx.Desc:
|
|
@@ -507,7 +621,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
507
621
|
this.finishHyperedge();
|
|
508
622
|
return;
|
|
509
623
|
case Ctx.Graph:
|
|
510
|
-
this.
|
|
624
|
+
this.finishGraph();
|
|
511
625
|
return;
|
|
512
626
|
default: {
|
|
513
627
|
const value: never = ctx;
|
|
@@ -539,6 +653,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
539
653
|
this.descText += text;
|
|
540
654
|
return;
|
|
541
655
|
case Ctx.Skip:
|
|
656
|
+
case Ctx.Dropped:
|
|
542
657
|
return;
|
|
543
658
|
default:
|
|
544
659
|
if (!isWhitespace(text)) {
|
|
@@ -559,6 +674,24 @@ class GraphmlReader implements XmlHandler {
|
|
|
559
674
|
if (!this.graphSeen) {
|
|
560
675
|
this.report.fail(GRAPHML_ISSUE.NO_GRAPH, "the document has no <graph> element");
|
|
561
676
|
}
|
|
677
|
+
// GraphML allows an edge before the nodes it joins: what waited for them is added now
|
|
678
|
+
for (const edge of this.deferred) {
|
|
679
|
+
if ("endpoints" in edge) {
|
|
680
|
+
this.expandHyperedge(edge);
|
|
681
|
+
} else {
|
|
682
|
+
this.addEdge(edge);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
this.deferred.length = 0;
|
|
686
|
+
this.checkPorts();
|
|
687
|
+
for (const [kind, more] of this.unknownOverflow) {
|
|
688
|
+
this.report.warning(
|
|
689
|
+
"unsupported",
|
|
690
|
+
GRAPHML_ISSUE.UNKNOWN_XML_ATTRIBUTE,
|
|
691
|
+
`${more} more distinct attribute names on <${kind}> are not GraphML attributes and are not kept (not listed one by one)`,
|
|
692
|
+
{ element: kind },
|
|
693
|
+
);
|
|
694
|
+
}
|
|
562
695
|
const meta: GraphmlMeta = { graphId: this.graphId, edgedefault: this.edgedefault, namespaces: this.namespaces };
|
|
563
696
|
this.sink.setMeta({
|
|
564
697
|
sourceFormat: FORMAT,
|
|
@@ -601,7 +734,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
601
734
|
private startInGraphml(name: string, local: string, attrs: ReadonlyMap<string, string>, line: number): void {
|
|
602
735
|
switch (local) {
|
|
603
736
|
case "desc":
|
|
604
|
-
this.beginDesc("graph");
|
|
737
|
+
this.beginDesc("graph", line);
|
|
605
738
|
return;
|
|
606
739
|
case "key":
|
|
607
740
|
this.beginKey(attrs, line);
|
|
@@ -621,14 +754,27 @@ class GraphmlReader implements XmlHandler {
|
|
|
621
754
|
* A child of `<key>`.
|
|
622
755
|
* @param name - the element name
|
|
623
756
|
* @param local - its local name
|
|
757
|
+
* @param attrs - its attributes
|
|
624
758
|
* @param line - the line
|
|
625
759
|
*/
|
|
626
|
-
private startInKey(name: string, local: string, line: number): void {
|
|
760
|
+
private startInKey(name: string, local: string, attrs: ReadonlyMap<string, string>, line: number): void {
|
|
627
761
|
switch (local) {
|
|
628
762
|
case "desc":
|
|
629
|
-
this.beginDesc("key");
|
|
763
|
+
this.beginDesc("key", line);
|
|
630
764
|
return;
|
|
631
765
|
case "default":
|
|
766
|
+
this.reportUnreadAttributes("default", attrs, NO_ATTRIBUTES, line);
|
|
767
|
+
if (
|
|
768
|
+
this.pendingKey !== null &&
|
|
769
|
+
(this.pendingKey.defaultText !== null || this.pendingKey.defaultTree !== undefined)
|
|
770
|
+
) {
|
|
771
|
+
this.report.warning(
|
|
772
|
+
"validation-error",
|
|
773
|
+
GRAPHML_ISSUE.DUPLICATE_ATTRIBUTE,
|
|
774
|
+
`key "${this.pendingKey.id ?? ""}" has a second <default>; the later one is kept`,
|
|
775
|
+
{ line, element: this.pendingKey.id },
|
|
776
|
+
);
|
|
777
|
+
}
|
|
632
778
|
this.content.key = null;
|
|
633
779
|
this.content.pendingKey = this.pendingKey;
|
|
634
780
|
this.content.text = "";
|
|
@@ -649,22 +795,32 @@ class GraphmlReader implements XmlHandler {
|
|
|
649
795
|
* @param line - the line
|
|
650
796
|
*/
|
|
651
797
|
private startInGraph(name: string, local: string, attrs: ReadonlyMap<string, string>, line: number): void {
|
|
798
|
+
const first = this.graphs.length === 1 && this.topGraphs === 1;
|
|
652
799
|
switch (local) {
|
|
653
800
|
case "desc":
|
|
654
|
-
if (
|
|
655
|
-
this.beginDesc("graph");
|
|
801
|
+
if (first) {
|
|
802
|
+
this.beginDesc("graph", line);
|
|
656
803
|
} else {
|
|
804
|
+
this.report.warnOnce(
|
|
805
|
+
"unsupported",
|
|
806
|
+
GRAPHML_ISSUE.DESC_DROPPED,
|
|
807
|
+
`the <desc> of a ${this.graphs.length === 1 ? "second top-level" : "nested"} <graph> is not kept`,
|
|
808
|
+
{ line },
|
|
809
|
+
`${GRAPHML_ISSUE.DESC_DROPPED}:graph`,
|
|
810
|
+
);
|
|
657
811
|
this.ctx.push(Ctx.Skip);
|
|
658
812
|
}
|
|
659
813
|
return;
|
|
660
814
|
case "data":
|
|
661
|
-
if (
|
|
815
|
+
if (first) {
|
|
662
816
|
this.beginData(attrs, line, "graph");
|
|
663
817
|
} else {
|
|
664
818
|
this.report.warnOnce(
|
|
665
819
|
"unsupported",
|
|
666
820
|
GRAPHML_ISSUE.NESTED_GRAPH_DATA,
|
|
667
|
-
|
|
821
|
+
this.graphs.length === 1
|
|
822
|
+
? "data of a second top-level <graph> cannot be kept; the graphs are merged and only the first one's attributes are kept"
|
|
823
|
+
: "data of a nested <graph> cannot be kept; only the top-level graph has attributes",
|
|
668
824
|
{ line },
|
|
669
825
|
);
|
|
670
826
|
this.ctx.push(Ctx.Skip);
|
|
@@ -723,11 +879,19 @@ class GraphmlReader implements XmlHandler {
|
|
|
723
879
|
"<port> declarations are not kept; sourceport / targetport edge attributes are",
|
|
724
880
|
{ line },
|
|
725
881
|
);
|
|
726
|
-
this.
|
|
882
|
+
this.beginPort(attrs, line);
|
|
727
883
|
return;
|
|
728
884
|
case "graph": {
|
|
729
885
|
const node = this.nodes[this.nodes.length - 1];
|
|
730
886
|
this.materializeNode(node);
|
|
887
|
+
if (node.index === INVALID_INDEX) {
|
|
888
|
+
this.report.error(
|
|
889
|
+
"missing-value",
|
|
890
|
+
GRAPHML_ISSUE.UNKNOWN_PARENT,
|
|
891
|
+
"the nodes of this nested <graph> lose their parent: the node holding it was skipped; they are read as top-level nodes",
|
|
892
|
+
{ line },
|
|
893
|
+
);
|
|
894
|
+
}
|
|
731
895
|
this.beginGraph(attrs, line, node.index);
|
|
732
896
|
return;
|
|
733
897
|
}
|
|
@@ -768,11 +932,98 @@ class GraphmlReader implements XmlHandler {
|
|
|
768
932
|
case "data":
|
|
769
933
|
this.beginData(attrs, line, "edge");
|
|
770
934
|
return;
|
|
935
|
+
case "graph":
|
|
936
|
+
this.report.warnOnce(
|
|
937
|
+
"unsupported",
|
|
938
|
+
GRAPHML_ISSUE.EDGE_GRAPH_DROPPED,
|
|
939
|
+
"a <graph> inside an <edge> is not supported; it is dropped with the nodes and edges it holds (counted as skipped)",
|
|
940
|
+
{ line, element: attrs.get("id") ?? null },
|
|
941
|
+
);
|
|
942
|
+
this.ctx.push(Ctx.Dropped);
|
|
943
|
+
return;
|
|
771
944
|
default:
|
|
772
945
|
this.unknownElement(name, line);
|
|
773
946
|
}
|
|
774
947
|
}
|
|
775
948
|
|
|
949
|
+
/**
|
|
950
|
+
* An element inside a dropped `<graph>` (one held by an edge): counted as skipped.
|
|
951
|
+
* @param local - its local name
|
|
952
|
+
*/
|
|
953
|
+
private dropStart(local: string): void {
|
|
954
|
+
if (local === "node") {
|
|
955
|
+
this.report.counts.skippedNodes++;
|
|
956
|
+
} else if (local === "edge" || local === "hyperedge") {
|
|
957
|
+
this.report.counts.skippedEdges++;
|
|
958
|
+
}
|
|
959
|
+
this.ctx.push(local === "data" || local === "desc" ? Ctx.Skip : Ctx.Dropped);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Open a `<port>`: its name is kept for the edge port check, its content is skipped.
|
|
964
|
+
* @param attrs - its attributes
|
|
965
|
+
* @param line - the line
|
|
966
|
+
*/
|
|
967
|
+
private beginPort(attrs: ReadonlyMap<string, string>, line: number): void {
|
|
968
|
+
this.reportUnreadAttributes("port", attrs, PORT_ATTRIBUTES, line);
|
|
969
|
+
const node = this.nodes[this.nodes.length - 1];
|
|
970
|
+
const name = attrs.get("name");
|
|
971
|
+
if (node.id !== null && name !== undefined) {
|
|
972
|
+
const id = node.restoredId ?? node.id;
|
|
973
|
+
let names = this.ports.get(id);
|
|
974
|
+
if (names === undefined) {
|
|
975
|
+
names = new Set();
|
|
976
|
+
this.ports.set(id, names);
|
|
977
|
+
}
|
|
978
|
+
names.add(name);
|
|
979
|
+
}
|
|
980
|
+
this.ctx.push(Ctx.Port);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* A child of `<port>`: a nested port declares a name too; data and desc are skipped.
|
|
985
|
+
* @param name - the element name
|
|
986
|
+
* @param local - its local name
|
|
987
|
+
* @param attrs - its attributes
|
|
988
|
+
* @param line - the line
|
|
989
|
+
*/
|
|
990
|
+
private startInPort(name: string, local: string, attrs: ReadonlyMap<string, string>, line: number): void {
|
|
991
|
+
switch (local) {
|
|
992
|
+
case "port":
|
|
993
|
+
this.beginPort(attrs, line);
|
|
994
|
+
return;
|
|
995
|
+
case "data":
|
|
996
|
+
case "desc":
|
|
997
|
+
this.ctx.push(Ctx.Skip);
|
|
998
|
+
return;
|
|
999
|
+
default:
|
|
1000
|
+
this.unknownElement(name, line);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Report the `sourceport` / `targetport` values that name a port their node never declares
|
|
1006
|
+
* (once, with the count), now that every node has been read.
|
|
1007
|
+
*/
|
|
1008
|
+
private checkPorts(): void {
|
|
1009
|
+
let dangling = 0;
|
|
1010
|
+
let first: string | null = null;
|
|
1011
|
+
for (const [node, port] of this.portRefs) {
|
|
1012
|
+
if (this.ports.get(node)?.has(port) !== true) {
|
|
1013
|
+
dangling++;
|
|
1014
|
+
first ??= `${String(node)}:${port}`;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (dangling > 0) {
|
|
1018
|
+
this.report.warning(
|
|
1019
|
+
"missing-value",
|
|
1020
|
+
GRAPHML_ISSUE.DANGLING_REFERENCE,
|
|
1021
|
+
`${dangling} edge port reference${plural(dangling)} ${agree(dangling, "names", "name")} a port their node does not declare (first: ${first ?? ""}); the values are kept`,
|
|
1022
|
+
{ element: first },
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
776
1027
|
/**
|
|
777
1028
|
* A child of `<hyperedge>` (under the star / clique policies).
|
|
778
1029
|
* @param name - the element name
|
|
@@ -783,6 +1034,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
783
1034
|
private startInHyperedge(name: string, local: string, attrs: ReadonlyMap<string, string>, line: number): void {
|
|
784
1035
|
switch (local) {
|
|
785
1036
|
case "endpoint":
|
|
1037
|
+
this.reportUnreadAttributes("endpoint", attrs, ENDPOINT_ATTRIBUTES, line);
|
|
786
1038
|
this.readEndpoint(attrs, line);
|
|
787
1039
|
this.ctx.push(Ctx.Skip);
|
|
788
1040
|
return;
|
|
@@ -796,6 +1048,13 @@ class GraphmlReader implements XmlHandler {
|
|
|
796
1048
|
this.ctx.push(Ctx.Skip);
|
|
797
1049
|
return;
|
|
798
1050
|
case "desc":
|
|
1051
|
+
this.report.warnOnce(
|
|
1052
|
+
"unsupported",
|
|
1053
|
+
GRAPHML_ISSUE.DESC_DROPPED,
|
|
1054
|
+
"the <desc> of a hyperedge is not kept by the star / clique expansions",
|
|
1055
|
+
{ line },
|
|
1056
|
+
`${GRAPHML_ISSUE.DESC_DROPPED}:hyperedge`,
|
|
1057
|
+
);
|
|
799
1058
|
this.ctx.push(Ctx.Skip);
|
|
800
1059
|
return;
|
|
801
1060
|
default:
|
|
@@ -809,25 +1068,22 @@ class GraphmlReader implements XmlHandler {
|
|
|
809
1068
|
* @param line - the line
|
|
810
1069
|
*/
|
|
811
1070
|
private unknownElement(name: string, line: number): void {
|
|
812
|
-
this.report.warnOnce(
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
{
|
|
817
|
-
line,
|
|
818
|
-
element: name,
|
|
819
|
-
},
|
|
820
|
-
);
|
|
1071
|
+
this.report.warnOnce("unsupported", GRAPHML_ISSUE.UNKNOWN_ELEMENT, unknownElementMessage(name), {
|
|
1072
|
+
line,
|
|
1073
|
+
element: name,
|
|
1074
|
+
});
|
|
821
1075
|
this.ctx.push(Ctx.Skip);
|
|
822
1076
|
}
|
|
823
1077
|
|
|
824
1078
|
/**
|
|
825
1079
|
* Open a `<desc>`.
|
|
826
1080
|
* @param target - what the text describes
|
|
1081
|
+
* @param line - the line
|
|
827
1082
|
*/
|
|
828
|
-
private beginDesc(target: "graph" | "key"): void {
|
|
1083
|
+
private beginDesc(target: "graph" | "key", line: number): void {
|
|
829
1084
|
this.descTarget = target;
|
|
830
1085
|
this.descText = "";
|
|
1086
|
+
this.descLine = line;
|
|
831
1087
|
this.ctx.push(Ctx.Desc);
|
|
832
1088
|
}
|
|
833
1089
|
|
|
@@ -837,6 +1093,16 @@ class GraphmlReader implements XmlHandler {
|
|
|
837
1093
|
if (this.descTarget === "key" && this.pendingKey !== null) {
|
|
838
1094
|
this.pendingKey.desc = text;
|
|
839
1095
|
} else if (this.descTarget === "graph" && text.length > 0) {
|
|
1096
|
+
if (this.graphDescSeen) {
|
|
1097
|
+
// a <graphml> and a <graph> description: the graph has one description field
|
|
1098
|
+
this.report.warning(
|
|
1099
|
+
"unsupported",
|
|
1100
|
+
GRAPHML_ISSUE.DESC_DROPPED,
|
|
1101
|
+
"a second graph-level <desc> replaces the first; the earlier text is not kept",
|
|
1102
|
+
{ line: this.descLine },
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
this.graphDescSeen = true;
|
|
840
1106
|
this.description = text;
|
|
841
1107
|
}
|
|
842
1108
|
this.descTarget = null;
|
|
@@ -847,8 +1113,10 @@ class GraphmlReader implements XmlHandler {
|
|
|
847
1113
|
|
|
848
1114
|
/**
|
|
849
1115
|
* Report, once per element kind and attribute name, an XML attribute the importer does not read:
|
|
850
|
-
* a GraphML `parse.*` hint as ignored, anything else as
|
|
851
|
-
* `xml:` / `xsi:` attributes are not data and pass silently.
|
|
1116
|
+
* a GraphML `parse.*` hint as ignored, anything else as not kept. Namespace declarations and
|
|
1117
|
+
* `xml:` / `xsi:` attributes are not data and pass silently. Beyond MAX_UNKNOWN_ATTRIBUTE_NAMES
|
|
1118
|
+
* distinct names per element kind the rest are counted and reported once at the end, so a file
|
|
1119
|
+
* with thousands of vendor attributes does not grow the report without bound.
|
|
852
1120
|
* @param kind - the element name
|
|
853
1121
|
* @param attrs - the element's attributes
|
|
854
1122
|
* @param known - the attributes the importer reads on that element
|
|
@@ -860,21 +1128,39 @@ class GraphmlReader implements XmlHandler {
|
|
|
860
1128
|
known: ReadonlySet<string>,
|
|
861
1129
|
line: number,
|
|
862
1130
|
): void {
|
|
1131
|
+
if (attrs.size === 0) {
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
863
1134
|
for (const name of attrs.keys()) {
|
|
864
1135
|
if (known.has(name) || name === "xmlns" || /^(xmlns|xml|xsi):/.test(name)) {
|
|
865
1136
|
continue;
|
|
866
1137
|
}
|
|
867
1138
|
const hint = name.startsWith("parse.");
|
|
868
1139
|
const code = hint ? GRAPHML_ISSUE.PARSE_HINT_IGNORED : GRAPHML_ISSUE.UNKNOWN_XML_ATTRIBUTE;
|
|
869
|
-
this.
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
)
|
|
1140
|
+
let names = this.unknownNames.get(kind);
|
|
1141
|
+
if (names === undefined) {
|
|
1142
|
+
names = new Set();
|
|
1143
|
+
this.unknownNames.set(kind, names);
|
|
1144
|
+
}
|
|
1145
|
+
if (names.has(name)) {
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
if (names.size >= MAX_UNKNOWN_ATTRIBUTE_NAMES && !hint) {
|
|
1149
|
+
this.unknownOverflow.set(kind, (this.unknownOverflow.get(kind) ?? 0) + 1);
|
|
1150
|
+
}
|
|
1151
|
+
names.add(name);
|
|
1152
|
+
if (names.size > MAX_UNKNOWN_ATTRIBUTE_NAMES && !hint) {
|
|
1153
|
+
continue;
|
|
1154
|
+
}
|
|
1155
|
+
let message: string;
|
|
1156
|
+
if (hint) {
|
|
1157
|
+
message = `the <${kind}> parse hint ${name} is ignored`;
|
|
1158
|
+
} else if (SCHEMA_ATTRIBUTES.has(name)) {
|
|
1159
|
+
message = `the <${kind}> attribute ${name} is not kept`;
|
|
1160
|
+
} else {
|
|
1161
|
+
message = `the <${kind}> attribute ${name} is not a GraphML attribute; it is not kept`;
|
|
1162
|
+
}
|
|
1163
|
+
this.report.warning("unsupported", code, message, { line, element: name });
|
|
878
1164
|
}
|
|
879
1165
|
}
|
|
880
1166
|
|
|
@@ -888,17 +1174,29 @@ class GraphmlReader implements XmlHandler {
|
|
|
888
1174
|
private beginGraph(attrs: ReadonlyMap<string, string>, line: number, parent: number): void {
|
|
889
1175
|
this.reportUnreadAttributes("graph", attrs, GRAPH_ATTRIBUTES, line);
|
|
890
1176
|
const top = this.graphs.length === 0;
|
|
1177
|
+
if (top) {
|
|
1178
|
+
this.topGraphs++;
|
|
1179
|
+
}
|
|
1180
|
+
const graphId = attrs.get("id");
|
|
1181
|
+
if (graphId !== undefined && graphId.length > 0) {
|
|
1182
|
+
this.graphIds.add(graphId);
|
|
1183
|
+
}
|
|
891
1184
|
const edgedefault = attrs.get("edgedefault");
|
|
892
1185
|
let directed: boolean;
|
|
893
1186
|
if (edgedefault === "directed" || edgedefault === "undirected") {
|
|
894
1187
|
directed = edgedefault === "directed";
|
|
895
1188
|
} else if (edgedefault === undefined) {
|
|
896
1189
|
directed = top ? this.options.defaultDirected : this.graphs[this.graphs.length - 1].directed;
|
|
897
|
-
|
|
1190
|
+
// once per kind: a file of thousands of nested graphs without edgedefault is one finding
|
|
1191
|
+
const direction = directed ? "directed" : "undirected";
|
|
1192
|
+
this.report.warnOnce(
|
|
898
1193
|
"validation-error",
|
|
899
1194
|
GRAPHML_ISSUE.EDGEDEFAULT_MISSING,
|
|
900
|
-
|
|
1195
|
+
top
|
|
1196
|
+
? `<graph> has no edgedefault; read as ${direction}`
|
|
1197
|
+
: `a nested <graph> has no edgedefault; it takes its container's (${direction})`,
|
|
901
1198
|
{ line },
|
|
1199
|
+
`${GRAPHML_ISSUE.EDGEDEFAULT_MISSING}:${top ? "top" : "nested"}`,
|
|
902
1200
|
);
|
|
903
1201
|
} else {
|
|
904
1202
|
directed = top ? this.options.defaultDirected : this.graphs[this.graphs.length - 1].directed;
|
|
@@ -909,6 +1207,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
909
1207
|
{ line },
|
|
910
1208
|
);
|
|
911
1209
|
}
|
|
1210
|
+
let hints: GraphState["hints"] = null;
|
|
912
1211
|
if (top && this.graphSeen) {
|
|
913
1212
|
this.report.warnOnce(
|
|
914
1213
|
"unsupported",
|
|
@@ -916,6 +1215,17 @@ class GraphmlReader implements XmlHandler {
|
|
|
916
1215
|
"the document holds more than one top-level <graph>; their nodes and edges are merged into one graph",
|
|
917
1216
|
{ line, element: attrs.get("id") ?? null },
|
|
918
1217
|
);
|
|
1218
|
+
for (const name of ["parse.nodes", "parse.edges"]) {
|
|
1219
|
+
if (attrs.has(name)) {
|
|
1220
|
+
this.report.warnOnce(
|
|
1221
|
+
"unsupported",
|
|
1222
|
+
GRAPHML_ISSUE.PARSE_HINT_IGNORED,
|
|
1223
|
+
`the ${name} hint of a second top-level <graph> is ignored`,
|
|
1224
|
+
{ line, element: name },
|
|
1225
|
+
`${GRAPHML_ISSUE.PARSE_HINT_IGNORED}:second:${name}`,
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
919
1229
|
} else if (top) {
|
|
920
1230
|
this.graphSeen = true;
|
|
921
1231
|
this.graphId = attrs.get("id") ?? null;
|
|
@@ -923,6 +1233,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
923
1233
|
this.direction.setHeader(directed, { line });
|
|
924
1234
|
const nodes = this.hint(attrs.get("parse.nodes"), "parse.nodes", line);
|
|
925
1235
|
const edges = this.hint(attrs.get("parse.edges"), "parse.edges", line);
|
|
1236
|
+
hints = nodes === null && edges === null ? null : { nodes, edges };
|
|
926
1237
|
if (nodes !== null || edges !== null) {
|
|
927
1238
|
try {
|
|
928
1239
|
this.sink.reserve(nodes ?? undefined, edges ?? undefined);
|
|
@@ -941,10 +1252,31 @@ class GraphmlReader implements XmlHandler {
|
|
|
941
1252
|
}
|
|
942
1253
|
}
|
|
943
1254
|
}
|
|
944
|
-
this.graphs.push({ directed, parent });
|
|
1255
|
+
this.graphs.push({ directed, parent, hints, nodes: 0, edges: 0, line });
|
|
945
1256
|
this.ctx.push(Ctx.Graph);
|
|
946
1257
|
}
|
|
947
1258
|
|
|
1259
|
+
/** Close a `<graph>`: a top-level graph's count hints are checked against what it held. */
|
|
1260
|
+
private finishGraph(): void {
|
|
1261
|
+
const graph = this.graphs.pop();
|
|
1262
|
+
if (graph?.hints === null || graph === undefined) {
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
for (const [name, hint, found] of [
|
|
1266
|
+
["parse.nodes", graph.hints.nodes, graph.nodes],
|
|
1267
|
+
["parse.edges", graph.hints.edges, graph.edges],
|
|
1268
|
+
] as const) {
|
|
1269
|
+
if (hint !== null && hint !== found) {
|
|
1270
|
+
this.report.warning(
|
|
1271
|
+
"validation-error",
|
|
1272
|
+
GRAPHML_ISSUE.COUNT_MISMATCH,
|
|
1273
|
+
`<graph ${name}="${hint}"> but the graph holds ${found} ${name === "parse.nodes" ? "<node>" : "<edge>"} elements`,
|
|
1274
|
+
{ line: graph.line, element: name },
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
|
|
948
1280
|
/**
|
|
949
1281
|
* A `parse.nodes` / `parse.edges` hint the sink can reserve; anything else is reported and ignored.
|
|
950
1282
|
* @param text - the attribute text, or undefined
|
|
@@ -977,7 +1309,17 @@ class GraphmlReader implements XmlHandler {
|
|
|
977
1309
|
* @param line - the line
|
|
978
1310
|
*/
|
|
979
1311
|
private beginKey(attrs: ReadonlyMap<string, string>, line: number): void {
|
|
1312
|
+
// attr.list (a list extension of some writers) and yEd's yfiles.foldertype are reported here
|
|
1313
|
+
this.reportUnreadAttributes("key", attrs, KEY_ATTRIBUTES, line);
|
|
980
1314
|
const id = attrs.get("id") ?? null;
|
|
1315
|
+
if (id !== null && this.unknownKeyIds.delete(id)) {
|
|
1316
|
+
this.report.warning(
|
|
1317
|
+
"missing-value",
|
|
1318
|
+
GRAPHML_ISSUE.KEY_DECLARED_LATE,
|
|
1319
|
+
`key "${id}" is declared after data that uses it; those values were dropped (declare <key> before <graph>)`,
|
|
1320
|
+
{ line, element: id },
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
981
1323
|
const forText = attrs.get("for") ?? "all";
|
|
982
1324
|
let domains: readonly Domain[];
|
|
983
1325
|
switch (forText) {
|
|
@@ -1067,6 +1409,8 @@ class GraphmlReader implements XmlHandler {
|
|
|
1067
1409
|
continue;
|
|
1068
1410
|
}
|
|
1069
1411
|
if (domain === "edge" && !yfiles && isWeightField(name, this.options.weightFrom)) {
|
|
1412
|
+
// networkx writes one weight key per value type (long and double): every key of
|
|
1413
|
+
// that name is the weight; two of them on one edge are reported in finishData
|
|
1070
1414
|
weight = true;
|
|
1071
1415
|
this.weightOrigin ??= { id: key.id, title: key.attrName, type: key.attrType };
|
|
1072
1416
|
continue;
|
|
@@ -1245,8 +1589,12 @@ class GraphmlReader implements XmlHandler {
|
|
|
1245
1589
|
* @param domain - the domain of the element holding it
|
|
1246
1590
|
*/
|
|
1247
1591
|
private beginData(attrs: ReadonlyMap<string, string>, line: number, domain: Domain): void {
|
|
1592
|
+
this.reportUnreadAttributes("data", attrs, DATA_ATTRIBUTES, line);
|
|
1248
1593
|
const keyId = attrs.get("key");
|
|
1249
1594
|
const { content } = this;
|
|
1595
|
+
if (keyId !== undefined) {
|
|
1596
|
+
this.checkDuplicateData(keyId, domain, line);
|
|
1597
|
+
}
|
|
1250
1598
|
content.key = null;
|
|
1251
1599
|
content.pendingKey = null;
|
|
1252
1600
|
content.domain = domain;
|
|
@@ -1261,6 +1609,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
1261
1609
|
const entries = this.keys.get(keyId);
|
|
1262
1610
|
const key = entries?.find((entry) => entry.domains.includes(domain)) ?? entries?.[0];
|
|
1263
1611
|
if (key === undefined) {
|
|
1612
|
+
this.unknownKeyIds.add(keyId);
|
|
1264
1613
|
this.report.error(
|
|
1265
1614
|
"validation-error",
|
|
1266
1615
|
GRAPHML_ISSUE.UNKNOWN_KEY,
|
|
@@ -1281,6 +1630,36 @@ class GraphmlReader implements XmlHandler {
|
|
|
1281
1630
|
this.ctx.push(Ctx.Data);
|
|
1282
1631
|
}
|
|
1283
1632
|
|
|
1633
|
+
/**
|
|
1634
|
+
* Report a second `<data>` of one key on one element; the later value is kept (it overwrites).
|
|
1635
|
+
* @param keyId - the key
|
|
1636
|
+
* @param domain - the domain of the element holding it
|
|
1637
|
+
* @param line - the line
|
|
1638
|
+
*/
|
|
1639
|
+
private checkDuplicateData(keyId: string, domain: Domain, line: number): void {
|
|
1640
|
+
let holder: { keys: Set<string> | null } | null = null;
|
|
1641
|
+
if (domain === "node") {
|
|
1642
|
+
holder = this.nodes.at(-1) ?? null;
|
|
1643
|
+
} else if (domain === "edge") {
|
|
1644
|
+
holder = this.edge;
|
|
1645
|
+
} else {
|
|
1646
|
+
holder = this.graphKeys;
|
|
1647
|
+
}
|
|
1648
|
+
if (holder === null) {
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
holder.keys ??= new Set();
|
|
1652
|
+
if (holder.keys.has(keyId)) {
|
|
1653
|
+
this.report.warning(
|
|
1654
|
+
"validation-error",
|
|
1655
|
+
GRAPHML_ISSUE.DUPLICATE_ATTRIBUTE,
|
|
1656
|
+
`a second <data key="${keyId}"> on one ${domain}; the later value is kept`,
|
|
1657
|
+
{ line, element: keyId },
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1660
|
+
holder.keys.add(keyId);
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1284
1663
|
/**
|
|
1285
1664
|
* A child element inside `<data>` / `<default>`: the content becomes a tree.
|
|
1286
1665
|
* @param name - the element name
|
|
@@ -1337,9 +1716,18 @@ class GraphmlReader implements XmlHandler {
|
|
|
1337
1716
|
case "node": {
|
|
1338
1717
|
const node = this.nodes[this.nodes.length - 1];
|
|
1339
1718
|
if (key.originalId) {
|
|
1340
|
-
if (typeof value
|
|
1341
|
-
|
|
1719
|
+
if (typeof value !== "string" || node.index !== INVALID_INDEX) {
|
|
1720
|
+
this.report.warning(
|
|
1721
|
+
"validation-error",
|
|
1722
|
+
GRAPHML_ISSUE.ORIGINAL_ID_IGNORED,
|
|
1723
|
+
typeof value === "string"
|
|
1724
|
+
? `the ${ORIGINAL_ID_ATTRIBUTE} value comes after the node's nested <graph>, which added the node under its written id; it is not restored`
|
|
1725
|
+
: `the ${ORIGINAL_ID_ATTRIBUTE} value holds elements, not text; it is not restored`,
|
|
1726
|
+
where,
|
|
1727
|
+
);
|
|
1728
|
+
return;
|
|
1342
1729
|
}
|
|
1730
|
+
node.restoredId = value;
|
|
1343
1731
|
return;
|
|
1344
1732
|
}
|
|
1345
1733
|
if (key.yfiles) {
|
|
@@ -1371,8 +1759,18 @@ class GraphmlReader implements XmlHandler {
|
|
|
1371
1759
|
);
|
|
1372
1760
|
return;
|
|
1373
1761
|
}
|
|
1762
|
+
if (edge.weightKey !== null && edge.weightKey !== key.id) {
|
|
1763
|
+
this.report.warning(
|
|
1764
|
+
"validation-error",
|
|
1765
|
+
GRAPHML_ISSUE.DUPLICATE_ATTRIBUTE,
|
|
1766
|
+
`the edge has values of two weight keys ("${edge.weightKey}" and "${key.id}"); the later one is kept`,
|
|
1767
|
+
where,
|
|
1768
|
+
);
|
|
1769
|
+
}
|
|
1770
|
+
edge.weightKey = key.id;
|
|
1374
1771
|
try {
|
|
1375
|
-
edge.weight = parseWeightText(value);
|
|
1772
|
+
edge.weight = parseWeightText(value, this.report);
|
|
1773
|
+
this.checkOverflow(value, edge.weight, key.name, where);
|
|
1376
1774
|
} catch (err) {
|
|
1377
1775
|
this.skipEdge(edge);
|
|
1378
1776
|
this.report.recordError(err, where);
|
|
@@ -1439,7 +1837,18 @@ class GraphmlReader implements XmlHandler {
|
|
|
1439
1837
|
*/
|
|
1440
1838
|
private graphics(domain: "node" | "edge", key: KeyEntry, tree: unknown, where: Where): [ColumnHandle, unknown][] {
|
|
1441
1839
|
const out: [ColumnHandle, unknown][] = [];
|
|
1442
|
-
|
|
1840
|
+
const unmapped: string[] = [];
|
|
1841
|
+
const values = graphicsValues(domain, tree, unmapped);
|
|
1842
|
+
for (const field of unmapped) {
|
|
1843
|
+
this.report.warnOnce(
|
|
1844
|
+
"validation-error",
|
|
1845
|
+
GRAPHML_ISSUE.YFILES_VALUE,
|
|
1846
|
+
`a yFiles ${domain} ${field} that is not a number is not mapped to its yfiles column (the graphics tree is kept)`,
|
|
1847
|
+
where,
|
|
1848
|
+
`${GRAPHML_ISSUE.YFILES_VALUE}:${domain}:${field}`,
|
|
1849
|
+
);
|
|
1850
|
+
}
|
|
1851
|
+
for (const [field, value] of values) {
|
|
1443
1852
|
const id = `${domain}:${field}`;
|
|
1444
1853
|
let handle = this.graphicsColumns.get(id);
|
|
1445
1854
|
if (handle === undefined) {
|
|
@@ -1504,6 +1913,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
1504
1913
|
{ line: where.line, element: target.name },
|
|
1505
1914
|
);
|
|
1506
1915
|
}
|
|
1916
|
+
this.checkOverflow(value, parsed, target.name, where);
|
|
1507
1917
|
return parsed;
|
|
1508
1918
|
} catch (err) {
|
|
1509
1919
|
this.report.recordError(err, where);
|
|
@@ -1511,6 +1921,25 @@ class GraphmlReader implements XmlHandler {
|
|
|
1511
1921
|
}
|
|
1512
1922
|
}
|
|
1513
1923
|
|
|
1924
|
+
/**
|
|
1925
|
+
* Report (once per column) a finite number text too large for a double, stored as Infinity.
|
|
1926
|
+
* @param text - the value text
|
|
1927
|
+
* @param value - the parsed value
|
|
1928
|
+
* @param column - the column name
|
|
1929
|
+
* @param where - the location
|
|
1930
|
+
*/
|
|
1931
|
+
private checkOverflow(text: string, value: unknown, column: string, where: Where): void {
|
|
1932
|
+
if (typeof value === "number" && overflowsToInfinity(text)) {
|
|
1933
|
+
this.report.warnOnce(
|
|
1934
|
+
"precision",
|
|
1935
|
+
PRECISION_CODE,
|
|
1936
|
+
`values of "${column}" beyond the double range (${text.trim()}) are stored as Infinity`,
|
|
1937
|
+
{ line: where.line, element: column },
|
|
1938
|
+
`${PRECISION_CODE}:overflow:${column}`,
|
|
1939
|
+
);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1514
1943
|
// ------------------------------------------------------------------ nodes
|
|
1515
1944
|
|
|
1516
1945
|
/**
|
|
@@ -1522,6 +1951,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
1522
1951
|
private beginNode(attrs: ReadonlyMap<string, string>, line: number): void {
|
|
1523
1952
|
this.reportUnreadAttributes("node", attrs, NODE_ATTRIBUTES, line);
|
|
1524
1953
|
const graph = this.graphs[this.graphs.length - 1];
|
|
1954
|
+
graph.nodes++;
|
|
1525
1955
|
const state: NodeState = {
|
|
1526
1956
|
id: null,
|
|
1527
1957
|
idText: null,
|
|
@@ -1531,6 +1961,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
1531
1961
|
restoredId: null,
|
|
1532
1962
|
failed: false,
|
|
1533
1963
|
line,
|
|
1964
|
+
keys: null,
|
|
1534
1965
|
};
|
|
1535
1966
|
const idText = attrs.get("id");
|
|
1536
1967
|
if (idText === undefined || idText.length === 0) {
|
|
@@ -1569,7 +2000,14 @@ class GraphmlReader implements XmlHandler {
|
|
|
1569
2000
|
try {
|
|
1570
2001
|
const existing = this.sink.indexOf(id);
|
|
1571
2002
|
const index = this.sink.addNode(id);
|
|
1572
|
-
if (existing !== INVALID_INDEX && this.
|
|
2003
|
+
if (existing !== INVALID_INDEX && this.hubs.has(index)) {
|
|
2004
|
+
this.report.warning(
|
|
2005
|
+
"validation-error",
|
|
2006
|
+
GRAPHML_ISSUE.HUB_ID_CLASH,
|
|
2007
|
+
`node "${String(id)}" has the id of the hub a star-expanded hyperedge created earlier; the node is merged into the hub`,
|
|
2008
|
+
{ line: node.line, element: String(id) },
|
|
2009
|
+
);
|
|
2010
|
+
} else if (existing !== INVALID_INDEX && this.declared.has(index)) {
|
|
1573
2011
|
this.report.warning(
|
|
1574
2012
|
"validation-error",
|
|
1575
2013
|
GRAPHML_ISSUE.DUPLICATE_NODE,
|
|
@@ -1644,10 +2082,15 @@ class GraphmlReader implements XmlHandler {
|
|
|
1644
2082
|
private beginEdge(attrs: ReadonlyMap<string, string>, line: number): void {
|
|
1645
2083
|
this.reportUnreadAttributes("edge", attrs, EDGE_ATTRIBUTES, line);
|
|
1646
2084
|
const graph = this.graphs[this.graphs.length - 1];
|
|
2085
|
+
graph.edges++;
|
|
2086
|
+
const idText = attrs.get("id");
|
|
2087
|
+
const sourceText = attrs.get("source");
|
|
2088
|
+
const targetText = attrs.get("target");
|
|
1647
2089
|
const edge: EdgeState = {
|
|
1648
2090
|
source: null,
|
|
1649
2091
|
target: null,
|
|
1650
|
-
|
|
2092
|
+
// an exporter that writes id="" for an edge without one: the same as no id
|
|
2093
|
+
id: idText === undefined || idText.length === 0 ? null : idText,
|
|
1651
2094
|
kind: graph.directed ? "directed" : "undirected",
|
|
1652
2095
|
sourcePort: attrs.get("sourceport") ?? null,
|
|
1653
2096
|
targetPort: attrs.get("targetport") ?? null,
|
|
@@ -1655,10 +2098,12 @@ class GraphmlReader implements XmlHandler {
|
|
|
1655
2098
|
pending: [],
|
|
1656
2099
|
failed: false,
|
|
1657
2100
|
line,
|
|
2101
|
+
keys: null,
|
|
2102
|
+
weightKey: null,
|
|
2103
|
+
sourceText: sourceText ?? null,
|
|
2104
|
+
targetText: targetText ?? null,
|
|
1658
2105
|
};
|
|
1659
2106
|
const where = { line, element: edge.id };
|
|
1660
|
-
const sourceText = attrs.get("source");
|
|
1661
|
-
const targetText = attrs.get("target");
|
|
1662
2107
|
if (sourceText === undefined || sourceText.length === 0) {
|
|
1663
2108
|
this.skipEdge(edge);
|
|
1664
2109
|
this.report.error("missing-value", GRAPHML_ISSUE.MISSING_ENDPOINT, "<edge> without a source", where);
|
|
@@ -1678,16 +2123,17 @@ class GraphmlReader implements XmlHandler {
|
|
|
1678
2123
|
}
|
|
1679
2124
|
const directedText = attrs.get("directed");
|
|
1680
2125
|
if (directedText !== undefined) {
|
|
1681
|
-
|
|
2126
|
+
// the schema types directed as xs:boolean: true, false, 1 or 0
|
|
2127
|
+
if (directedText === "true" || directedText === "1") {
|
|
1682
2128
|
edge.kind = "directed";
|
|
1683
|
-
} else if (directedText === "false") {
|
|
2129
|
+
} else if (directedText === "false" || directedText === "0") {
|
|
1684
2130
|
edge.kind = "undirected";
|
|
1685
2131
|
} else {
|
|
1686
2132
|
this.skipEdge(edge);
|
|
1687
2133
|
this.report.error(
|
|
1688
2134
|
"validation-error",
|
|
1689
2135
|
GRAPHML_ISSUE.INVALID_DIRECTED,
|
|
1690
|
-
`directed="${directedText}" is neither true nor false`,
|
|
2136
|
+
`directed="${directedText}" is neither true nor false (nor 1 or 0)`,
|
|
1691
2137
|
where,
|
|
1692
2138
|
);
|
|
1693
2139
|
}
|
|
@@ -1709,6 +2155,31 @@ class GraphmlReader implements XmlHandler {
|
|
|
1709
2155
|
this.skipEdge(edge);
|
|
1710
2156
|
return;
|
|
1711
2157
|
}
|
|
2158
|
+
if (
|
|
2159
|
+
(!this.options.addMissingNodes &&
|
|
2160
|
+
(this.sink.indexOf(edge.source) === INVALID_INDEX ||
|
|
2161
|
+
this.sink.indexOf(edge.target) === INVALID_INDEX)) ||
|
|
2162
|
+
(edge.id !== null && this.deferredEdgeIds.has(edge.id))
|
|
2163
|
+
) {
|
|
2164
|
+
// GraphML allows nodes and edges in any order: wait for the end of the document. An
|
|
2165
|
+
// edge reusing a waiting edge's id waits behind it, so the first one keeps the id.
|
|
2166
|
+
if (edge.id !== null) {
|
|
2167
|
+
this.deferredEdgeIds.add(edge.id);
|
|
2168
|
+
}
|
|
2169
|
+
this.deferred.push(edge);
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
this.addEdge(edge);
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
/**
|
|
2176
|
+
* Push an edge through the direction resolver and write its values.
|
|
2177
|
+
* @param edge - the edge, its endpoints known
|
|
2178
|
+
*/
|
|
2179
|
+
private addEdge(edge: EdgeState): void {
|
|
2180
|
+
if (edge.source === null || edge.target === null) {
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
1712
2183
|
const element = edge.id ?? `${String(edge.source)} -> ${String(edge.target)}`;
|
|
1713
2184
|
const where = { line: edge.line, element };
|
|
1714
2185
|
try {
|
|
@@ -1716,6 +2187,8 @@ class GraphmlReader implements XmlHandler {
|
|
|
1716
2187
|
this.requireNode(edge.source);
|
|
1717
2188
|
this.requireNode(edge.target);
|
|
1718
2189
|
}
|
|
2190
|
+
this.checkGraphEndpoint(edge.source, edge.sourceText, where);
|
|
2191
|
+
this.checkGraphEndpoint(edge.target, edge.targetText, where);
|
|
1719
2192
|
if (edge.id !== null && this.edgeIds.has(edge.id)) {
|
|
1720
2193
|
throw new GraphFormatError("E_DUPLICATE_EDGE_ID", `edge id "${edge.id}" is used twice`, {
|
|
1721
2194
|
id: edge.id,
|
|
@@ -1732,9 +2205,11 @@ class GraphmlReader implements XmlHandler {
|
|
|
1732
2205
|
}
|
|
1733
2206
|
if (edge.sourcePort !== null) {
|
|
1734
2207
|
this.sink.setEdgeValue(this.portColumn("source"), e, edge.sourcePort);
|
|
2208
|
+
this.portRefs.push([edge.source, edge.sourcePort]);
|
|
1735
2209
|
}
|
|
1736
2210
|
if (edge.targetPort !== null) {
|
|
1737
2211
|
this.sink.setEdgeValue(this.portColumn("target"), e, edge.targetPort);
|
|
2212
|
+
this.portRefs.push([edge.target, edge.targetPort]);
|
|
1738
2213
|
}
|
|
1739
2214
|
const { pending } = edge;
|
|
1740
2215
|
for (let i = 0; i < pending.length; i += 2) {
|
|
@@ -1757,6 +2232,24 @@ class GraphmlReader implements XmlHandler {
|
|
|
1757
2232
|
}
|
|
1758
2233
|
}
|
|
1759
2234
|
|
|
2235
|
+
/**
|
|
2236
|
+
* Report an endpoint that names a nested `<graph>` rather than a node (a node of that id is
|
|
2237
|
+
* then created under addMissingNodes).
|
|
2238
|
+
* @param id - the endpoint id
|
|
2239
|
+
* @param text - the endpoint as written, or null
|
|
2240
|
+
* @param where - the edge's location
|
|
2241
|
+
*/
|
|
2242
|
+
private checkGraphEndpoint(id: NodeId, text: string | null, where: Where): void {
|
|
2243
|
+
if (text !== null && this.graphIds.has(text) && this.sink.indexOf(id) === INVALID_INDEX) {
|
|
2244
|
+
this.report.warning(
|
|
2245
|
+
"validation-error",
|
|
2246
|
+
GRAPHML_ISSUE.GRAPH_ENDPOINT,
|
|
2247
|
+
`edge endpoint "${text}" names a <graph>, not a node; a node "${text}" is created`,
|
|
2248
|
+
where,
|
|
2249
|
+
);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
|
|
1760
2253
|
/**
|
|
1761
2254
|
* E_UNKNOWN_NODE when an endpoint was never declared (addMissingNodes false enforced by the
|
|
1762
2255
|
* importer, whatever the sink allows).
|
|
@@ -1826,6 +2319,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
1826
2319
|
* @param line - the line
|
|
1827
2320
|
*/
|
|
1828
2321
|
private beginHyperedge(attrs: ReadonlyMap<string, string>, line: number): void {
|
|
2322
|
+
this.reportUnreadAttributes("hyperedge", attrs, HYPEREDGE_ATTRIBUTES, line);
|
|
1829
2323
|
const id = attrs.get("id") ?? null;
|
|
1830
2324
|
const where = { line, element: id };
|
|
1831
2325
|
switch (this.options.hyperedges) {
|
|
@@ -1908,12 +2402,52 @@ class GraphmlReader implements XmlHandler {
|
|
|
1908
2402
|
if (hyperedge.failed) {
|
|
1909
2403
|
return;
|
|
1910
2404
|
}
|
|
2405
|
+
if (hyperedge.endpoints.length === 0) {
|
|
2406
|
+
this.skipEdge(hyperedge);
|
|
2407
|
+
this.report.error(
|
|
2408
|
+
"missing-value",
|
|
2409
|
+
GRAPHML_ISSUE.HYPEREDGE_ENDPOINT,
|
|
2410
|
+
"<hyperedge> without endpoints yields no edge",
|
|
2411
|
+
{ line: hyperedge.line, element: hyperedge.id },
|
|
2412
|
+
);
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2415
|
+
if (
|
|
2416
|
+
!this.options.addMissingNodes &&
|
|
2417
|
+
hyperedge.endpoints.some((endpoint) => this.sink.indexOf(endpoint.node) === INVALID_INDEX)
|
|
2418
|
+
) {
|
|
2419
|
+
this.deferred.push(hyperedge);
|
|
2420
|
+
return;
|
|
2421
|
+
}
|
|
2422
|
+
this.expandHyperedge(hyperedge);
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
/**
|
|
2426
|
+
* Expand a hyperedge to a star or a clique; under addMissingNodes false every endpoint must be
|
|
2427
|
+
* a declared node.
|
|
2428
|
+
* @param hyperedge - the hyperedge, its endpoints read
|
|
2429
|
+
*/
|
|
2430
|
+
private expandHyperedge(hyperedge: HyperedgeState): void {
|
|
1911
2431
|
const where = { line: hyperedge.line, element: hyperedge.id };
|
|
1912
2432
|
try {
|
|
2433
|
+
for (const endpoint of hyperedge.endpoints) {
|
|
2434
|
+
if (!this.options.addMissingNodes) {
|
|
2435
|
+
this.requireNode(endpoint.node);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
for (const node of new Set(hyperedge.endpoints.map((endpoint) => endpoint.node))) {
|
|
2439
|
+
this.countEndpoints(node, node);
|
|
2440
|
+
}
|
|
1913
2441
|
if (this.options.hyperedges === "star") {
|
|
1914
2442
|
this.expandStar(hyperedge, where);
|
|
1915
|
-
} else {
|
|
1916
|
-
this.
|
|
2443
|
+
} else if (this.expandClique(hyperedge, where) === 0) {
|
|
2444
|
+
this.skipEdge(hyperedge);
|
|
2445
|
+
this.report.error(
|
|
2446
|
+
"validation-error",
|
|
2447
|
+
GRAPHML_ISSUE.HYPEREDGE_ENDPOINT,
|
|
2448
|
+
"no two endpoints of this hyperedge can be joined (all in, all out, or only one); it yields no edge",
|
|
2449
|
+
where,
|
|
2450
|
+
);
|
|
1917
2451
|
}
|
|
1918
2452
|
} catch (err) {
|
|
1919
2453
|
this.skipEdge(hyperedge);
|
|
@@ -1936,6 +2470,7 @@ class GraphmlReader implements XmlHandler {
|
|
|
1936
2470
|
}
|
|
1937
2471
|
const hub = this.sink.addNode(hubId);
|
|
1938
2472
|
this.declared.add(hub);
|
|
2473
|
+
this.hubs.add(hub);
|
|
1939
2474
|
this.report.counts.nodes++;
|
|
1940
2475
|
if (this.hubHandle === INVALID_INDEX) {
|
|
1941
2476
|
this.hubHandle = this.sink.declareNodeColumn({
|
|
@@ -1972,9 +2507,11 @@ class GraphmlReader implements XmlHandler {
|
|
|
1972
2507
|
* joined undirected to every other endpoint; two endpoints of the same direction are not joined.
|
|
1973
2508
|
* @param hyperedge - the hyperedge
|
|
1974
2509
|
* @param where - the location for issues
|
|
2510
|
+
* @returns the number of edge elements added
|
|
1975
2511
|
*/
|
|
1976
|
-
private expandClique(hyperedge: HyperedgeState, where: Where):
|
|
2512
|
+
private expandClique(hyperedge: HyperedgeState, where: Where): number {
|
|
1977
2513
|
const { endpoints } = hyperedge;
|
|
2514
|
+
let added = 0;
|
|
1978
2515
|
for (let i = 0; i < endpoints.length; i++) {
|
|
1979
2516
|
for (let j = i + 1; j < endpoints.length; j++) {
|
|
1980
2517
|
const a = endpoints[i];
|
|
@@ -1989,9 +2526,11 @@ class GraphmlReader implements XmlHandler {
|
|
|
1989
2526
|
} else {
|
|
1990
2527
|
continue;
|
|
1991
2528
|
}
|
|
2529
|
+
added++;
|
|
1992
2530
|
this.report.counts.edges += this.sink.edgeCount - before;
|
|
1993
2531
|
}
|
|
1994
2532
|
}
|
|
2533
|
+
return added;
|
|
1995
2534
|
}
|
|
1996
2535
|
|
|
1997
2536
|
// ------------------------------------------------------------------ options and ids
|
|
@@ -2099,12 +2638,13 @@ function resolveYfiles(options: GraphmlImportOptions | undefined): "json" | "ski
|
|
|
2099
2638
|
* or when the head does not start with markup
|
|
2100
2639
|
*/
|
|
2101
2640
|
function sniffGraphml(head: Uint8Array): number {
|
|
2102
|
-
const text =
|
|
2641
|
+
const text = sniffXmlText(head.subarray(0, SNIFF_BYTES));
|
|
2103
2642
|
// Only a document that starts as markup: a JSON or CSV value may mention a `<graphml>` tag.
|
|
2104
2643
|
if (!/^\uFEFF?\s*</.test(text)) {
|
|
2105
2644
|
return 0;
|
|
2106
2645
|
}
|
|
2107
|
-
|
|
2646
|
+
// the root may carry a namespace prefix (<g:graphml xmlns:g="...">)
|
|
2647
|
+
const root = text.search(/<([\w.-]+:)?graphml[\s>/]/);
|
|
2108
2648
|
if (root >= 0) {
|
|
2109
2649
|
return text.includes(GRAPHML_NAMESPACE, root) ? 1 : 0.9;
|
|
2110
2650
|
}
|
|
@@ -2130,8 +2670,14 @@ async function importGraphml(
|
|
|
2130
2670
|
reportUnusedOptions(options, report, USED_OPTIONS);
|
|
2131
2671
|
const reader = new GraphmlReader(sink, report, common, yfilesMode);
|
|
2132
2672
|
try {
|
|
2133
|
-
await tokenizeXml(
|
|
2673
|
+
await tokenizeXml(
|
|
2674
|
+
textChunks(input, report, { ...common, declaredEncoding: xmlDeclaredEncoding, xml: true }),
|
|
2675
|
+
reader,
|
|
2676
|
+
);
|
|
2134
2677
|
} catch (err) {
|
|
2678
|
+
if (err instanceof XmlEmptyInputError) {
|
|
2679
|
+
report.fail(GRAPHML_ISSUE.EMPTY_INPUT, "the input is empty (no markup at all)", { line: err.line });
|
|
2680
|
+
}
|
|
2135
2681
|
if (err instanceof XmlSyntaxError) {
|
|
2136
2682
|
report.fail(GRAPHML_ISSUE.XML_SYNTAX, err.message, { line: err.line });
|
|
2137
2683
|
}
|
|
@@ -2142,11 +2688,45 @@ async function importGraphml(
|
|
|
2142
2688
|
return report.finish();
|
|
2143
2689
|
}
|
|
2144
2690
|
|
|
2145
|
-
/**
|
|
2691
|
+
/**
|
|
2692
|
+
* The GraphML importer.
|
|
2693
|
+
* @category Built-in formats
|
|
2694
|
+
*/
|
|
2146
2695
|
export const graphmlImporter: GraphImporter<GraphmlImportOptions> = Object.freeze({
|
|
2147
2696
|
format: FORMAT,
|
|
2697
|
+
options: Object.freeze(["yfiles"]),
|
|
2148
2698
|
extensions: EXTENSIONS,
|
|
2149
2699
|
mimeTypes: MIME_TYPES,
|
|
2150
2700
|
sniff: sniffGraphml,
|
|
2151
2701
|
import: importGraphml,
|
|
2152
2702
|
});
|
|
2703
|
+
|
|
2704
|
+
/** The elements GraphML defines, for telling a misplaced one from a foreign one. */
|
|
2705
|
+
const GRAPHML_ELEMENTS = new Set([
|
|
2706
|
+
"graphml",
|
|
2707
|
+
"key",
|
|
2708
|
+
"default",
|
|
2709
|
+
"graph",
|
|
2710
|
+
"node",
|
|
2711
|
+
"edge",
|
|
2712
|
+
"hyperedge",
|
|
2713
|
+
"endpoint",
|
|
2714
|
+
"port",
|
|
2715
|
+
"data",
|
|
2716
|
+
"desc",
|
|
2717
|
+
"locator",
|
|
2718
|
+
]);
|
|
2719
|
+
|
|
2720
|
+
/**
|
|
2721
|
+
* The message of an element skipped where it stands.
|
|
2722
|
+
* @param name - the element name
|
|
2723
|
+
* @returns a message that says where a GraphML element belongs, or that a foreign one is not GraphML
|
|
2724
|
+
*/
|
|
2725
|
+
function unknownElementMessage(name: string): string {
|
|
2726
|
+
if (name === "key") {
|
|
2727
|
+
return "element <key> is only allowed as a child of <graphml>, before the first <graph>; it was skipped";
|
|
2728
|
+
}
|
|
2729
|
+
return GRAPHML_ELEMENTS.has(name)
|
|
2730
|
+
? `element <${name}> is not allowed at this place in a GraphML file and was skipped`
|
|
2731
|
+
: `element <${name}> is not GraphML and was skipped`;
|
|
2732
|
+
}
|