@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
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import { GraphFormatError, INVALID_INDEX, MAX_COUNT, } from "@graphty/graph-format";
|
|
29
29
|
import { declareAttribute, declareCompanion, declareResolved, losesPrecision, parseDeclaredTemporal, parseDeclaredValue, PRECISION_CODE, } from "../../common/attributes.js";
|
|
30
|
-
import { COUNT_HINT_CODE, DUPLICATE_ATTRIBUTE_CODE, DUPLICATE_EDGE_ID_CODE, DUPLICATE_NODE_CODE, ID_MERGED_CODE, MISSING_ENDPOINT_CODE, MISSING_ID_CODE, NO_GRAPH_CODE, STRAY_TEXT_CODE, UNKNOWN_ELEMENT_CODE, UNKNOWN_PARENT_CODE, XML_SYNTAX_CODE, } from "../../common/codes.js";
|
|
31
|
-
import { parseDecimalText } from "../../common/declared-types.js";
|
|
30
|
+
import { BAD_VALUE_CODE, COUNT_HINT_CODE, COUNT_MISMATCH_CODE, DUPLICATE_ATTRIBUTE_CODE, DUPLICATE_EDGE_ID_CODE, DUPLICATE_NODE_CODE, EMPTY_INPUT_CODE, ID_MERGED_CODE, MISSING_ENDPOINT_CODE, MISSING_ID_CODE, MULTIPLE_GRAPHS_CODE, NO_GRAPH_CODE, PARENT_CYCLE_CODE, STRAY_TEXT_CODE, UNKNOWN_ELEMENT_CODE, UNKNOWN_PARENT_CODE, UNKNOWN_XML_ATTRIBUTE_CODE, XML_SYNTAX_CODE, } from "../../common/codes.js";
|
|
31
|
+
import { overflowsToInfinity, parseDecimalText } from "../../common/declared-types.js";
|
|
32
32
|
import { DirectionResolver } from "../../common/direction.js";
|
|
33
33
|
import { IdCoercer } from "../../common/ids.js";
|
|
34
34
|
import { textChunks, throwIfAborted } from "../../common/input.js";
|
|
@@ -37,44 +37,169 @@ import { reportSinkOptions, reportUnusedOptions, resolveImportOptions, } from ".
|
|
|
37
37
|
import { ImportReportBuilder } from "../../common/report.js";
|
|
38
38
|
import { parseTimeText, timeTextCompanion } from "../../common/temporal.js";
|
|
39
39
|
import { isWeightField, parseWeightText } from "../../common/weights.js";
|
|
40
|
-
import { isWhitespace, localName, tokenizeXml, xmlDeclaredEncoding, XmlSyntaxError, } from "../../common/xml.js";
|
|
40
|
+
import { isWhitespace, localName, sniffXmlText, tokenizeXml, xmlDeclaredEncoding, XmlEmptyInputError, XmlSyntaxError, } from "../../common/xml.js";
|
|
41
41
|
import { EDGE_DECLS, GEXF_FORMAT, GRAPH_MODES, NODE_DECLS, OPEN_END, OPEN_START, RESERVED_EDGE_NAMES, RESERVED_NODE_NAMES, temporalTableDecls, temporalTableName, TIME_FORMATS, TIME_REPRESENTATIONS, } from "./schema.js";
|
|
42
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* The document is not a GEXF document (no `<gexf>` root).
|
|
44
|
+
* @category Issue and loss codes
|
|
45
|
+
*/
|
|
43
46
|
export const NOT_GEXF_CODE = "E_NOT_GEXF";
|
|
44
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* The graph declares edges but no `<nodes>` section.
|
|
49
|
+
* @category Issue and loss codes
|
|
50
|
+
*/
|
|
45
51
|
export const MISSING_NODES_CODE = "E_GEXF_MISSING_NODES";
|
|
46
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* An edge `type` outside directed / undirected / mutual.
|
|
54
|
+
* @category Issue and loss codes
|
|
55
|
+
*/
|
|
47
56
|
export const EDGE_TYPE_CODE = "E_GEXF_EDGE_TYPE";
|
|
48
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* An `<attributes>` group without a class, or with an unknown one.
|
|
59
|
+
* @category Issue and loss codes
|
|
60
|
+
*/
|
|
49
61
|
export const ATTRIBUTES_CLASS_CODE = "E_GEXF_ATTRIBUTES_CLASS";
|
|
50
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* An `<attribute>` without an id.
|
|
64
|
+
* @category Issue and loss codes
|
|
65
|
+
*/
|
|
51
66
|
export const ATTRIBUTE_ID_CODE = "E_GEXF_ATTRIBUTE_ID";
|
|
52
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* A graph header value (`defaultedgetype`, `mode`, `timeformat`, ...) outside its set.
|
|
69
|
+
* @category Issue and loss codes
|
|
70
|
+
*/
|
|
53
71
|
export const HEADER_VALUE_CODE = "W_GEXF_HEADER_VALUE";
|
|
54
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* An `<attribute>` without a type (read as string).
|
|
74
|
+
* @category Issue and loss codes
|
|
75
|
+
*/
|
|
55
76
|
export const ATTRIBUTE_TYPE_CODE = "W_GEXF_ATTRIBUTE_TYPE";
|
|
56
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* An `<attvalue>` naming an attribute the document never declares (once per id).
|
|
79
|
+
* @category Issue and loss codes
|
|
80
|
+
*/
|
|
57
81
|
export const UNKNOWN_ATTRIBUTE_CODE = "W_GEXF_UNKNOWN_ATTRIBUTE";
|
|
58
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* An `<attvalue>` without a `for` or a `value`.
|
|
84
|
+
* @category Issue and loss codes
|
|
85
|
+
*/
|
|
59
86
|
export const ATTVALUE_SHAPE_CODE = "W_GEXF_ATTVALUE_SHAPE";
|
|
60
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* A timed value on an attribute declared in a static group (stored as dynamic anyway).
|
|
89
|
+
* @category Issue and loss codes
|
|
90
|
+
*/
|
|
61
91
|
export const TIMED_STATIC_CODE = "W_GEXF_TIMED_VALUE_ON_STATIC";
|
|
62
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* The `weight` XML attribute is present but `weightFrom` is null.
|
|
94
|
+
* @category Issue and loss codes
|
|
95
|
+
*/
|
|
63
96
|
export const WEIGHT_IGNORED_CODE = "W_GEXF_WEIGHT_IGNORED";
|
|
64
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Viz elements skipped under `viz: false`.
|
|
99
|
+
* @category Issue and loss codes
|
|
100
|
+
*/
|
|
65
101
|
export const VIZ_SKIPPED_CODE = "W_GEXF_VIZ_SKIPPED";
|
|
66
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* A 1.2 dynamic viz element (its bounds are dropped, the value kept).
|
|
104
|
+
* @category Issue and loss codes
|
|
105
|
+
*/
|
|
67
106
|
export const VIZ_DYNAMIC_CODE = "W_GEXF_VIZ_DYNAMIC_DROPPED";
|
|
68
107
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
108
|
+
* `startopen` / `endopen` on a `<spell>` stored closed. No longer recorded: the open bits of each spell are kept in
|
|
109
|
+
* the spells.open column. Kept so the exported code table is stable.
|
|
110
|
+
* @category Issue and loss codes
|
|
71
111
|
*/
|
|
72
112
|
export const SPELL_OPEN_CODE = "W_GEXF_SPELL_OPEN_DROPPED";
|
|
73
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* A viz element with a value that does not parse (the element is skipped).
|
|
115
|
+
* @category Issue and loss codes
|
|
116
|
+
*/
|
|
74
117
|
export const VIZ_VALUE_CODE = "W_GEXF_VIZ_VALUE";
|
|
75
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Both `start` and `startopen` (or `end` and `endopen`) on one element; the closed bound wins.
|
|
120
|
+
* @category Issue and loss codes
|
|
121
|
+
*/
|
|
76
122
|
export const OPEN_BOUND_CONFLICT_CODE = "W_GEXF_OPEN_BOUND_CONFLICT";
|
|
123
|
+
/**
|
|
124
|
+
* Both `timestamp` and `start` / `end` on one element or value; `start` / `end` win.
|
|
125
|
+
* @category Issue and loss codes
|
|
126
|
+
*/
|
|
127
|
+
export const TIMESTAMP_CONFLICT_CODE = "W_GEXF_TIMESTAMP_CONFLICT";
|
|
128
|
+
/**
|
|
129
|
+
* A value outside the `<options>` its attribute declares (the value is kept).
|
|
130
|
+
* @category Issue and loss codes
|
|
131
|
+
*/
|
|
132
|
+
export const VALUE_OUTSIDE_OPTIONS_CODE = "W_GEXF_VALUE_OUTSIDE_OPTIONS";
|
|
77
133
|
export { DUPLICATE_NODE_CODE, ID_MERGED_CODE, MISSING_ENDPOINT_CODE, MISSING_ID_CODE, NO_GRAPH_CODE, UNKNOWN_PARENT_CODE, };
|
|
134
|
+
/** The GEXF versions graph-io knows, and the version a namespace implies when `version` is missing. */
|
|
135
|
+
const VERSIONS = new Set(["1.0", "1.1", "1.2", "1.3"]);
|
|
136
|
+
/**
|
|
137
|
+
* The version a GEXF namespace names (`http://www.gexf.net/1.2draft`, `http://gexf.net/1.3`).
|
|
138
|
+
* @param ns - the root's namespace
|
|
139
|
+
* @returns the version, or null
|
|
140
|
+
*/
|
|
141
|
+
function namespaceVersion(ns) {
|
|
142
|
+
const match = ns === null ? null : /gexf\.net\/(1\.[0-3])/.exec(ns);
|
|
143
|
+
return match === null ? null : match[1];
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Whether a namespace is a GEXF viz namespace (`http://www.gexf.net/1.2draft/viz`, the 1.1
|
|
147
|
+
* triple-slash `http://gexf.net/1.1draft///viz`, `http://gexf.net/1.3/viz`, GEXF 1.0's `http://www.gephi.org/gexf/viz`).
|
|
148
|
+
* @param ns - the element's namespace
|
|
149
|
+
* @returns true for a viz namespace
|
|
150
|
+
*/
|
|
151
|
+
function isVizNamespace(ns) {
|
|
152
|
+
return ns !== null && /(gexf\.net\/.*viz|gephi\.org\/gexf\/viz)\/?$/.test(ns);
|
|
153
|
+
}
|
|
154
|
+
/** Distinct unknown attribute names reported one by one per element kind; the rest are counted in one issue. */
|
|
155
|
+
const MAX_UNKNOWN_ATTRIBUTE_NAMES = 16;
|
|
156
|
+
/** The XML attributes the importer reads on each element other than `<node>` / `<edge>`. */
|
|
157
|
+
const ELEMENT_ATTRIBUTES = {
|
|
158
|
+
gexf: new Set(["version", "variant"]),
|
|
159
|
+
meta: new Set(["lastmodifieddate"]),
|
|
160
|
+
graph: new Set([
|
|
161
|
+
"defaultedgetype",
|
|
162
|
+
"mode",
|
|
163
|
+
"type",
|
|
164
|
+
"timeformat",
|
|
165
|
+
"timerepresentation",
|
|
166
|
+
"idtype",
|
|
167
|
+
"start",
|
|
168
|
+
"end",
|
|
169
|
+
"timestamp",
|
|
170
|
+
"startopen",
|
|
171
|
+
"endopen",
|
|
172
|
+
]),
|
|
173
|
+
// `type` is GEXF 1.0's name for `mode` (static | dynamic)
|
|
174
|
+
attributes: new Set(["class", "mode", "type", "start", "end", "startopen", "endopen"]),
|
|
175
|
+
attribute: new Set(["id", "title", "type"]),
|
|
176
|
+
attvalue: new Set(["for", "value", "start", "end", "timestamp", "startopen", "endopen", "id"]),
|
|
177
|
+
spell: new Set(["start", "end", "timestamp", "startopen", "endopen"]),
|
|
178
|
+
parent: new Set(["for"]),
|
|
179
|
+
nodes: new Set(["count"]),
|
|
180
|
+
edges: new Set(["count"]),
|
|
181
|
+
};
|
|
182
|
+
/** A `count` hint as written: decimal digits only (as GraphML's parse.nodes). */
|
|
183
|
+
const COUNT_TEXT = /^\d+$/;
|
|
184
|
+
/** An xs:date / xs:dateTime text (`lastmodifieddate`). */
|
|
185
|
+
const DATE_TEXT = /^-?\d{4,}-\d{2}-\d{2}(.*)$/s;
|
|
186
|
+
/** The time of an xs:dateTime, after its date. */
|
|
187
|
+
const TIME_TEXT = /^T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?/;
|
|
188
|
+
/** The zone that may end an xs:date or xs:dateTime. */
|
|
189
|
+
const ZONE_TEXT = /^(?:Z|[+-]\d{2}:\d{2})?$/;
|
|
190
|
+
/**
|
|
191
|
+
* Whether a text is an xs:date or xs:dateTime.
|
|
192
|
+
* @param text - the text
|
|
193
|
+
* @returns true when it is
|
|
194
|
+
*/
|
|
195
|
+
function isDateText(text) {
|
|
196
|
+
const m = DATE_TEXT.exec(text);
|
|
197
|
+
if (m === null) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
const time = TIME_TEXT.exec(m[1])?.[0] ?? "";
|
|
201
|
+
return ZONE_TEXT.test(m[1].slice(time.length));
|
|
202
|
+
}
|
|
78
203
|
const EDGE_TYPES = new Set(["directed", "undirected", "mutual"]);
|
|
79
204
|
/**
|
|
80
205
|
* An edge type as a keyword of {@link EDGE_TYPES}: the schema spells them in lower case, but files
|
|
@@ -274,7 +399,15 @@ class GexfReader {
|
|
|
274
399
|
index: INVALID_INDEX,
|
|
275
400
|
mirror: INVALID_INDEX,
|
|
276
401
|
where: { line: 0, element: null },
|
|
402
|
+
xmlWeight: null,
|
|
277
403
|
};
|
|
404
|
+
/** The `for` ids of the static values of the open `<attvalues>`, for the duplicate check. */
|
|
405
|
+
this.attvalueIds = new Set();
|
|
406
|
+
/** The open `<nodes>` / `<edges>` sections: their count hint and the elements read directly in them. */
|
|
407
|
+
this.sections = [];
|
|
408
|
+
/** Unknown attribute names reported per element kind, and how many more were not listed. */
|
|
409
|
+
this.unknownNames = new Map();
|
|
410
|
+
this.unknownOverflow = new Map();
|
|
278
411
|
/** The one `<attvalue>` location, reused per value (its fields are copied into every issue). */
|
|
279
412
|
this.valueWhere = { line: 0, element: null };
|
|
280
413
|
this.attributeGroup = null;
|
|
@@ -321,15 +454,26 @@ class GexfReader {
|
|
|
321
454
|
};
|
|
322
455
|
}
|
|
323
456
|
// ---------------------------------------------------------------- events
|
|
324
|
-
start(name, attrs, line) {
|
|
457
|
+
start(name, attrs, line, ns = "") {
|
|
325
458
|
const parent = this.ctx.length === 0 ? null : this.ctx[this.ctx.length - 1];
|
|
326
459
|
const local = localName(name);
|
|
460
|
+
if (ns === null &&
|
|
461
|
+
parent !== null &&
|
|
462
|
+
parent !== Ctx.Skip &&
|
|
463
|
+
parent !== Ctx.MetaText &&
|
|
464
|
+
parent !== Ctx.AttributeText &&
|
|
465
|
+
!name.startsWith("viz:")) {
|
|
466
|
+
// an undeclared prefix or a name that is not namespace-well-formed (<:node>, <a:b:node>)
|
|
467
|
+
this.unknownElement(name, line);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
327
470
|
switch (parent) {
|
|
328
471
|
case null:
|
|
329
472
|
if (local !== "gexf") {
|
|
330
473
|
this.report.fail(NOT_GEXF_CODE, `the root element is <${name}>, not <gexf>`, { line });
|
|
331
474
|
}
|
|
332
|
-
this.
|
|
475
|
+
this.checkAttributes("gexf", attrs, line);
|
|
476
|
+
this.readRoot(attrs, ns, line);
|
|
333
477
|
this.ctx.push(Ctx.Gexf);
|
|
334
478
|
return;
|
|
335
479
|
case Ctx.Gexf:
|
|
@@ -351,7 +495,7 @@ class GexfReader {
|
|
|
351
495
|
this.startInNodes(name, local, attrs, line);
|
|
352
496
|
return;
|
|
353
497
|
case Ctx.Node:
|
|
354
|
-
this.startInNode(name, local, attrs, line);
|
|
498
|
+
this.startInNode(name, local, attrs, line, ns);
|
|
355
499
|
return;
|
|
356
500
|
case Ctx.Attvalues:
|
|
357
501
|
this.startInAttvalues(name, local, attrs, line);
|
|
@@ -366,7 +510,7 @@ class GexfReader {
|
|
|
366
510
|
this.startInEdges(name, local, attrs, line);
|
|
367
511
|
return;
|
|
368
512
|
case Ctx.Edge:
|
|
369
|
-
this.startInEdge(name, local, attrs, line);
|
|
513
|
+
this.startInEdge(name, local, attrs, line, ns);
|
|
370
514
|
return;
|
|
371
515
|
case Ctx.MetaText:
|
|
372
516
|
case Ctx.AttributeText:
|
|
@@ -406,6 +550,10 @@ class GexfReader {
|
|
|
406
550
|
case Ctx.Node:
|
|
407
551
|
this.nodeStack.pop();
|
|
408
552
|
return;
|
|
553
|
+
case Ctx.Nodes:
|
|
554
|
+
case Ctx.Edges:
|
|
555
|
+
this.finishSection();
|
|
556
|
+
return;
|
|
409
557
|
case Ctx.Edge:
|
|
410
558
|
this.edge = null;
|
|
411
559
|
return;
|
|
@@ -416,8 +564,6 @@ class GexfReader {
|
|
|
416
564
|
this.finishSpells();
|
|
417
565
|
return;
|
|
418
566
|
case Ctx.Gexf:
|
|
419
|
-
case Ctx.Nodes:
|
|
420
|
-
case Ctx.Edges:
|
|
421
567
|
case Ctx.Attvalues:
|
|
422
568
|
case Ctx.Skip:
|
|
423
569
|
case undefined:
|
|
@@ -458,16 +604,30 @@ class GexfReader {
|
|
|
458
604
|
if (!this.graphSeen) {
|
|
459
605
|
this.report.fail(NO_GRAPH_CODE, "the document has no <graph> element");
|
|
460
606
|
}
|
|
607
|
+
for (const [kind, more] of this.unknownOverflow) {
|
|
608
|
+
this.report.warning("unsupported", UNKNOWN_XML_ATTRIBUTE_CODE, `${more} more distinct attribute names on <${kind}> are not GEXF attributes and are not kept (not listed one by one)`, { element: kind });
|
|
609
|
+
}
|
|
461
610
|
}
|
|
462
611
|
// ---------------------------------------------------------------- root, meta, graph
|
|
463
612
|
/**
|
|
464
|
-
* Record the root's version
|
|
613
|
+
* Record the root's version: as written when known; the namespace's when absent; an unknown
|
|
614
|
+
* one is kept as written and reported.
|
|
465
615
|
* @param attrs - the `<gexf>` attributes
|
|
616
|
+
* @param ns - the root's namespace
|
|
617
|
+
* @param line - the line
|
|
466
618
|
*/
|
|
467
|
-
readRoot(attrs) {
|
|
619
|
+
readRoot(attrs, ns, line) {
|
|
468
620
|
const version = attrs.get("version");
|
|
469
621
|
if (version !== undefined && version.length > 0) {
|
|
470
622
|
this.meta.sourceVersion = version;
|
|
623
|
+
if (!VERSIONS.has(version)) {
|
|
624
|
+
this.report.warning("validation-error", HEADER_VALUE_CODE, `<gexf version="${version}"> is not a GEXF version graph-io knows (1.0 to 1.3); read as GEXF 1.3`, { line, element: "version" });
|
|
625
|
+
}
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
const implied = namespaceVersion(ns);
|
|
629
|
+
if (implied !== null) {
|
|
630
|
+
this.meta.sourceVersion = implied;
|
|
471
631
|
}
|
|
472
632
|
}
|
|
473
633
|
/**
|
|
@@ -480,21 +640,27 @@ class GexfReader {
|
|
|
480
640
|
startInGexf(name, local, attrs, line) {
|
|
481
641
|
switch (local) {
|
|
482
642
|
case "meta": {
|
|
643
|
+
this.checkAttributes("meta", attrs, line);
|
|
483
644
|
const modified = attrs.get("lastmodifieddate");
|
|
484
645
|
if (modified !== undefined && modified.length > 0) {
|
|
485
646
|
this.meta.modified = modified;
|
|
647
|
+
if (!isDateText(modified.trim())) {
|
|
648
|
+
this.report.warning("validation-error", HEADER_VALUE_CODE, `<meta lastmodifieddate="${modified}"> is not an xs:date; kept as written`, { line, element: "lastmodifieddate" });
|
|
649
|
+
}
|
|
486
650
|
}
|
|
487
651
|
this.ctx.push(Ctx.Meta);
|
|
488
652
|
return;
|
|
489
653
|
}
|
|
490
654
|
case "graph":
|
|
655
|
+
this.checkAttributes("graph", attrs, line);
|
|
491
656
|
if (this.graphSeen) {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
657
|
+
// the schema allows one graph; merging is what the GraphML importer does too
|
|
658
|
+
this.report.warnOnce("unsupported", MULTIPLE_GRAPHS_CODE, "a second <graph> element; its nodes and edges are merged into the first and its header is ignored", { line });
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
this.graphSeen = true;
|
|
662
|
+
this.readHeader(attrs, line);
|
|
495
663
|
}
|
|
496
|
-
this.graphSeen = true;
|
|
497
|
-
this.readHeader(attrs, line);
|
|
498
664
|
this.ctx.push(Ctx.Graph);
|
|
499
665
|
return;
|
|
500
666
|
default:
|
|
@@ -538,9 +704,11 @@ class GexfReader {
|
|
|
538
704
|
this.metaField = null;
|
|
539
705
|
this.metaText = "";
|
|
540
706
|
}
|
|
541
|
-
/** Close `<meta>`:
|
|
707
|
+
/** Close `<meta>`: the patch is applied with the graph header, or now when `<meta>` follows the graph. */
|
|
542
708
|
finishMeta() {
|
|
543
|
-
|
|
709
|
+
if (this.graphSeen) {
|
|
710
|
+
this.sink.setMeta(this.meta);
|
|
711
|
+
}
|
|
544
712
|
}
|
|
545
713
|
/**
|
|
546
714
|
* Read `<graph defaultedgetype mode timeformat timerepresentation idtype start end timestamp>`
|
|
@@ -594,7 +762,7 @@ class GexfReader {
|
|
|
594
762
|
const idType = attrs.get("idtype");
|
|
595
763
|
if (idType !== undefined) {
|
|
596
764
|
if (idType === "string") {
|
|
597
|
-
// informational only: Gephi writes idtype="string" unconditionally, so
|
|
765
|
+
// informational only: Gephi writes idtype="string" unconditionally, so honoring it
|
|
598
766
|
// would turn every Gephi export's integer ids into text; the canonical rule of
|
|
599
767
|
// design section 4.1 applies and the exporter's check() reports W_ID_TEXT_TYPE
|
|
600
768
|
patch.idType = "string";
|
|
@@ -642,39 +810,59 @@ class GexfReader {
|
|
|
642
810
|
return;
|
|
643
811
|
case "nodes":
|
|
644
812
|
this.nodesSeen = true;
|
|
645
|
-
this.
|
|
646
|
-
this.ctx.push(Ctx.Nodes);
|
|
813
|
+
this.beginSection(attrs, "node", line);
|
|
647
814
|
return;
|
|
648
815
|
case "edges":
|
|
649
816
|
if (!this.nodesSeen) {
|
|
650
817
|
this.report.error("validation-error", MISSING_NODES_CODE, "the graph declares <edges> but no <nodes>", { line });
|
|
651
818
|
}
|
|
652
819
|
this.resolveParents();
|
|
653
|
-
this.
|
|
654
|
-
this.ctx.push(Ctx.Edges);
|
|
820
|
+
this.beginSection(attrs, "edge", line);
|
|
655
821
|
return;
|
|
656
822
|
default:
|
|
657
823
|
this.unknownElement(name, line);
|
|
658
824
|
}
|
|
659
825
|
}
|
|
826
|
+
/**
|
|
827
|
+
* Open a `<nodes>` / `<edges>` section: its count hint is reserved and later compared with the
|
|
828
|
+
* elements the section holds.
|
|
829
|
+
* @param attrs - the section's attributes
|
|
830
|
+
* @param domain - node or edge
|
|
831
|
+
* @param line - the line
|
|
832
|
+
*/
|
|
833
|
+
beginSection(attrs, domain, line) {
|
|
834
|
+
this.checkAttributes(`${domain}s`, attrs, line);
|
|
835
|
+
this.sections.push({ domain, hint: this.reserve(attrs, domain, line), seen: 0, line });
|
|
836
|
+
this.ctx.push(domain === "node" ? Ctx.Nodes : Ctx.Edges);
|
|
837
|
+
}
|
|
838
|
+
/** Close a `<nodes>` / `<edges>` section: a count hint that disagrees is reported. */
|
|
839
|
+
finishSection() {
|
|
840
|
+
const section = this.sections.pop();
|
|
841
|
+
if (section?.hint === undefined || section.hint === null || section.hint === section.seen) {
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
this.report.warning("validation-error", COUNT_MISMATCH_CODE, `<${section.domain}s count="${section.hint}"> but the section holds ${section.seen} <${section.domain}> elements (nested ones included)`, { line: section.line, element: "count" });
|
|
845
|
+
}
|
|
660
846
|
/**
|
|
661
847
|
* Pass a section's `count` hint to the sink; a hint the sink cannot reserve is a warning.
|
|
662
848
|
* @param attrs - the `<nodes>` or `<edges>` attributes
|
|
663
849
|
* @param domain - which count it is
|
|
664
850
|
* @param line - the line
|
|
851
|
+
* @returns the hint, or null when absent or ignored
|
|
665
852
|
*/
|
|
666
853
|
reserve(attrs, domain, line) {
|
|
667
854
|
const text = attrs.get("count");
|
|
668
855
|
if (text === undefined) {
|
|
669
|
-
return;
|
|
856
|
+
return null;
|
|
670
857
|
}
|
|
671
|
-
|
|
672
|
-
|
|
858
|
+
// decimal digits only: Number() would read "" as 0, "0x10" as 16 and "1e3" as 1000
|
|
859
|
+
const count = COUNT_TEXT.test(text) ? Number(text) : Number.NaN;
|
|
860
|
+
if (!Number.isSafeInteger(count) || count > MAX_COUNT) {
|
|
673
861
|
this.report.warning("validation-error", COUNT_HINT_CODE, `<${domain}s count="${text}"> is not a count the sink can reserve; ignored`, { line, element: "count" });
|
|
674
|
-
return;
|
|
862
|
+
return null;
|
|
675
863
|
}
|
|
676
864
|
if (count === 0) {
|
|
677
|
-
return;
|
|
865
|
+
return count;
|
|
678
866
|
}
|
|
679
867
|
try {
|
|
680
868
|
if (domain === "node") {
|
|
@@ -693,6 +881,7 @@ class GexfReader {
|
|
|
693
881
|
element: "count",
|
|
694
882
|
});
|
|
695
883
|
}
|
|
884
|
+
return count;
|
|
696
885
|
}
|
|
697
886
|
// ---------------------------------------------------------------- attribute declarations
|
|
698
887
|
/**
|
|
@@ -701,6 +890,7 @@ class GexfReader {
|
|
|
701
890
|
* @param line - the line
|
|
702
891
|
*/
|
|
703
892
|
beginAttributes(attrs, line) {
|
|
893
|
+
this.checkAttributes("attributes", attrs, line);
|
|
704
894
|
const cls = attrs.get("class");
|
|
705
895
|
const where = { line };
|
|
706
896
|
let domain = null;
|
|
@@ -712,7 +902,7 @@ class GexfReader {
|
|
|
712
902
|
? "<attributes> without a class"
|
|
713
903
|
: `<attributes class="${cls}"> is neither node nor edge`, where);
|
|
714
904
|
}
|
|
715
|
-
const mode = attrs.get("mode");
|
|
905
|
+
const mode = attrs.get("mode") ?? attrs.get("type");
|
|
716
906
|
if (mode !== undefined && mode !== "static" && mode !== "dynamic") {
|
|
717
907
|
this.warnHeader("mode", mode, where);
|
|
718
908
|
}
|
|
@@ -731,6 +921,7 @@ class GexfReader {
|
|
|
731
921
|
this.unknownElement(name, line);
|
|
732
922
|
return;
|
|
733
923
|
}
|
|
924
|
+
this.checkAttributes("attribute", attrs, line);
|
|
734
925
|
const group = this.attributeGroup;
|
|
735
926
|
if (group === null || group.domain === null) {
|
|
736
927
|
// the group's class was refused; its attributes are dropped with it
|
|
@@ -810,13 +1001,20 @@ class GexfReader {
|
|
|
810
1001
|
report.warning("validation-error", DUPLICATE_ATTRIBUTE_CODE, `${domain} attribute "${id}" is declared twice; the first declaration is kept`, where);
|
|
811
1002
|
return;
|
|
812
1003
|
}
|
|
1004
|
+
if (this.unknownAttributes.has(`${domain}:${id}`)) {
|
|
1005
|
+
report.warning("missing-value", UNKNOWN_ATTRIBUTE_CODE, `${domain} attribute "${id}" is declared after values that use it; those values were dropped (declare <attributes> before <nodes>)`, where);
|
|
1006
|
+
}
|
|
813
1007
|
const title = attrs.get("title") ?? null;
|
|
814
1008
|
const type = attrs.get("type") ?? null;
|
|
815
1009
|
if (type === null) {
|
|
816
1010
|
report.warning("validation-error", ATTRIBUTE_TYPE_CODE, `attribute "${id}" declares no type; read as string`, where);
|
|
817
1011
|
}
|
|
818
1012
|
const name = title !== null && title.length > 0 ? title : id;
|
|
819
|
-
if (domain === "edge" && isWeightField(name, options.weightFrom)) {
|
|
1013
|
+
if (domain === "edge" && this.weightAttr !== null && isWeightField(name, options.weightFrom)) {
|
|
1014
|
+
// the first attribute of that title stays THE weight; this one becomes a plain column
|
|
1015
|
+
report.warning("validation-error", DUPLICATE_ATTRIBUTE_CODE, `edge attribute "${id}" is also titled ${name}; attribute "${this.weightAttr.id}" is the weight, "${id}" is kept as a column`, where);
|
|
1016
|
+
}
|
|
1017
|
+
else if (domain === "edge" && isWeightField(name, options.weightFrom)) {
|
|
820
1018
|
const declared = declareAttribute({ format: GEXF_FORMAT, id, title, type, long: options.long });
|
|
821
1019
|
for (const issue of declared.issues) {
|
|
822
1020
|
report.warning(issue.category, issue.code, issue.message, where);
|
|
@@ -827,7 +1025,8 @@ class GexfReader {
|
|
|
827
1025
|
}
|
|
828
1026
|
const declaredNames = domain === "node" ? this.declaredNodeNames : this.declaredEdgeNames;
|
|
829
1027
|
const reserved = domain === "node" ? RESERVED_NODE_NAMES : RESERVED_EDGE_NAMES;
|
|
830
|
-
const
|
|
1028
|
+
const weightName = domain === "edge" && this.weightAttr !== null ? options.weightFrom : null;
|
|
1029
|
+
const taken = (candidate) => reserved.has(candidate) || declaredNames.has(candidate) || candidate === weightName;
|
|
831
1030
|
const input = {
|
|
832
1031
|
format: GEXF_FORMAT,
|
|
833
1032
|
id,
|
|
@@ -900,7 +1099,9 @@ class GexfReader {
|
|
|
900
1099
|
const enclosing = this.nodeStack.length === 0 ? INVALID_INDEX : this.nodeStack[this.nodeStack.length - 1].index;
|
|
901
1100
|
const idText = attrs.get("id");
|
|
902
1101
|
const where = { line, element: idText ?? attrs.get("label") ?? null };
|
|
1102
|
+
this.countInSection("node");
|
|
903
1103
|
this.checkAttributeNames(attrs, NODE_ATTRIBUTES, "node", where);
|
|
1104
|
+
// GEXF types ids as xs:string: id="" is a legal id (the exporter writes it), only a missing one is not
|
|
904
1105
|
if (idText === undefined) {
|
|
905
1106
|
report.error("missing-value", MISSING_ID_CODE, "<node> without an id", where);
|
|
906
1107
|
report.counts.skippedNodes++;
|
|
@@ -908,6 +1109,9 @@ class GexfReader {
|
|
|
908
1109
|
this.ctx.push(Ctx.Node);
|
|
909
1110
|
return;
|
|
910
1111
|
}
|
|
1112
|
+
if (this.meta.idType === "integer" && !/^\s*[+-]?\d+\s*$/.test(idText)) {
|
|
1113
|
+
report.warnOnce("validation-error", HEADER_VALUE_CODE, `node id "${idText}" is not an integer although the graph declares idtype="integer"; ids follow the ids option`, where, `${HEADER_VALUE_CODE}:idtype`);
|
|
1114
|
+
}
|
|
911
1115
|
let index;
|
|
912
1116
|
try {
|
|
913
1117
|
const id = this.coerceId(idText, where);
|
|
@@ -930,11 +1134,17 @@ class GexfReader {
|
|
|
930
1134
|
}
|
|
931
1135
|
this.nodeStack.push({ index, where });
|
|
932
1136
|
this.ctx.push(Ctx.Node);
|
|
1137
|
+
// three independent parts: a bad pid must not cost the label or the lifetime
|
|
933
1138
|
try {
|
|
934
1139
|
const label = attrs.get("label");
|
|
935
1140
|
if (label !== undefined) {
|
|
936
1141
|
this.nodeColumns.label.set(sink, report, index, label, where);
|
|
937
1142
|
}
|
|
1143
|
+
}
|
|
1144
|
+
catch (err) {
|
|
1145
|
+
report.recordError(err, where);
|
|
1146
|
+
}
|
|
1147
|
+
try {
|
|
938
1148
|
const pid = attrs.get("pid");
|
|
939
1149
|
if (pid !== undefined) {
|
|
940
1150
|
this.parentRefs.push({
|
|
@@ -947,28 +1157,50 @@ class GexfReader {
|
|
|
947
1157
|
else if (enclosing !== INVALID_INDEX) {
|
|
948
1158
|
this.parentRefs.push({ child: index, parentId: null, parentIndex: enclosing, where });
|
|
949
1159
|
}
|
|
1160
|
+
}
|
|
1161
|
+
catch (err) {
|
|
1162
|
+
report.recordError(err, where);
|
|
1163
|
+
}
|
|
1164
|
+
try {
|
|
950
1165
|
this.writeLifetime(attrs, index, this.nodeColumns, where);
|
|
951
1166
|
}
|
|
952
1167
|
catch (err) {
|
|
953
1168
|
report.recordError(err, where);
|
|
954
1169
|
}
|
|
955
1170
|
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Count a `<node>` / `<edge>` element in the open sections of its kind, for the count hint
|
|
1173
|
+
* check: a `<nodes count>` counts the nodes nested in its nodes too.
|
|
1174
|
+
* @param domain - node or edge
|
|
1175
|
+
*/
|
|
1176
|
+
countInSection(domain) {
|
|
1177
|
+
for (const section of this.sections) {
|
|
1178
|
+
if (section.domain === domain) {
|
|
1179
|
+
section.seen++;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
956
1183
|
/**
|
|
957
1184
|
* A child of `<node>`.
|
|
958
1185
|
* @param name - the element name
|
|
959
1186
|
* @param local - its local name
|
|
960
1187
|
* @param attrs - its attributes
|
|
961
1188
|
* @param line - the line
|
|
1189
|
+
* @param ns - the element's namespace
|
|
962
1190
|
*/
|
|
963
|
-
startInNode(name, local, attrs, line) {
|
|
1191
|
+
startInNode(name, local, attrs, line, ns) {
|
|
964
1192
|
const frame = this.nodeStack[this.nodeStack.length - 1];
|
|
965
1193
|
if (frame.index === INVALID_INDEX && local !== "nodes") {
|
|
966
1194
|
// the node was skipped; its children are too
|
|
967
1195
|
this.ctx.push(Ctx.Skip);
|
|
968
1196
|
return;
|
|
969
1197
|
}
|
|
1198
|
+
if (frame.index === INVALID_INDEX) {
|
|
1199
|
+
this.report.error("missing-value", UNKNOWN_PARENT_CODE, "the nodes nested in this skipped <node> lose their parent; they are read as top-level nodes", { line });
|
|
1200
|
+
}
|
|
970
1201
|
switch (local) {
|
|
971
1202
|
case "attvalues":
|
|
1203
|
+
this.attvalueIds.clear();
|
|
972
1204
|
this.ctx.push(Ctx.Attvalues);
|
|
973
1205
|
return;
|
|
974
1206
|
case "parents":
|
|
@@ -982,13 +1214,16 @@ class GexfReader {
|
|
|
982
1214
|
this.ctx.push(Ctx.Spells);
|
|
983
1215
|
return;
|
|
984
1216
|
case "nodes":
|
|
985
|
-
this.
|
|
986
|
-
this.ctx.push(Ctx.Nodes);
|
|
1217
|
+
this.beginSection(attrs, "node", line);
|
|
987
1218
|
return;
|
|
988
1219
|
case "color":
|
|
989
1220
|
case "position":
|
|
990
1221
|
case "size":
|
|
991
1222
|
case "shape":
|
|
1223
|
+
if (!isViz(name, ns)) {
|
|
1224
|
+
this.unknownElement(name, line);
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
992
1227
|
this.writeViz(name, local, attrs, "node", frame.index, frame.where);
|
|
993
1228
|
this.ctx.push(Ctx.Skip);
|
|
994
1229
|
return;
|
|
@@ -1009,10 +1244,11 @@ class GexfReader {
|
|
|
1009
1244
|
return;
|
|
1010
1245
|
}
|
|
1011
1246
|
this.ctx.push(Ctx.Skip);
|
|
1247
|
+
this.checkAttributes("parent", attrs, line);
|
|
1012
1248
|
const frame = this.nodeStack[this.nodeStack.length - 1];
|
|
1013
1249
|
const text = attrs.get("for");
|
|
1014
1250
|
if (text === undefined) {
|
|
1015
|
-
this.report.
|
|
1251
|
+
this.report.error("missing-value", UNKNOWN_PARENT_CODE, "<parent> without a for attribute names no parent", frame.where);
|
|
1016
1252
|
return;
|
|
1017
1253
|
}
|
|
1018
1254
|
try {
|
|
@@ -1040,6 +1276,20 @@ class GexfReader {
|
|
|
1040
1276
|
const lists = this.parentsRefs;
|
|
1041
1277
|
this.parentRefs = [];
|
|
1042
1278
|
this.parentsRefs = [];
|
|
1279
|
+
// each node's parent so far, as the parent column holds it: a link closes a cycle when the
|
|
1280
|
+
// child is the parent or one of its ancestors. A repeated link (a duplicate node declaring
|
|
1281
|
+
// the same pid) is not a cycle, which an undirected union-find would wrongly report.
|
|
1282
|
+
// ponytail: O(depth) per link, so O(n * depth); fine for real hierarchies, add path
|
|
1283
|
+
// compression over an ancestry index if a deep chain ever shows up in a profile.
|
|
1284
|
+
const parentOf = new Map();
|
|
1285
|
+
const isAncestorOrSelf = (candidate, of) => {
|
|
1286
|
+
for (let at = of; at !== undefined; at = parentOf.get(at)) {
|
|
1287
|
+
if (at === candidate) {
|
|
1288
|
+
return true;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
return false;
|
|
1292
|
+
};
|
|
1043
1293
|
for (const ref of refs) {
|
|
1044
1294
|
let parent = ref.parentIndex;
|
|
1045
1295
|
if (ref.parentId !== null) {
|
|
@@ -1049,8 +1299,16 @@ class GexfReader {
|
|
|
1049
1299
|
continue;
|
|
1050
1300
|
}
|
|
1051
1301
|
}
|
|
1302
|
+
if (parentOf.get(ref.child) === parent) {
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
if (isAncestorOrSelf(ref.child, parent)) {
|
|
1306
|
+
report.error("validation-error", PARENT_CYCLE_CODE, `the parent link of node "${ref.where.element ?? "?"}" would close a parent cycle; it is dropped`, ref.where);
|
|
1307
|
+
continue;
|
|
1308
|
+
}
|
|
1052
1309
|
try {
|
|
1053
1310
|
this.parentColumn.set(sink, report, ref.child, parent, ref.where);
|
|
1311
|
+
parentOf.set(ref.child, parent);
|
|
1054
1312
|
}
|
|
1055
1313
|
catch (err) {
|
|
1056
1314
|
report.recordError(err, ref.where);
|
|
@@ -1105,7 +1363,9 @@ class GexfReader {
|
|
|
1105
1363
|
const { where } = this.edgeFrame;
|
|
1106
1364
|
where.line = line;
|
|
1107
1365
|
where.element = idText ?? edgeElement(sourceText, targetText);
|
|
1366
|
+
this.countInSection("edge");
|
|
1108
1367
|
this.checkAttributeNames(attrs, EDGE_ATTRIBUTES, "edge", where);
|
|
1368
|
+
// source="" names the node whose id is "" (legal in GEXF), so only a missing endpoint is E_MISSING_ENDPOINT
|
|
1109
1369
|
if (sourceText === undefined || targetText === undefined) {
|
|
1110
1370
|
report.error("missing-value", MISSING_ENDPOINT_CODE, `<edge> without a ${sourceText === undefined ? "source" : "target"}`, where);
|
|
1111
1371
|
report.counts.skippedEdges++;
|
|
@@ -1122,13 +1382,10 @@ class GexfReader {
|
|
|
1122
1382
|
}
|
|
1123
1383
|
kind = type;
|
|
1124
1384
|
}
|
|
1125
|
-
if (idText !== undefined) {
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
return;
|
|
1130
|
-
}
|
|
1131
|
-
this.edgeIds.add(idText);
|
|
1385
|
+
if (idText !== undefined && this.edgeIds.has(idText)) {
|
|
1386
|
+
report.error("validation-error", DUPLICATE_EDGE_ID_CODE, `edge id "${idText}" is declared more than once; the edge is skipped`, where);
|
|
1387
|
+
report.counts.skippedEdges++;
|
|
1388
|
+
return;
|
|
1132
1389
|
}
|
|
1133
1390
|
let index;
|
|
1134
1391
|
try {
|
|
@@ -1156,9 +1413,14 @@ class GexfReader {
|
|
|
1156
1413
|
report.counts.skippedEdges++;
|
|
1157
1414
|
return;
|
|
1158
1415
|
}
|
|
1416
|
+
// the id is claimed only by an edge that was added, so a rejected edge does not cost the next
|
|
1417
|
+
if (idText !== undefined) {
|
|
1418
|
+
this.edgeIds.add(idText);
|
|
1419
|
+
}
|
|
1159
1420
|
const frame = this.edgeFrame;
|
|
1160
1421
|
frame.index = index;
|
|
1161
1422
|
frame.mirror = this.resolver.lastMirror;
|
|
1423
|
+
frame.xmlWeight = attrs.get("weight") ?? null;
|
|
1162
1424
|
this.edge = frame;
|
|
1163
1425
|
try {
|
|
1164
1426
|
if (idText !== undefined) {
|
|
@@ -1194,7 +1456,21 @@ class GexfReader {
|
|
|
1194
1456
|
this.report.warnOnce("coercion", WEIGHT_IGNORED_CODE, "the weight attribute is ignored because weightFrom is null; edges are unweighted", where);
|
|
1195
1457
|
return undefined;
|
|
1196
1458
|
}
|
|
1197
|
-
|
|
1459
|
+
// a blank weight="" is an absent weight (common/weights.ts), as a blank weight attvalue is
|
|
1460
|
+
const weight = parseWeightText(text, this.report);
|
|
1461
|
+
this.checkOverflow(text, where);
|
|
1462
|
+
return weight;
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Report (once) a weight text too large for a double, stored as Infinity; `INF` itself is a
|
|
1466
|
+
* legal weight (design section 3.7) and is not reported.
|
|
1467
|
+
* @param text - the weight text
|
|
1468
|
+
* @param where - the location
|
|
1469
|
+
*/
|
|
1470
|
+
checkOverflow(text, where) {
|
|
1471
|
+
if (overflowsToInfinity(text)) {
|
|
1472
|
+
this.report.warnOnce("precision", PRECISION_CODE, `weight ${text.trim()} is beyond the double range and is stored as Infinity`, where, `${PRECISION_CODE}:weight`);
|
|
1473
|
+
}
|
|
1198
1474
|
}
|
|
1199
1475
|
/**
|
|
1200
1476
|
* A child of `<edge>`.
|
|
@@ -1202,8 +1478,9 @@ class GexfReader {
|
|
|
1202
1478
|
* @param local - its local name
|
|
1203
1479
|
* @param attrs - its attributes
|
|
1204
1480
|
* @param line - the line
|
|
1481
|
+
* @param ns - the element's namespace
|
|
1205
1482
|
*/
|
|
1206
|
-
startInEdge(name, local, attrs, line) {
|
|
1483
|
+
startInEdge(name, local, attrs, line, ns) {
|
|
1207
1484
|
const { edge } = this;
|
|
1208
1485
|
if (edge === null) {
|
|
1209
1486
|
this.ctx.push(Ctx.Skip);
|
|
@@ -1211,6 +1488,7 @@ class GexfReader {
|
|
|
1211
1488
|
}
|
|
1212
1489
|
switch (local) {
|
|
1213
1490
|
case "attvalues":
|
|
1491
|
+
this.attvalueIds.clear();
|
|
1214
1492
|
this.ctx.push(Ctx.Attvalues);
|
|
1215
1493
|
return;
|
|
1216
1494
|
case "spells":
|
|
@@ -1222,6 +1500,10 @@ class GexfReader {
|
|
|
1222
1500
|
case "color":
|
|
1223
1501
|
case "thickness":
|
|
1224
1502
|
case "shape":
|
|
1503
|
+
if (!isViz(name, ns)) {
|
|
1504
|
+
this.unknownElement(name, line);
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1225
1507
|
this.writeViz(name, local, attrs, "edge", edge.index, edge.where);
|
|
1226
1508
|
this.ctx.push(Ctx.Skip);
|
|
1227
1509
|
return;
|
|
@@ -1244,6 +1526,7 @@ class GexfReader {
|
|
|
1244
1526
|
return;
|
|
1245
1527
|
}
|
|
1246
1528
|
this.ctx.push(Ctx.Skip);
|
|
1529
|
+
this.checkAttributes("attvalue", attrs, line);
|
|
1247
1530
|
const { report, edge } = this;
|
|
1248
1531
|
const domain = edge === null ? "node" : "edge";
|
|
1249
1532
|
let index;
|
|
@@ -1273,6 +1556,12 @@ class GexfReader {
|
|
|
1273
1556
|
report.warning("missing-value", ATTVALUE_SHAPE_CODE, `<attvalue for="${forId}"> without a value attribute`, valueWhere);
|
|
1274
1557
|
return;
|
|
1275
1558
|
}
|
|
1559
|
+
if (!isTimed(attrs)) {
|
|
1560
|
+
if (this.attvalueIds.has(forId)) {
|
|
1561
|
+
report.warning("validation-error", DUPLICATE_ATTRIBUTE_CODE, `a second static <attvalue for="${forId}"> on one ${domain}; the later value "${value}" is kept`, { ...valueWhere, element: forId });
|
|
1562
|
+
}
|
|
1563
|
+
this.attvalueIds.add(forId);
|
|
1564
|
+
}
|
|
1276
1565
|
try {
|
|
1277
1566
|
if (domain === "edge" && this.weightAttr !== null && this.weightAttr.id === forId) {
|
|
1278
1567
|
if (isTimed(attrs)) {
|
|
@@ -1315,12 +1604,14 @@ class GexfReader {
|
|
|
1315
1604
|
if (edge === null) {
|
|
1316
1605
|
return;
|
|
1317
1606
|
}
|
|
1318
|
-
const weight = parseWeightText(text);
|
|
1607
|
+
const weight = parseWeightText(text, this.report);
|
|
1319
1608
|
if (weight === undefined) {
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1609
|
+
// blank is absent (common/weights.ts), as a blank weight="" XML attribute is
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
this.checkOverflow(text, where);
|
|
1613
|
+
if (edge.xmlWeight !== null) {
|
|
1614
|
+
this.report.warning("validation-error", DUPLICATE_ATTRIBUTE_CODE, `the edge has both weight="${edge.xmlWeight}" and a weight attvalue "${text}"; the attvalue is kept`, where);
|
|
1324
1615
|
}
|
|
1325
1616
|
sink.setEdgeWeight(edge.index, weight);
|
|
1326
1617
|
if (edge.mirror !== INVALID_INDEX) {
|
|
@@ -1353,8 +1644,30 @@ class GexfReader {
|
|
|
1353
1644
|
if (losesPrecision(spec, text)) {
|
|
1354
1645
|
this.report.warning("precision", PRECISION_CODE, `long value ${text.trim()} of "${attribute.name}" exceeds 2^53 and was rounded`, where);
|
|
1355
1646
|
}
|
|
1647
|
+
else if (typeof value === "number" && overflowsToInfinity(text)) {
|
|
1648
|
+
this.report.warnOnce("precision", PRECISION_CODE, `values of "${attribute.name}" beyond the double range (${text.trim()}) are stored as Infinity`, where, `${PRECISION_CODE}:overflow:${attribute.name}`);
|
|
1649
|
+
}
|
|
1650
|
+
this.checkOptions(attribute, value, where);
|
|
1356
1651
|
this.setCell(domain, attribute.handle, index, value);
|
|
1357
1652
|
}
|
|
1653
|
+
/**
|
|
1654
|
+
* Report (once per attribute) a value outside the `<options>` the attribute declares; the
|
|
1655
|
+
* value is kept.
|
|
1656
|
+
* @param attribute - the declaration
|
|
1657
|
+
* @param value - the parsed value (a list checks each item)
|
|
1658
|
+
* @param where - the value's location
|
|
1659
|
+
*/
|
|
1660
|
+
checkOptions(attribute, value, where) {
|
|
1661
|
+
const { options } = attribute.valueDecl;
|
|
1662
|
+
if (options === undefined || options.length === 0) {
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
const items = Array.isArray(value) ? value : [value];
|
|
1666
|
+
const outside = items.find((item) => !options.includes(item));
|
|
1667
|
+
if (typeof outside === "string" || typeof outside === "number" || typeof outside === "boolean") {
|
|
1668
|
+
this.report.warnOnce("validation-error", VALUE_OUTSIDE_OPTIONS_CODE, `"${outside}" is not among the options of attribute "${attribute.name}"; the value is kept`, where, `${VALUE_OUTSIDE_OPTIONS_CODE}:${attribute.name}`);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1358
1671
|
/**
|
|
1359
1672
|
* Append a timed value to the attribute's temporal table (design section 5.10), creating the
|
|
1360
1673
|
* table on first use.
|
|
@@ -1389,6 +1702,9 @@ class GexfReader {
|
|
|
1389
1702
|
}
|
|
1390
1703
|
}
|
|
1391
1704
|
const row = this.temporalRow(attrs, index, value, where);
|
|
1705
|
+
if (row === null) {
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1392
1708
|
if (temporalValue) {
|
|
1393
1709
|
row.push(valueText ?? undefined);
|
|
1394
1710
|
}
|
|
@@ -1413,14 +1729,15 @@ class GexfReader {
|
|
|
1413
1729
|
origin: { format: GEXF_FORMAT, id: weightAttr.id, type: weightAttr.spec.declared },
|
|
1414
1730
|
}, false));
|
|
1415
1731
|
}
|
|
1416
|
-
const value = parseWeightText(text);
|
|
1732
|
+
const value = parseWeightText(text, this.report);
|
|
1417
1733
|
if (value === undefined) {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1734
|
+
// blank is absent (common/weights.ts): no timed value
|
|
1735
|
+
return;
|
|
1736
|
+
}
|
|
1737
|
+
const row = this.temporalRow(attrs, index, value, where);
|
|
1738
|
+
if (row !== null) {
|
|
1739
|
+
sink.addExtensionRow(weightAttr.table, row);
|
|
1422
1740
|
}
|
|
1423
|
-
sink.addExtensionRow(weightAttr.table, this.temporalRow(attrs, index, value, where));
|
|
1424
1741
|
}
|
|
1425
1742
|
/**
|
|
1426
1743
|
* The row of a temporal table for one timed value: element, bounds, value, bound texts, open bits.
|
|
@@ -1428,10 +1745,13 @@ class GexfReader {
|
|
|
1428
1745
|
* @param index - the element's index
|
|
1429
1746
|
* @param value - the parsed value
|
|
1430
1747
|
* @param where - the value's location
|
|
1431
|
-
* @returns the row, without the optional value text
|
|
1748
|
+
* @returns the row, without the optional value text; null when the interval is inverted (recorded)
|
|
1432
1749
|
*/
|
|
1433
1750
|
temporalRow(attrs, index, value, where) {
|
|
1434
1751
|
const bounds = this.readBounds(attrs, where);
|
|
1752
|
+
if (bounds === null) {
|
|
1753
|
+
return null;
|
|
1754
|
+
}
|
|
1435
1755
|
return [
|
|
1436
1756
|
index,
|
|
1437
1757
|
bounds.start.value,
|
|
@@ -1448,7 +1768,7 @@ class GexfReader {
|
|
|
1448
1768
|
* section 5.1); missing bounds are unbounded.
|
|
1449
1769
|
* @param attrs - the attributes
|
|
1450
1770
|
* @param where - the location, for the conflict warning
|
|
1451
|
-
* @returns the bounds and the open bits
|
|
1771
|
+
* @returns the bounds and the open bits; null (recorded as E_BAD_VALUE) when the interval is inverted
|
|
1452
1772
|
*/
|
|
1453
1773
|
readBounds(attrs, where) {
|
|
1454
1774
|
let start = { value: -Infinity, text: null };
|
|
@@ -1458,6 +1778,9 @@ class GexfReader {
|
|
|
1458
1778
|
if (timestamp !== undefined) {
|
|
1459
1779
|
start = parseTimeText(timestamp, this.timeFormat);
|
|
1460
1780
|
end = start;
|
|
1781
|
+
if (attrs.has("start") || attrs.has("end") || attrs.has("startopen") || attrs.has("endopen")) {
|
|
1782
|
+
this.report.warnOnce("validation-error", TIMESTAMP_CONFLICT_CODE, "both timestamp and start / end are given; start / end set the interval and the timestamp is kept as written", where);
|
|
1783
|
+
}
|
|
1461
1784
|
}
|
|
1462
1785
|
const startText = attrs.get("start");
|
|
1463
1786
|
const startOpen = attrs.get("startopen");
|
|
@@ -1483,6 +1806,10 @@ class GexfReader {
|
|
|
1483
1806
|
end = parseTimeText(endOpen, this.timeFormat);
|
|
1484
1807
|
open |= OPEN_END;
|
|
1485
1808
|
}
|
|
1809
|
+
if (start.value > end.value) {
|
|
1810
|
+
this.report.error("validation-error", BAD_VALUE_CODE, `the interval starts after it ends (${start.text ?? String(start.value)} > ${end.text ?? String(end.value)}); it is not kept`, where);
|
|
1811
|
+
return null;
|
|
1812
|
+
}
|
|
1486
1813
|
return { start, end, open };
|
|
1487
1814
|
}
|
|
1488
1815
|
/**
|
|
@@ -1510,9 +1837,13 @@ class GexfReader {
|
|
|
1510
1837
|
attrs.has("startopen") ||
|
|
1511
1838
|
attrs.has("endopen")) {
|
|
1512
1839
|
const bounds = this.readBounds(attrs, where);
|
|
1840
|
+
if (bounds === null) {
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1513
1843
|
const timestamp = attrs.get("timestamp");
|
|
1514
1844
|
if (timestamp !== undefined) {
|
|
1515
|
-
|
|
1845
|
+
// parsed again: beside start / startopen, bounds.start is the start, not the timestamp
|
|
1846
|
+
columns.timestamp.set(sink, report, index, parseTimeText(timestamp, this.timeFormat), where);
|
|
1516
1847
|
}
|
|
1517
1848
|
if (attrs.has("start") || attrs.has("startopen")) {
|
|
1518
1849
|
columns.start.set(sink, report, index, bounds.start, where);
|
|
@@ -1553,10 +1884,15 @@ class GexfReader {
|
|
|
1553
1884
|
line: where.line ?? null,
|
|
1554
1885
|
});
|
|
1555
1886
|
}
|
|
1556
|
-
|
|
1887
|
+
const pair = [
|
|
1557
1888
|
parseTimeText(items[0], this.timeFormat).value,
|
|
1558
1889
|
parseTimeText(items[1], this.timeFormat).value,
|
|
1559
|
-
]
|
|
1890
|
+
];
|
|
1891
|
+
if (pair[0] > pair[1]) {
|
|
1892
|
+
this.report.error("validation-error", BAD_VALUE_CODE, `interval "${part.trim()}" starts after it ends; it is not kept`, where);
|
|
1893
|
+
continue;
|
|
1894
|
+
}
|
|
1895
|
+
pairs.push(pair);
|
|
1560
1896
|
}
|
|
1561
1897
|
return pairs;
|
|
1562
1898
|
}
|
|
@@ -1573,9 +1909,13 @@ class GexfReader {
|
|
|
1573
1909
|
return;
|
|
1574
1910
|
}
|
|
1575
1911
|
this.ctx.push(Ctx.Skip);
|
|
1912
|
+
this.checkAttributes("spell", attrs, line);
|
|
1576
1913
|
const where = this.spellsWhere ?? { line };
|
|
1577
1914
|
try {
|
|
1578
1915
|
const bounds = this.readBounds(attrs, where);
|
|
1916
|
+
if (bounds === null) {
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1579
1919
|
this.spellPairs?.push([bounds.start.value, bounds.end.value]);
|
|
1580
1920
|
this.spellOpen.push(bounds.open);
|
|
1581
1921
|
}
|
|
@@ -1691,23 +2031,47 @@ class GexfReader {
|
|
|
1691
2031
|
this.ctx.push(Ctx.Skip);
|
|
1692
2032
|
}
|
|
1693
2033
|
/**
|
|
1694
|
-
* Report XML attributes the importer does not read (once per attribute name and
|
|
2034
|
+
* Report XML attributes the importer does not read (once per attribute name and element kind;
|
|
2035
|
+
* beyond MAX_UNKNOWN_ATTRIBUTE_NAMES distinct names per kind the rest are counted and reported
|
|
2036
|
+
* once at the end). Namespace declarations and `xml:` / `xsi:` attributes are not data.
|
|
1695
2037
|
* @param attrs - the element's attributes
|
|
1696
2038
|
* @param known - the attribute names read
|
|
1697
|
-
* @param
|
|
2039
|
+
* @param kind - the element name
|
|
1698
2040
|
* @param where - the element's location
|
|
1699
2041
|
*/
|
|
1700
|
-
checkAttributeNames(attrs, known,
|
|
2042
|
+
checkAttributeNames(attrs, known, kind, where) {
|
|
2043
|
+
if (attrs.size === 0) {
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
1701
2046
|
for (const key of attrs.keys()) {
|
|
1702
|
-
if (
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
2047
|
+
if (known.has(key) || key === "xmlns" || /^(xmlns|xml|xsi):/.test(key)) {
|
|
2048
|
+
continue;
|
|
2049
|
+
}
|
|
2050
|
+
let names = this.unknownNames.get(kind);
|
|
2051
|
+
if (names === undefined) {
|
|
2052
|
+
names = new Set();
|
|
2053
|
+
this.unknownNames.set(kind, names);
|
|
1708
2054
|
}
|
|
2055
|
+
if (names.has(key)) {
|
|
2056
|
+
continue;
|
|
2057
|
+
}
|
|
2058
|
+
names.add(key);
|
|
2059
|
+
if (names.size > MAX_UNKNOWN_ATTRIBUTE_NAMES) {
|
|
2060
|
+
this.unknownOverflow.set(kind, (this.unknownOverflow.get(kind) ?? 0) + 1);
|
|
2061
|
+
continue;
|
|
2062
|
+
}
|
|
2063
|
+
this.report.warning("unsupported", UNKNOWN_XML_ATTRIBUTE_CODE, `attribute ${key} of <${kind}> is not GEXF and was ignored`, { ...where, element: key });
|
|
1709
2064
|
}
|
|
1710
2065
|
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Report the XML attributes the importer does not read on an element other than node / edge.
|
|
2068
|
+
* @param kind - the element's local name (a key of ELEMENT_ATTRIBUTES)
|
|
2069
|
+
* @param attrs - its attributes
|
|
2070
|
+
* @param line - the line
|
|
2071
|
+
*/
|
|
2072
|
+
checkAttributes(kind, attrs, line) {
|
|
2073
|
+
this.checkAttributeNames(attrs, ELEMENT_ATTRIBUTES[kind] ?? new Set(), kind, { line });
|
|
2074
|
+
}
|
|
1711
2075
|
/**
|
|
1712
2076
|
* Write one cell of a domain.
|
|
1713
2077
|
* @param domain - node or edge
|
|
@@ -1745,6 +2109,16 @@ class GexfReader {
|
|
|
1745
2109
|
}
|
|
1746
2110
|
}
|
|
1747
2111
|
}
|
|
2112
|
+
/**
|
|
2113
|
+
* Whether a node / edge child named like a viz element is one: in a viz namespace, or written
|
|
2114
|
+
* with the `viz:` prefix the document never declares (hand-written files often omit it).
|
|
2115
|
+
* @param name - the element name as written
|
|
2116
|
+
* @param ns - its namespace
|
|
2117
|
+
* @returns true for a viz element
|
|
2118
|
+
*/
|
|
2119
|
+
function isViz(name, ns) {
|
|
2120
|
+
return isVizNamespace(ns) || (ns === null && name.startsWith("viz:"));
|
|
2121
|
+
}
|
|
1748
2122
|
/**
|
|
1749
2123
|
* The element text of an edge without an id, for issues.
|
|
1750
2124
|
* @param source - the source text, or undefined
|
|
@@ -1811,7 +2185,7 @@ function parseColor(attrs) {
|
|
|
1811
2185
|
if (hex !== undefined && !attrs.has("r")) {
|
|
1812
2186
|
const digits = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
1813
2187
|
if (!/^[0-9a-fA-F]{6}$/.test(digits)) {
|
|
1814
|
-
throw new GraphFormatError("E_COLUMN_TYPE", `viz hex="${hex}" is not a #RRGGBB
|
|
2188
|
+
throw new GraphFormatError("E_COLUMN_TYPE", `viz hex="${hex}" is not a #RRGGBB color`, {
|
|
1815
2189
|
attribute: "hex",
|
|
1816
2190
|
});
|
|
1817
2191
|
}
|
|
@@ -1836,7 +2210,7 @@ const XML_START = /^\uFEFF?\s*</;
|
|
|
1836
2210
|
* not start with markup
|
|
1837
2211
|
*/
|
|
1838
2212
|
function sniffGexf(head) {
|
|
1839
|
-
const text =
|
|
2213
|
+
const text = sniffXmlText(head);
|
|
1840
2214
|
// Only a document that starts as markup: a JSON or CSV value may mention a `<gexf>` tag.
|
|
1841
2215
|
if (!XML_START.test(text)) {
|
|
1842
2216
|
return 0;
|
|
@@ -1864,9 +2238,13 @@ function resolveGexfOptions(options) {
|
|
|
1864
2238
|
}
|
|
1865
2239
|
return viz;
|
|
1866
2240
|
}
|
|
1867
|
-
/**
|
|
2241
|
+
/**
|
|
2242
|
+
* The GEXF importer.
|
|
2243
|
+
* @category Built-in formats
|
|
2244
|
+
*/
|
|
1868
2245
|
export const gexfImporter = Object.freeze({
|
|
1869
2246
|
format: GEXF_FORMAT,
|
|
2247
|
+
options: Object.freeze(["viz"]),
|
|
1870
2248
|
extensions: Object.freeze([".gexf"]),
|
|
1871
2249
|
mimeTypes: Object.freeze(["application/gexf+xml", "application/xml", "text/xml"]),
|
|
1872
2250
|
sniff: sniffGexf,
|
|
@@ -1891,9 +2269,12 @@ export const gexfImporter = Object.freeze({
|
|
|
1891
2269
|
reportUnusedOptions(options, report, USED_OPTIONS);
|
|
1892
2270
|
const reader = new GexfReader(sink, report, resolved, viz);
|
|
1893
2271
|
try {
|
|
1894
|
-
await tokenizeXml(textChunks(input, report, { ...resolved, declaredEncoding: xmlDeclaredEncoding }), reader);
|
|
2272
|
+
await tokenizeXml(textChunks(input, report, { ...resolved, declaredEncoding: xmlDeclaredEncoding, xml: true }), reader);
|
|
1895
2273
|
}
|
|
1896
2274
|
catch (err) {
|
|
2275
|
+
if (err instanceof XmlEmptyInputError) {
|
|
2276
|
+
report.fail(EMPTY_INPUT_CODE, "the input is empty (no markup at all)", { line: err.line });
|
|
2277
|
+
}
|
|
1897
2278
|
if (err instanceof XmlSyntaxError) {
|
|
1898
2279
|
report.fail(XML_SYNTAX_CODE, err.message, { line: err.line });
|
|
1899
2280
|
}
|