xml-toolkit 1.1.2 → 1.1.4

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.
@@ -1,18 +1,19 @@
1
1
  const XMLNode = require ('./XMLNode.js')
2
2
  const SAXEvent = require ('./SAXEvent.js')
3
3
  const EntityResolver = require ('./EntityResolver.js')
4
-
5
- const CH_LF = '\n'.charCodeAt (0)
4
+ const XMLPosition = require ('./XMLPosition.js')
6
5
 
7
6
  const XMLIterator = class {
8
7
 
8
+ #position = new XMLPosition ()
9
+
9
10
  constructor (src, options = {}) {
10
11
 
11
- this.src = src
12
- this.options = options
13
- this.pos = 0
14
- this.selfEnclosed = null
15
- this.entityResolver = options.entityResolver ?? new EntityResolver ()
12
+ this.src = src
13
+ this.options = options
14
+ this.absolutePosition = 0
15
+ this.selfEnclosed = null
16
+ this.entityResolver = options.entityResolver ?? new EntityResolver ()
16
17
 
17
18
  }
18
19
 
@@ -34,21 +35,7 @@ const XMLIterator = class {
34
35
 
35
36
  get linePos () {
36
37
 
37
- const {pos} = this
38
-
39
- let l = 1, c = -1
40
-
41
- for (let i = pos; i >= 0; i --) {
42
-
43
- if (this.src.charCodeAt (i) !== CH_LF) continue
44
-
45
- l ++; if (c !== -1) continue
46
-
47
- c = pos - i
48
-
49
- }
50
-
51
- return [l, c]
38
+ return this.#position.toJSON ()
52
39
 
53
40
  }
54
41
 
@@ -56,18 +43,24 @@ const XMLIterator = class {
56
43
 
57
44
  if (this.selfEnclosed !== null) return this.autoClose ()
58
45
 
59
- const {src, pos, entityResolver} = this
46
+ const {src, absolutePosition, entityResolver} = this
60
47
 
61
- if (src.length - pos < 1) return {done: true}
48
+ if (src.length - absolutePosition < 1) return {done: true}
62
49
 
63
- const value = new XMLNode (src.slice (pos), entityResolver)
50
+ const value = new XMLNode (src.slice (absolutePosition), entityResolver)
64
51
 
65
52
  value.trim ()
66
-
67
- const {length} = value.src; if (length === 0) return {done: true}
68
53
 
69
- this.pos += length
54
+ {
55
+
56
+ const {src} = value, {length} = src; if (length === 0) return {done: true}
70
57
 
58
+ this.absolutePosition += length
59
+
60
+ this.#position.scan (src)
61
+
62
+ }
63
+
71
64
  if (value.isStartElement && value.isSelfEnclosed) this.selfEnclosed = value
72
65
 
73
66
  return {value}
package/lib/XMLParser.js CHANGED
@@ -93,7 +93,7 @@ const XMLParser = class {
93
93
 
94
94
  case SAXEvent.TYPES.END_ELEMENT:
95
95
 
96
- if (this.element === null) throw new Error (`Unbalanced end element tag "${node.src}" occured at position ${nodes.pos}`)
96
+ if (this.element === null) throw new Error (`Unbalanced end element tag "${node.src}" occured at position ${nodes.absolutePosition}`)
97
97
  node ['_ns_map'] = this.element ['_ns_map']
98
98
  this.element.type = type
99
99
  this.element = this.element.parent
@@ -0,0 +1,37 @@
1
+ const CH_LF = '\n'.charCodeAt (0)
2
+
3
+ class XMLPosition {
4
+
5
+ #line = 0
6
+ #localPosition = 0
7
+ #lastLine = 0
8
+ #lastLocalPosition = 0
9
+
10
+ scan (chunk) {
11
+
12
+ this.#lastLine = this.#line
13
+ this.#lastLocalPosition = this.#localPosition
14
+
15
+ for (let i = 0; i < chunk.length; i ++) switch (chunk.charCodeAt (i)) {
16
+
17
+ case CH_LF:
18
+ this.#line ++
19
+ this.#localPosition = 0
20
+ break
21
+
22
+ default:
23
+ this.#localPosition ++
24
+
25
+ }
26
+
27
+ }
28
+
29
+ toJSON () {
30
+
31
+ return [this.#lastLine + 1, this.#lastLocalPosition + 1]
32
+
33
+ }
34
+
35
+ }
36
+
37
+ module.exports = XMLPosition
package/lib/XMLReader.js CHANGED
@@ -1,8 +1,9 @@
1
- const assert = require ('assert')
2
- const {Transform} = require ('stream')
3
- const SAXEvent = require ('./SAXEvent.js')
4
- const XMLNode = require ('./XMLNode.js')
5
- const XMLLexer = require ('./XMLLexer.js')
1
+ const assert = require ('assert')
2
+ const {Transform} = require ('stream')
3
+ const SAXEvent = require ('./SAXEvent.js')
4
+ const XMLNode = require ('./XMLNode.js')
5
+ const XMLLexer = require ('./XMLLexer.js')
6
+ const XMLPosition = require ('./XMLPosition.js')
6
7
  const XMLValidatior = require ('./validation/XMLValidatior.js')
7
8
 
8
9
  const OPT_SRC = Symbol ('_src')
@@ -10,10 +11,10 @@ const OPT_SAX = Symbol ('_sax')
10
11
 
11
12
  const NO_CHILDREN = []; NO_CHILDREN.push = () => {}
12
13
 
13
- const CH_LF = '\n'.charCodeAt (0)
14
-
15
14
  const XMLReader = class extends Transform {
16
15
 
16
+ #position = new XMLPosition ()
17
+
17
18
  constructor (options = {}) {
18
19
 
19
20
  options.decodeStrings = false
@@ -70,16 +71,15 @@ const XMLReader = class extends Transform {
70
71
  this.useNamespaces = options.useNamespaces
71
72
  this.filter = filter || null
72
73
  this.map = map || null
73
-
74
- this.lineNo = 1
75
- this.lineStart = 0
74
+
75
+ this.line = 0
76
+ this.localPosition = 0
76
77
 
77
78
  if (this.useEntities) this.entityResolver = new (require ('./EntityResolver.js')) ()
78
79
 
79
80
  this.text = ''
80
81
  this.element = null
81
82
  this.position = 0
82
- this.pos = 0 // TODO: cleanup
83
83
 
84
84
  this [OPT_SRC] = null; this.on ('pipe', src => this [OPT_SRC] = src)
85
85
 
@@ -252,7 +252,7 @@ const XMLReader = class extends Transform {
252
252
 
253
253
  get linePos () {
254
254
 
255
- return [this.lineNo, this.pos - this.lineStart]
255
+ return this.#position.toJSON ()
256
256
 
257
257
  }
258
258
 
@@ -260,17 +260,7 @@ const XMLReader = class extends Transform {
260
260
 
261
261
  const {length} = chunk; if (length !== 0) {
262
262
 
263
- for (let i = 0; i < length; i ++) {
264
-
265
- this.pos ++
266
-
267
- if (chunk.charCodeAt (i) !== CH_LF) continue
268
-
269
- this.lineNo ++
270
-
271
- this.lineStart = this.pos
272
-
273
- }
263
+ this.#position.scan (chunk)
274
264
 
275
265
  let e = new XMLNode (chunk, this.entityResolver), {type} = e, {element} = this
276
266
 
package/lib/XMLSchema.js CHANGED
@@ -15,10 +15,26 @@ const XMLSchema = class extends Map {
15
15
 
16
16
  this [TYPES] = new Map ()
17
17
 
18
- {(this.parent = parent).set (this.targetNamespace = targetNamespace, this)}
18
+ this.targetNamespace = targetNamespace
19
+ this.parent = parent
19
20
 
20
21
  this.setSource (node)
21
22
 
23
+ if (this.parent.has (targetNamespace)) {
24
+
25
+ const existing = this.parent.get (targetNamespace)
26
+
27
+ for (const [k, v] of this.entries ()) existing.set (k, v)
28
+
29
+ for (const [k, v] of this [TYPES].entries ()) existing [TYPES].set (k, v)
30
+
31
+ }
32
+ else {
33
+
34
+ this.parent.set (targetNamespace, this)
35
+
36
+ }
37
+
22
38
  }
23
39
 
24
40
  adjustNode (node) {
@@ -51,6 +67,7 @@ const XMLSchema = class extends Map {
51
67
  case 'group':
52
68
  splitNs ('ref')
53
69
  splitNs ('type')
70
+ splitNs ('substitutionGroup')
54
71
  break
55
72
 
56
73
  case 'extension':
@@ -50,8 +50,8 @@ const XMLSchemata = class extends Map {
50
50
 
51
51
  }
52
52
 
53
- getType ([localName, namespaceURI]) {
54
-
53
+ getType ([localName, namespaceURI]) {
54
+
55
55
  const schema = this.get (namespaceURI); if (schema == null) throw new Error ('Unknown namespace: ' + namespaceURI)
56
56
 
57
57
  const node = schema.getType (localName)
@@ -234,12 +234,22 @@ const XMLSchemata = class extends Map {
234
234
 
235
235
  const schema = new XMLSchema (this, targetNamespace, node)
236
236
 
237
- for (const {localName, namespaceURI, attributes: {schemaLocation, namespace}} of schema._src.children)
237
+ for (const {localName, namespaceURI, attributes: {schemaLocation, namespace}} of schema._src.children) {
238
238
 
239
- if (localName === 'import' && namespaceURI === XMLSchema.namespaceURI && namespace !== NamespacesMap.XMLNamespace)
239
+ if (localName === 'import' && namespaceURI === XMLSchema.namespaceURI && namespace !== NamespacesMap.XMLNamespace) {
240
240
 
241
241
  this.addFile (path.join (options.dirname, schemaLocation), {targetNamespace: namespace})
242
242
 
243
+ }
244
+
245
+ if (localName === 'include' && namespaceURI === XMLSchema.namespaceURI) {
246
+
247
+ this.addFile (path.join (options.dirname, schemaLocation), {targetNamespace})
248
+
249
+ }
250
+
251
+ }
252
+
243
253
  }
244
254
 
245
255
  addFile (fn, options = {}) {
@@ -6,11 +6,22 @@ class MatchElement extends Match {
6
6
 
7
7
  const {node: {attributes: {name}, targetNamespace}} = this.occurable
8
8
 
9
- if (localName !== name || namespaceURI != targetNamespace) return null
9
+ if (localName === name && namespaceURI == targetNamespace) {
10
+
11
+ this.occured ++
12
+
13
+ return this.occurable.node
14
+
15
+ }
16
+
17
+ const el = this.occurable.xs?.getSchema (namespaceURI)?.get (localName)
18
+ const sg = el?.attributes?.substitutionGroup
19
+
20
+ if (!sg || sg [0] !== name || sg [1] !== targetNamespace) return null
10
21
 
11
22
  this.occured ++
12
23
 
13
- return this.occurable.node
24
+ return el
14
25
 
15
26
  }
16
27
 
@@ -116,13 +116,39 @@ class XMLValidationState {
116
116
 
117
117
  }
118
118
 
119
+ resolveAttribute (name, attributesMap) {
120
+
121
+ const {attributes} = this.anyType
122
+
123
+ if (attributes.has (name)) {
124
+
125
+ const def = attributes.get (name)
126
+
127
+ if (!def.targetNamespace || def.targetNamespace === attributesMap.getNamespaceURI (name)) return def
128
+
129
+ }
130
+
131
+ const localName = attributesMap.getLocalName (name); if (localName === name) return null
132
+
133
+ const def = attributes.get (localName); if (!def) return null
134
+
135
+ if (def.targetNamespace !== attributesMap.getNamespaceURI (name)) return null
136
+
137
+ return def
138
+
139
+ }
140
+
119
141
  validateAttributes (attributesMap) {
120
142
 
121
143
  const {attributes, isAnyAttributeAllowed} = this.anyType
122
144
 
145
+ const matched = new Set ()
146
+
123
147
  for (const [name, value] of attributesMap) {
124
148
 
125
- if (!attributes.has (name)) {
149
+ const def = this.resolveAttribute (name, attributesMap)
150
+
151
+ if (!def) {
126
152
 
127
153
  if (isAnyAttributeAllowed) continue
128
154
 
@@ -131,8 +157,10 @@ class XMLValidationState {
131
157
  throw Error (`Unknown attribute: "${name}"`)
132
158
 
133
159
  }
134
-
135
- const def = attributes.get (name), {attributes: {fixed, type}} = def
160
+
161
+ matched.add (def)
162
+
163
+ const {attributes: {fixed, type}} = def
136
164
 
137
165
  if (typeof fixed === 'string' && value !== fixed) throw Error (`The attribute "${name}" must have the value "${fixed}", not "${value}"`)
138
166
 
@@ -164,7 +192,7 @@ class XMLValidationState {
164
192
 
165
193
  }
166
194
 
167
- for (const [name, def] of attributes) if (!attributesMap.has (name)) {
195
+ for (const [name, def] of attributes) if (!matched.has (def)) {
168
196
 
169
197
  if (def.attributes.use === 'required') throw Error (`Missing required attribute: "${name}"`)
170
198
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xml-toolkit",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "XML parser, marshaller, SOAP adapter",
5
5
  "main": "index.js",
6
6
  "files": [