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.
- package/lib/XMLIterator.js +21 -28
- package/lib/XMLParser.js +1 -1
- package/lib/XMLPosition.js +37 -0
- package/lib/XMLReader.js +13 -23
- package/lib/XMLSchema.js +18 -1
- package/lib/XMLSchemata.js +14 -4
- package/lib/validation/MatchElement.js +13 -2
- package/lib/validation/XMLValidationState.js +32 -4
- package/package.json +1 -1
package/lib/XMLIterator.js
CHANGED
|
@@ -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
|
|
12
|
-
this.options
|
|
13
|
-
this.
|
|
14
|
-
this.selfEnclosed
|
|
15
|
-
this.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
|
-
|
|
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,
|
|
46
|
+
const {src, absolutePosition, entityResolver} = this
|
|
60
47
|
|
|
61
|
-
if (src.length -
|
|
48
|
+
if (src.length - absolutePosition < 1) return {done: true}
|
|
62
49
|
|
|
63
|
-
const value = new XMLNode (src.slice (
|
|
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
|
-
|
|
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.
|
|
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
|
|
2
|
-
const {Transform}
|
|
3
|
-
const SAXEvent
|
|
4
|
-
const XMLNode
|
|
5
|
-
const XMLLexer
|
|
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.
|
|
75
|
-
this.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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':
|
package/lib/XMLSchemata.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
|