xml-toolkit 1.0.61 → 1.1.0

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 CHANGED
@@ -1,5 +1,6 @@
1
1
  ![workflow](https://github.com/do-/node-xml-toolkit/actions/workflows/main.yml/badge.svg)
2
2
  ![Jest coverage](./badges/coverage-jest%20coverage.svg)
3
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/do-/node-xml-toolkit)
3
4
 
4
5
  `node-xml-toolkit` is a pure node.js library for solving diverse XML related application tasks, e. g.:
5
6
  * scanning through multi gigabyte long XML files with a limited memory buffer;
@@ -13,16 +14,19 @@ It features both (fast and simple, but greedy) [synchronous](https://github.com/
13
14
  npm install xml-toolkit
14
15
  ```
15
16
 
16
- # Using
17
-
18
- * [Parsing a small file completely](XMLParser)
17
+ # Usage
18
+ * [Parsing a small file completely](XMLParser) (optionally validating)
19
19
 
20
20
  ```js
21
21
  const fs = require ('fs')
22
- const {XMLParser} = require ('xml-toolkit')
22
+ const {XMLParser, XMLSchemata} = require ('xml-toolkit')
23
23
 
24
24
  const xml = fs.readFileSync ('doc.xml')
25
- const parser = new XMLParser ({...options})
25
+ // const xs = new XMLSchemata ('schema.xsd')
26
+
27
+ const parser = new XMLParser ({
28
+ // xs,
29
+ })
26
30
 
27
31
  const document = parser.process (xml)
28
32
 
@@ -31,12 +35,15 @@ for (const element of document.detach ().children) {
31
35
  }
32
36
  ```
33
37
 
34
- * [Reading a Record List](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Reading-a-Record-List), streaming mode
38
+ * [Reading a Record List](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Reading-a-Record-List) (optionally validating)
35
39
 
36
40
  ```js
37
- const {XMLReader, XMLNode} = require ('xml-toolkit')
41
+ const {XMLReader, XMLNode, XMLSchemata} = require ('xml-toolkit')
42
+
43
+ // const xs = new XMLSchemata ('schema.xsd')
38
44
 
39
45
  const records = new XMLReader ({
46
+ //xs,
40
47
  filterElements : 'Record',
41
48
  map : XMLNode.toObject ({})
42
49
  }).process (xmlSource)
@@ -56,7 +63,7 @@ const records = new XMLReader ({
56
63
  // records.on ('data', record => doSomethingWith (record))
57
64
  ```
58
65
 
59
- * [Getting a Single Element](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Getting-a-Single-Element), streaming mode
66
+ * [Getting a Single Element](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Getting-a-Single-Element)
60
67
 
61
68
  ```js
62
69
  const {XMLReader, XMLNode} = require ('xml-toolkit')
@@ -67,6 +74,21 @@ const data = await new XMLReader ({
67
74
  }).process (xmlSource).findFirst ()
68
75
  ```
69
76
 
77
+ * [Formatting XML](https://github.com/do-/node-xml-toolkit/wiki/XMLPrinter)
78
+ ```js
79
+ const {XMLParser} = require ('xml-toolkit')
80
+ xml = new XMLParser ()
81
+ .process (fs.readFileSync ('doc.xml'))
82
+ .toString ({
83
+ // decl: {encoding: 'UTF-8', standalone: 1},
84
+ space: '\t',
85
+ // attrSpace: 2,
86
+ // EOL: '\n',
87
+ // level: 0,
88
+ // encodeLineBreaks: false,
89
+ })
90
+ ```
91
+
70
92
  * [Patching XML](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Patching-XML)
71
93
 
72
94
  ```js
@@ -80,8 +102,6 @@ let xmlResult = ''; for await (const node of new XMLReader ().process (xmlSource
80
102
  * [Serializing an Object According to an XML Schema](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Serializing-an-Object-According-to-an-XML-Schema)
81
103
 
82
104
  ```js
83
- const {XMLSchemata} = require ('xml-toolkit')
84
-
85
105
  const data = {ExportDebtRequestsResponse: {
86
106
  "request-data": {
87
107
  // ...
@@ -98,14 +118,13 @@ const xml = xs.stringify (data)
98
118
  <!-- ... and so on ... -->
99
119
  */
100
120
  ```
101
-
102
- * Invoking a [SOAP 1.1](https://github.com/do-/node-xml-toolkit/wiki/SOAP11) or [SOAP 1.2](https://github.com/do-/node-xml-toolkit/wiki/SOAP12) Web Service
121
+ * Invoking a [SOAP 1.1](SOAP11) Web Service
103
122
 
104
123
  ```js
105
124
  const http = require ('http')
106
- const {SOAP11, SOAP12} = require ('xml-toolkit')
125
+ const {SOAP11} = require ('xml-toolkit')
107
126
 
108
- const soap = new SOAP11 ('their.wsdl') // or SOAP12
127
+ const soap = new SOAP11 ('their.wsdl')
109
128
 
110
129
  const {method, headers, body} = soap.http ({RequestElementNameOfTheirs: {amount: '0.01'}})
111
130
 
@@ -113,32 +132,6 @@ const rq = http.request (endpointURL, {method, headers})
113
132
  rq.write (body)
114
133
  ```
115
134
 
116
- * [Implementing](https://github.com/do-/node-xml-toolkit/wiki/Use-Case:-Implement-a-SOAP-Web-Service) a SOAP service
117
-
118
- ```js
119
- const {XMLSchemata, SOAP11, SOAP12, SOAPFault} = require ('xml-toolkit')
120
-
121
- const SOAP = SOAP11 // or SOAP12
122
-
123
- const xs = new XMLSchemata (`myService.wsdl`)
124
-
125
- let body, statusCode; try {
126
- body = xs.stringify (myMethod (/*...*/))
127
- statusCode = 200
128
- }
129
- catch (x) {
130
- body = new SOAPFault (x)
131
- statusCode = 500
132
- }
133
-
134
- rp.writeHead (statusCode, {
135
- 'Content-Type': SOAP.contentType,
136
- })
137
-
138
- const xml = SOAP.message (body)
139
- rp.end (xml)
140
- ```
141
-
142
135
  # Motivation
143
136
 
144
137
  Unlike Java (with [JAXB](https://www.oracle.com/technical-resources/articles/javase/jaxb.html) and [JAX-WS](https://www.oracle.com/technical-resources/articles/javase/jax-ws-2.html)) and some other software development platforms dating back to late 1990s, the core node.js library doesn't offer any standard tool for dealing with XML.
@@ -154,9 +147,6 @@ Pure js 3rd party modules are abundant, but after some real tasks based research
154
147
 
155
148
  No W3C specification is 100% implemented here. For instance, DTDs are not supported, so, in theory, any rogue XML file using such bizarre deprecated feature as [Entity Declarations](https://www.w3.org/TR/xml/#sec-entity-decl) may crash the local XML parser.
156
149
 
157
- Though `node-xml-toolkit` has some support for [XMLSchema](https://github.com/do-/node-xml-toolkit/wiki/XMLSchema), it cannot be used for validation. Here, XML Schema is used only as a template for outputting valid XML provided a correct set of input data. That means, each `decimal` will be formatted with proper `fractionDigits`, but no CPU cycle will be spent on checking whether the incoming 10 char string fully conforms to the [`date`](https://www.w3.org/TR/2012/REC-xmlschema11-2-20120405/datatypes.html#date) lexical representation or not.
158
-
159
- In short, `node-xml-toolkit` may produce incorrect results for some input data, especially for deliberately broken ones.
150
+ The [XMLSchema](https://github.com/do-/node-xml-toolkit/wiki/XMLSchemata) is fairly usable for most real world cases, still some features (like `xs:unique`) are ignored completely, some others may require more test coverage. In general, XML Schema support in `xml-toolkit` should be considered _forever beta_.
160
151
 
161
- There are perfectly reliable external tools for XML validation: for instance,
162
- [xmllint](https://linux.die.net/man/1/xmllint) (used in the test suite here) do just fine.
152
+ There are perfectly reliable external tools for XML validation: for instance, [xmllint](https://linux.die.net/man/1/xmllint) (used in the test suite here) do just fine.
package/index.js CHANGED
@@ -1,11 +1,16 @@
1
1
  const {
2
2
  XSSimpleType,
3
- XSSimpleTypeFloat,
4
- XSSimpleTypeBoolean,
3
+ } = require ('./lib/simple/XSSimpleType')
4
+
5
+ const {
5
6
  XSSimpleTypeDate,
6
7
  XSSimpleTypeDateTime,
7
- XSSimpleTypeQName,
8
- } = require ('./lib/XSSimpleType')
8
+ } = require ('./lib/simple/XSSimpleTypeDT')
9
+
10
+ const {
11
+ XSSimpleTypeFloat,
12
+ XSSimpleTypeDouble,
13
+ } = require ('./lib/simple/XSSimpleTypeFloatingPoint')
9
14
 
10
15
  module.exports = {
11
16
  AttributesMap: require ('./lib/AttributesMap'),
@@ -28,10 +33,11 @@ module.exports = {
28
33
  XMLSchemata: require ('./lib/XMLSchemata'),
29
34
  XSSimpleType,
30
35
  XSSimpleTypeFloat,
31
- XSSimpleTypeBoolean,
36
+ XSSimpleTypeDouble,
37
+ XSSimpleTypeBoolean: require ('./lib/simple/XSSimpleTypeBoolean'),
32
38
  XSSimpleTypeDate,
33
39
  XSSimpleTypeDateTime,
34
- XSSimpleTypeQName,
40
+ XSSimpleTypeQName: require ('./lib/simple/XSSimpleTypeQName'),
35
41
  }
36
42
 
37
43
  module.exports.SOAP = v => {switch (String (v)) {
@@ -60,7 +60,7 @@ const XMLMarshaller = class {
60
60
 
61
61
  const {targetNamespace, attributes: {name}} = schemaElement
62
62
 
63
- throw Error (`Cannot stringify ${JSON.stringify (data)} as ${name}#{${targetNamespace}}`, {cause})
63
+ throw Error (`Cannot stringify ${JSON.stringify (data)} as ${name}#{${targetNamespace}}: ${cause.message}`, {cause})
64
64
 
65
65
  }
66
66
 
package/lib/XMLParser.js CHANGED
@@ -1,21 +1,40 @@
1
- const assert = require ('assert')
2
- const SAXEvent = require ('./SAXEvent.js')
3
- const XMLNode = require ('./XMLNode.js')
4
- const XMLIterator = require ('./XMLIterator.js')
1
+ const SAXEvent = require ('./SAXEvent.js')
2
+ const XMLNode = require ('./XMLNode.js')
3
+ const XMLIterator = require ('./XMLIterator.js')
4
+ const XMLValidatior = require ('./validation/XMLValidatior.js')
5
5
 
6
6
  const XMLParser = class {
7
7
 
8
8
  constructor (options = {}) {
9
-
10
- if (!('stripSpace' in options)) options.stripSpace = true
11
- assert (options.stripSpace === true || options.stripSpace === false, 'options.stripSpace must be boolean, not ' + typeof options.stripSpace)
12
9
 
13
- if (!('useEntities' in options)) options.useEntities = true
14
- assert (options.useEntities === true || options.useEntities === false, 'options.useEntities must be boolean, not ' + typeof options.useEntities)
10
+ if ('xs' in options) {
11
+
12
+ const {xs} = options; if (!(xs instanceof Map && xs.constructor.name === 'XMLSchemata')) throw Error (`options.xs must be an XMLSchemata instance, found ${typeof xs} '${xs}'`)
13
+
14
+ this.xs = xs
15
+
16
+ }
17
+ else {
18
+
19
+ this.xs = null
20
+
21
+ }
22
+
23
+ for (const k of ['stripSpace', 'useEntities', 'useNamespaces']) {
24
+
25
+ if (!(k in options)) options [k] = true
15
26
 
16
- if (!('useNamespaces' in options)) options.useNamespaces = true
17
- assert (options.useNamespaces === true || options.useNamespaces === false, 'options.useNamespaces must be boolean, not ' + typeof options.useNamespaces)
27
+ switch (options [k]) {
28
+ case false:
29
+ if (this.xs !== null && k === 'useNamespaces') throw Error (`With an XMLSchema provided, options.${k} cannot be false`)
30
+ case true:
31
+ break
32
+ default:
33
+ throw Error (`options.${k} must be boolean, found ${typeof options [k]} '${options [k]}'`)
34
+ }
18
35
 
36
+ }
37
+
19
38
  this.stripSpace = options.stripSpace
20
39
  this.useEntities = options.useEntities
21
40
  this.useNamespaces = options.useNamespaces
@@ -46,9 +65,11 @@ const XMLParser = class {
46
65
 
47
66
  default:
48
67
 
49
- if (this.text.length === 0) break
50
68
  if (this.stripSpace) this.text = this.text.trim ()
51
69
  if (this.text.length === 0) break
70
+ if (this.validator) {
71
+ this.validator.characters (this.text)
72
+ }
52
73
  (new XMLNode (this.text, null, SAXEvent.TYPES.CHARACTERS)).parent = this.element
53
74
  this.text = ''
54
75
 
@@ -61,14 +82,22 @@ const XMLParser = class {
61
82
  node.parent = this.element
62
83
  if (this.useNamespaces) node.readNamespaces ()
63
84
  this.element = node
64
- if (this.document === null) this.document = node
85
+ if (this.document === null) {
86
+ this.document = node
87
+ if (this.xs !== null) this.validator = new XMLValidatior (this.xs, node, () => nodes.pos)
88
+ }
89
+ else {
90
+ if (this.xs !== null) this.validator.startElement (node)
91
+ }
65
92
  break
66
93
 
67
94
  case SAXEvent.TYPES.END_ELEMENT:
68
95
 
69
96
  if (this.element === null) throw new Error (`Unbalanced end element tag "${node.text}" occured at position ${nodes.pos}`)
97
+ node ['_ns_map'] = this.element ['_ns_map']
70
98
  this.element.type = type
71
99
  this.element = this.element.parent
100
+ if (this.xs !== null) this.validator.endElement (node)
72
101
  break
73
102
 
74
103
  }
package/lib/XMLReader.js CHANGED
@@ -3,6 +3,7 @@ const {Transform} = require ('stream')
3
3
  const SAXEvent = require ('./SAXEvent.js')
4
4
  const XMLNode = require ('./XMLNode.js')
5
5
  const XMLLexer = require ('./XMLLexer.js')
6
+ const XMLValidatior = require ('./validation/XMLValidatior.js')
6
7
 
7
8
  const OPT_SRC = Symbol ('_src')
8
9
  const OPT_SAX = Symbol ('_sax')
@@ -54,6 +55,14 @@ const XMLReader = class extends Transform {
54
55
 
55
56
  super (options)
56
57
 
58
+ if ('xs' in options) {
59
+ this.xs = options.xs
60
+ this.validator = null
61
+ }
62
+ else {
63
+ this.xs = null
64
+ }
65
+
57
66
  this.stripSpace = options.stripSpace
58
67
  this.useEntities = options.useEntities
59
68
  this.useNamespaces = options.useNamespaces
@@ -133,11 +142,46 @@ const XMLReader = class extends Transform {
133
142
  return this [OPT_SAX] = false
134
143
 
135
144
  }
145
+
146
+ validate (xmlNode) {
147
+
148
+ if (!this.validator) switch (xmlNode.type) {
149
+
150
+ case SAXEvent.TYPES.START_ELEMENT:
151
+ this.validator = new XMLValidatior (this.xs, xmlNode, () => this.position)
152
+
153
+ default:
154
+ return
155
+
156
+ }
157
+
158
+ switch (xmlNode.type) {
159
+
160
+ case SAXEvent.TYPES.START_ELEMENT : return this.validator.startElement (xmlNode)
161
+
162
+ case SAXEvent.TYPES.CHARACTERS : return this.validator.characters (xmlNode.src)
163
+
164
+ case SAXEvent.TYPES.END_ELEMENT : return this.validator.endElement (xmlNode)
165
+
166
+ }
167
+
168
+ }
136
169
 
137
170
  publish (xmlNode, type = null) {
138
171
 
139
172
  if (type !== null) xmlNode.type = type
140
173
 
174
+ if (this.xs) try {
175
+
176
+ this.validate (xmlNode)
177
+
178
+ }
179
+ catch (error) {
180
+
181
+ this.destroy (error)
182
+
183
+ }
184
+
141
185
  const {filter} = this; if (filter !== null) {
142
186
 
143
187
  if (!filter (xmlNode)) return
package/lib/XMLSchema.js CHANGED
@@ -70,6 +70,16 @@ const XMLSchema = class extends Map {
70
70
 
71
71
  }
72
72
 
73
+ getElement (localName) {
74
+
75
+ const el = this.get (localName)
76
+
77
+ if (!el || el.localName !== 'element') throw Error (`The element ${localName} is not found in ${this.targetNamespace}`)
78
+
79
+ return el
80
+
81
+ }
82
+
73
83
  setSource (node) {
74
84
 
75
85
  this.defaultNamespace = node.namespacesMap.default
@@ -94,6 +104,7 @@ const XMLSchema = class extends Map {
94
104
  this [TYPES].set (name, e)
95
105
  break
96
106
  default:
107
+ // console.log ([name, localName, this.targetNamespace])
97
108
  this.set (name, e)
98
109
  }
99
110
 
@@ -2,7 +2,13 @@ const fs = require ('fs')
2
2
  const path = require ('path')
3
3
 
4
4
  const XMLSchema = require ('./XMLSchema.js')
5
- const {XSSimpleType} = require ('./XSSimpleType.js')
5
+ const {XSSimpleType} = require ('./simple/XSSimpleType.js')
6
+ const XSSimpleTypeDT = require ('./simple/XSSimpleTypeDT.js')
7
+ const XSSimpleTypeDecimal = require ('./simple/XSSimpleTypeDecimal.js')
8
+ const XSSimpleTypeInteger = require ('./simple/XSSimpleTypeInteger.js')
9
+ const XSSimpleTypeBoolean = require ('./simple/XSSimpleTypeBoolean.js')
10
+ const XSSimpleTypeFloatingPoint = require ('./simple/XSSimpleTypeFloatingPoint.js')
11
+ const XSSimpleTypeQName = require ('./simple/XSSimpleTypeQName.js')
6
12
 
7
13
  class XMLSchemaBuiltIn extends XMLSchema {
8
14
 
@@ -14,15 +20,24 @@ class XMLSchemaBuiltIn extends XMLSchema {
14
20
 
15
21
  const doc = parent.parser.process (xml)
16
22
 
17
- super (parent, XMLSchema.namespaceURI, doc)
23
+ super (parent, XMLSchema.namespaceURI, doc)
24
+
25
+ this.byName = {
26
+ ...XSSimpleTypeInteger.byName,
27
+ ...XSSimpleTypeFloatingPoint.byName,
28
+ ...XSSimpleTypeDT.byName,
29
+ QName : XSSimpleTypeQName,
30
+ boolean : XSSimpleTypeBoolean,
31
+ decimal : XSSimpleTypeDecimal,
32
+ }
18
33
 
19
34
  }
20
35
 
21
- getSimpleTypeClass (node) {
36
+ getSimpleTypeClass ({attributes: {name}}) {
22
37
 
23
- return XSSimpleType.forName (node.attributes.name)
38
+ return this.byName [name] ?? XSSimpleType
24
39
 
25
- }
40
+ }
26
41
 
27
42
  }
28
43
 
@@ -3,7 +3,6 @@ const path = require ('path')
3
3
 
4
4
  const XMLSchema = require ('./XMLSchema.js')
5
5
  const NamespacesMap = require ('./NamespacesMap.js')
6
- const {XSSimpleType} = require ('./XSSimpleType.js')
7
6
 
8
7
  class XMLSchemaXml extends XMLSchema {
9
8
 
@@ -8,7 +8,8 @@ const XMLSchemaXml = require ('./XMLSchemaXml.js')
8
8
  const XMLSchemaBuiltIn = require ('./XMLSchemaBuiltIn.js')
9
9
  const XMLMarshaller = require ('./XMLMarshaller.js')
10
10
 
11
- const {XSSimpleType} = require ('./XSSimpleType.js')
11
+ const XSAnyType = require ('./XSAnyType.js')
12
+ const {XSSimpleType} = require ('./simple/XSSimpleType.js')
12
13
 
13
14
  const IDX = Symbol ('_index')
14
15
 
@@ -32,6 +33,12 @@ const XMLSchemata = class extends Map {
32
33
  this.addFile (fn)
33
34
 
34
35
  }
36
+
37
+ getDebugQName (localName, namespaceURI) {
38
+
39
+ return namespaceURI ? `{${namespaceURI}}${localName}` : localName
40
+
41
+ }
35
42
 
36
43
  register (name, targetNamespace) {
37
44
 
@@ -51,10 +58,20 @@ const XMLSchemata = class extends Map {
51
58
 
52
59
  if (node.localName === 'simpleType' && !node._xsSimpleType) node._xsSimpleType = new (schema.getSimpleTypeClass (node)) (this)
53
60
 
61
+ if (!('targetNamespace' in node)) node.targetNamespace = namespaceURI
62
+
54
63
  return node
55
64
 
56
65
  }
57
66
 
67
+ getAnyType (node) {
68
+
69
+ if ('_xsAnyType' in node) return node._xsAnyType
70
+
71
+ return node._xsAnyType = new XSAnyType (this, node)
72
+
73
+ }
74
+
58
75
  getSimpleType (node) {
59
76
 
60
77
  if ('_xsSimpleType' in node) return node._xsSimpleType
@@ -65,15 +82,65 @@ const XMLSchemata = class extends Map {
65
82
 
66
83
  getSimpleTypeClass (node) {
67
84
 
68
- if (node) for (const {localName, namespaceURI, attributes: {base}, children} of node.children)
69
-
70
- if (localName === 'restriction' && namespaceURI === XMLSchema.namespaceURI)
85
+ if (node) for (const {localName, attributes: {base}, children} of node.children) switch (localName) {
86
+
87
+ case 'restriction': return this.getType (base)._xsSimpleType.restrict (children)
88
+
89
+ case 'union': return class {
90
+
91
+ constructor (xs) {
92
+
93
+ this.xs = xs
94
+
95
+ this.nodes = children
96
+
97
+ }
98
+
99
+ stringify (v) {
100
+
101
+ const messages = []; for (const node of this.nodes) {
102
+
103
+ try {
104
+
105
+ return this.xs.getSimpleType (node).stringify (v)
106
+
107
+ }
108
+ catch (err) {
109
+
110
+ messages.push (err.message)
111
+
112
+ }
71
113
 
72
- return this.getType (base)._xsSimpleType.restrict (children.map (
114
+ }
115
+
116
+ throw Error (messages.join ('; '))
73
117
 
74
- ({localName, attributes: {value}}) => ({name: localName, value})
118
+ }
75
119
 
76
- ))
120
+ validateScalar (v) {
121
+
122
+ const messages = []; for (const node of this.nodes) {
123
+
124
+ try {
125
+
126
+ return this.xs.getSimpleType (node).validateScalar (v)
127
+
128
+ }
129
+ catch (err) {
130
+
131
+ messages.push (err.message)
132
+
133
+ }
134
+
135
+ }
136
+
137
+ throw Error (messages.join ('; '))
138
+
139
+ }
140
+
141
+ }
142
+
143
+ }
77
144
 
78
145
  return XSSimpleType
79
146
 
@@ -98,10 +165,26 @@ const XMLSchemata = class extends Map {
98
165
  getByReference (ref) {
99
166
 
100
167
  const [localName, namespaceURI] = ref
101
-
102
- const s = this.get (namespaceURI); if (s == null) throw new Error ('Unknown namespace: ' + namespaceURI)
103
-
104
- return s.get (localName)
168
+
169
+ return this.getSchema (namespaceURI).get (localName)
170
+
171
+ }
172
+
173
+ getSchema (namespaceURI) {
174
+
175
+ if (!this.has (namespaceURI)) throw new Error ('Unknown namespace: ' + namespaceURI)
176
+
177
+ return this.get (namespaceURI)
178
+
179
+ }
180
+
181
+ getTypeDefinition (node) {
182
+
183
+ // while (node.attributes.ref) node = this.getByReference (node.attributes.ref)
184
+
185
+ if (node.attributes.type) return this.getType (node.attributes.type)
186
+
187
+ return node.children.find (i => i.localName.endsWith ('Type'))
105
188
 
106
189
  }
107
190
 
@@ -0,0 +1,98 @@
1
+ const Occurable = require ('./validation/Occurable')
2
+
3
+ module.exports = class {
4
+
5
+ #isAnyAttributeAllowed = false
6
+ #isExtended = false
7
+ #parent = null
8
+
9
+ get isAnyAttributeAllowed () {
10
+
11
+ return this.#isAnyAttributeAllowed
12
+
13
+ }
14
+
15
+ constructor (xs, node) {
16
+
17
+ this.xs = xs
18
+ this.node = node
19
+
20
+ this.attributes = new Map ()
21
+ this.elements = []
22
+
23
+ this.scan (node)
24
+
25
+ if (this.#parent === null) return
26
+
27
+ if (this.#isExtended) this.extend (); else this.restrict ()
28
+
29
+ }
30
+
31
+ extend () {
32
+
33
+ this.attributes = new Map ([
34
+ ...this.#parent.attributes,
35
+ ...this.attributes
36
+ ])
37
+
38
+ this.elements = this.#parent.elements.concat (this.elements)
39
+
40
+ this.content = new Occurable ([this.#parent.content, this.content])
41
+
42
+ }
43
+
44
+ restrict () {
45
+
46
+ this.attributes = new Map ([
47
+ ...this.#parent.attributes,
48
+ ...this.attributes
49
+ ])
50
+
51
+ }
52
+
53
+ asSimpleType () {
54
+
55
+ return this.xs.getSimpleType (this.node)
56
+
57
+ }
58
+
59
+ get debugQName () {
60
+
61
+ const {xs, node} = this
62
+
63
+ return xs.getDebugQName (node.attributes.name, node.targetNamespace)
64
+
65
+ }
66
+
67
+ scan (node) {
68
+
69
+ while (node.attributes.ref) node = this.xs.getByReference (node.attributes.ref)
70
+
71
+ switch (node.localName) {
72
+
73
+ case 'anyAttribute': return this.#isAnyAttributeAllowed = true
74
+
75
+ case 'attribute': return this.attributes.set (node.attributes.name, node)
76
+
77
+ case 'any':
78
+ case 'element':
79
+ return this.elements.push (node)
80
+
81
+ case 'all':
82
+ case 'choice':
83
+ case 'sequence':
84
+ this.content ??= new Occurable (node, this.xs)
85
+ break
86
+
87
+ case 'extension':
88
+ this.#isExtended = true
89
+ case 'restriction':
90
+ this.#parent = this.xs.getAnyType (this.xs.getType (node.attributes.base))
91
+
92
+ }
93
+
94
+ for (const child of node.children) this.scan (child)
95
+
96
+ }
97
+
98
+ }