xml-toolkit 1.1.1 → 1.1.3
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/NamespacesMap.js +2 -0
- package/lib/XMLIterator.js +22 -0
- package/lib/XMLParser.js +3 -3
- package/lib/XMLReader.js +28 -4
- package/lib/XMLSchema.js +18 -1
- package/lib/XMLSchemata.js +14 -4
- package/lib/validation/MatchElement.js +13 -2
- package/lib/validation/XMLValidationState.js +38 -6
- package/package.json +1 -1
package/lib/NamespacesMap.js
CHANGED
|
@@ -2,6 +2,7 @@ const CH_COLON = ':'.charCodeAt (0)
|
|
|
2
2
|
|
|
3
3
|
const XMLNamespace = 'http://www.w3.org/XML/1998/namespace'
|
|
4
4
|
const XMLNamespacePrefix = 'xml'
|
|
5
|
+
const XSINamespace = 'http://www.w3.org/2001/XMLSchema-instance'
|
|
5
6
|
|
|
6
7
|
const DEFAULTS = [
|
|
7
8
|
[XMLNamespacePrefix, XMLNamespace],
|
|
@@ -75,5 +76,6 @@ const NamespacesMap = class extends Map {
|
|
|
75
76
|
|
|
76
77
|
NamespacesMap.XMLNamespace = XMLNamespace
|
|
77
78
|
NamespacesMap.XMLNamespacePrefix = XMLNamespacePrefix
|
|
79
|
+
NamespacesMap.XSINamespace = XSINamespace
|
|
78
80
|
|
|
79
81
|
module.exports = NamespacesMap
|
package/lib/XMLIterator.js
CHANGED
|
@@ -2,6 +2,8 @@ const XMLNode = require ('./XMLNode.js')
|
|
|
2
2
|
const SAXEvent = require ('./SAXEvent.js')
|
|
3
3
|
const EntityResolver = require ('./EntityResolver.js')
|
|
4
4
|
|
|
5
|
+
const CH_LF = '\n'.charCodeAt (0)
|
|
6
|
+
|
|
5
7
|
const XMLIterator = class {
|
|
6
8
|
|
|
7
9
|
constructor (src, options = {}) {
|
|
@@ -30,6 +32,26 @@ const XMLIterator = class {
|
|
|
30
32
|
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
get linePos () {
|
|
36
|
+
|
|
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]
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
33
55
|
next () {
|
|
34
56
|
|
|
35
57
|
if (this.selfEnclosed !== null) return this.autoClose ()
|
package/lib/XMLParser.js
CHANGED
|
@@ -41,7 +41,7 @@ const XMLParser = class {
|
|
|
41
41
|
|
|
42
42
|
if (this.useEntities) this.entityResolver = new (require ('./EntityResolver.js')) ()
|
|
43
43
|
|
|
44
|
-
}
|
|
44
|
+
}
|
|
45
45
|
|
|
46
46
|
process (src) {
|
|
47
47
|
|
|
@@ -84,7 +84,7 @@ const XMLParser = class {
|
|
|
84
84
|
this.element = node
|
|
85
85
|
if (this.document === null) {
|
|
86
86
|
this.document = node
|
|
87
|
-
if (this.xs !== null) this.validator = new XMLValidatior (this.xs, node, () =>
|
|
87
|
+
if (this.xs !== null) this.validator = new XMLValidatior (this.xs, node, () => nodes.linePos)
|
|
88
88
|
}
|
|
89
89
|
else {
|
|
90
90
|
if (this.xs !== null) this.validator.startElement (node)
|
|
@@ -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.
|
|
96
|
+
if (this.element === null) throw new Error (`Unbalanced end element tag "${node.src}" occured at position ${nodes.pos}`)
|
|
97
97
|
node ['_ns_map'] = this.element ['_ns_map']
|
|
98
98
|
this.element.type = type
|
|
99
99
|
this.element = this.element.parent
|
package/lib/XMLReader.js
CHANGED
|
@@ -10,6 +10,8 @@ const OPT_SAX = Symbol ('_sax')
|
|
|
10
10
|
|
|
11
11
|
const NO_CHILDREN = []; NO_CHILDREN.push = () => {}
|
|
12
12
|
|
|
13
|
+
const CH_LF = '\n'.charCodeAt (0)
|
|
14
|
+
|
|
13
15
|
const XMLReader = class extends Transform {
|
|
14
16
|
|
|
15
17
|
constructor (options = {}) {
|
|
@@ -68,12 +70,16 @@ const XMLReader = class extends Transform {
|
|
|
68
70
|
this.useNamespaces = options.useNamespaces
|
|
69
71
|
this.filter = filter || null
|
|
70
72
|
this.map = map || null
|
|
73
|
+
|
|
74
|
+
this.lineNo = 1
|
|
75
|
+
this.lineStart = 0
|
|
71
76
|
|
|
72
77
|
if (this.useEntities) this.entityResolver = new (require ('./EntityResolver.js')) ()
|
|
73
78
|
|
|
74
79
|
this.text = ''
|
|
75
80
|
this.element = null
|
|
76
|
-
this.position =
|
|
81
|
+
this.position = 0
|
|
82
|
+
this.pos = 0 // TODO: cleanup
|
|
77
83
|
|
|
78
84
|
this [OPT_SRC] = null; this.on ('pipe', src => this [OPT_SRC] = src)
|
|
79
85
|
|
|
@@ -111,7 +117,7 @@ const XMLReader = class extends Transform {
|
|
|
111
117
|
|
|
112
118
|
process (src, lexerOptions = {}) {
|
|
113
119
|
|
|
114
|
-
const lex = new XMLLexer (lexerOptions)
|
|
120
|
+
const lex = this.lex = new XMLLexer (lexerOptions)
|
|
115
121
|
|
|
116
122
|
lex.once ('error', x => this.destroy (x))
|
|
117
123
|
|
|
@@ -148,7 +154,7 @@ const XMLReader = class extends Transform {
|
|
|
148
154
|
if (!this.validator) switch (xmlNode.type) {
|
|
149
155
|
|
|
150
156
|
case SAXEvent.TYPES.START_ELEMENT:
|
|
151
|
-
this.validator = new XMLValidatior (this.xs, xmlNode, () => this.
|
|
157
|
+
this.validator = new XMLValidatior (this.xs, xmlNode, () => this.linePos)
|
|
152
158
|
|
|
153
159
|
default:
|
|
154
160
|
return
|
|
@@ -244,10 +250,28 @@ const XMLReader = class extends Transform {
|
|
|
244
250
|
|
|
245
251
|
}
|
|
246
252
|
|
|
253
|
+
get linePos () {
|
|
254
|
+
|
|
255
|
+
return [this.lineNo, this.pos - this.lineStart]
|
|
256
|
+
|
|
257
|
+
}
|
|
258
|
+
|
|
247
259
|
_transform (chunk, encoding, callback) {
|
|
248
260
|
|
|
249
261
|
const {length} = chunk; if (length !== 0) {
|
|
250
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
|
+
}
|
|
274
|
+
|
|
251
275
|
let e = new XMLNode (chunk, this.entityResolver), {type} = e, {element} = this
|
|
252
276
|
|
|
253
277
|
switch (type) {
|
|
@@ -310,7 +334,7 @@ const XMLReader = class extends Transform {
|
|
|
310
334
|
|
|
311
335
|
}
|
|
312
336
|
|
|
313
|
-
this.position +=
|
|
337
|
+
this.position += length
|
|
314
338
|
|
|
315
339
|
}
|
|
316
340
|
|
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
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const {XSINamespace} = require ('../NamespacesMap')
|
|
2
|
+
|
|
1
3
|
class XMLValidationState {
|
|
2
4
|
|
|
3
5
|
#child = null
|
|
@@ -49,9 +51,9 @@ class XMLValidationState {
|
|
|
49
51
|
|
|
50
52
|
formatPosition ({src}) {
|
|
51
53
|
|
|
52
|
-
const
|
|
54
|
+
const [line, char] = this.getPosition ()
|
|
53
55
|
|
|
54
|
-
return `${src} at position ${
|
|
56
|
+
return `${src} at line ${line}, position ${char}`
|
|
55
57
|
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -114,21 +116,51 @@ class XMLValidationState {
|
|
|
114
116
|
|
|
115
117
|
}
|
|
116
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
|
+
|
|
117
141
|
validateAttributes (attributesMap) {
|
|
118
142
|
|
|
119
143
|
const {attributes, isAnyAttributeAllowed} = this.anyType
|
|
120
144
|
|
|
145
|
+
const matched = new Set ()
|
|
146
|
+
|
|
121
147
|
for (const [name, value] of attributesMap) {
|
|
122
148
|
|
|
123
|
-
|
|
149
|
+
const def = this.resolveAttribute (name, attributesMap)
|
|
150
|
+
|
|
151
|
+
if (!def) {
|
|
124
152
|
|
|
125
153
|
if (isAnyAttributeAllowed) continue
|
|
126
154
|
|
|
155
|
+
if (attributesMap.getNamespaceURI (name) === XSINamespace) continue
|
|
156
|
+
|
|
127
157
|
throw Error (`Unknown attribute: "${name}"`)
|
|
128
158
|
|
|
129
159
|
}
|
|
130
|
-
|
|
131
|
-
|
|
160
|
+
|
|
161
|
+
matched.add (def)
|
|
162
|
+
|
|
163
|
+
const {attributes: {fixed, type}} = def
|
|
132
164
|
|
|
133
165
|
if (typeof fixed === 'string' && value !== fixed) throw Error (`The attribute "${name}" must have the value "${fixed}", not "${value}"`)
|
|
134
166
|
|
|
@@ -160,7 +192,7 @@ class XMLValidationState {
|
|
|
160
192
|
|
|
161
193
|
}
|
|
162
194
|
|
|
163
|
-
for (const [name, def] of attributes) if (!
|
|
195
|
+
for (const [name, def] of attributes) if (!matched.has (def)) {
|
|
164
196
|
|
|
165
197
|
if (def.attributes.use === 'required') throw Error (`Missing required attribute: "${name}"`)
|
|
166
198
|
|