xml-toolkit 1.1.4 → 1.1.6
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 +5 -1
- package/index.js +1 -0
- package/lib/XMLIterator.js +2 -2
- package/lib/XMLLexer.js +20 -18
- package/lib/XMLMessages.js +88 -0
- package/lib/XMLParser.js +14 -7
- package/lib/XMLPosition.js +49 -3
- package/lib/XMLReader.js +15 -23
- package/lib/XMLSchema.js +2 -1
- package/lib/XMLSchemata.js +8 -12
- package/lib/simple/DT7.js +37 -36
- package/lib/simple/XSSimpleType.js +37 -21
- package/lib/simple/XSSimpleTypeBoolean.js +2 -2
- package/lib/simple/XSSimpleTypeDT.js +39 -6
- package/lib/simple/XSSimpleTypeDecimal.js +14 -8
- package/lib/simple/XSSimpleTypeFloatingPoint.js +2 -2
- package/lib/validation/XMLValidationState.js +24 -62
- package/lib/validation/XMLValidatior.js +18 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,8 @@ const parser = new XMLParser ({
|
|
|
30
30
|
|
|
31
31
|
const document = parser.process (xml)
|
|
32
32
|
|
|
33
|
+
// console.log (parser.validationMessages)
|
|
34
|
+
|
|
33
35
|
for (const element of document.detach ().children) {
|
|
34
36
|
console.log (element.attributes)
|
|
35
37
|
}
|
|
@@ -46,7 +48,9 @@ const records = new XMLReader ({
|
|
|
46
48
|
//xs,
|
|
47
49
|
filterElements : 'Record',
|
|
48
50
|
map : XMLNode.toObject ({})
|
|
49
|
-
})
|
|
51
|
+
})
|
|
52
|
+
//.on ('validation-message', s => console.log (s))
|
|
53
|
+
.process (xmlSource)
|
|
50
54
|
|
|
51
55
|
// ...then:
|
|
52
56
|
// await someLoader.load (records)
|
package/index.js
CHANGED
|
@@ -31,6 +31,7 @@ module.exports = {
|
|
|
31
31
|
XMLSchema: require ('./lib/XMLSchema'),
|
|
32
32
|
XMLSchemaBuiltIn: require ('./lib/XMLSchemaBuiltIn'),
|
|
33
33
|
XMLSchemata: require ('./lib/XMLSchemata'),
|
|
34
|
+
XMLMessages: require ('./lib/XMLMessages'),
|
|
34
35
|
XSSimpleType,
|
|
35
36
|
XSSimpleTypeFloat,
|
|
36
37
|
XSSimpleTypeDouble,
|
package/lib/XMLIterator.js
CHANGED
package/lib/XMLLexer.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const assert = require ('assert')
|
|
2
2
|
const {Transform} = require ('stream')
|
|
3
3
|
const {StringDecoder} = require('string_decoder')
|
|
4
|
+
const XMLPosition = require ('./XMLPosition.js')
|
|
4
5
|
|
|
5
6
|
const ST_TEXT = 0
|
|
6
7
|
const ST_LT = 1
|
|
@@ -23,6 +24,8 @@ const S_ENCODING = Symbol ('encoding')
|
|
|
23
24
|
|
|
24
25
|
const XMLLexer = class extends Transform {
|
|
25
26
|
|
|
27
|
+
#position = new XMLPosition ()
|
|
28
|
+
|
|
26
29
|
constructor (options = {}) {
|
|
27
30
|
|
|
28
31
|
options.readableObjectMode = true
|
|
@@ -51,6 +54,12 @@ const XMLLexer = class extends Transform {
|
|
|
51
54
|
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
get position () {
|
|
58
|
+
|
|
59
|
+
return this.#position
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
54
63
|
isClosing (pos) {
|
|
55
64
|
|
|
56
65
|
const {awaited, body} = this, {length} = awaited; if (length === 0) return true
|
|
@@ -74,7 +83,7 @@ const XMLLexer = class extends Transform {
|
|
|
74
83
|
|
|
75
84
|
default:
|
|
76
85
|
assert (awaited === null, 'not ST_TAG[_X]: awaited must be null')
|
|
77
|
-
this.
|
|
86
|
+
this.xPosition = 0
|
|
78
87
|
break
|
|
79
88
|
|
|
80
89
|
}
|
|
@@ -155,7 +164,7 @@ const XMLLexer = class extends Transform {
|
|
|
155
164
|
|
|
156
165
|
default:
|
|
157
166
|
this.setState (ST_TAG_X, CL_DEAFULT)
|
|
158
|
-
this.
|
|
167
|
+
this.xPosition = pos
|
|
159
168
|
break
|
|
160
169
|
|
|
161
170
|
}
|
|
@@ -182,7 +191,7 @@ const XMLLexer = class extends Transform {
|
|
|
182
191
|
|
|
183
192
|
case ST_TAG_X:
|
|
184
193
|
|
|
185
|
-
for (let pos = this.
|
|
194
|
+
for (let pos = this.xPosition + 1; pos < body.length; pos ++) {
|
|
186
195
|
|
|
187
196
|
switch (body.charCodeAt (pos)) {
|
|
188
197
|
|
|
@@ -196,7 +205,7 @@ const XMLLexer = class extends Transform {
|
|
|
196
205
|
|
|
197
206
|
}
|
|
198
207
|
|
|
199
|
-
this.
|
|
208
|
+
this.xPosition = pos
|
|
200
209
|
break
|
|
201
210
|
|
|
202
211
|
case CH_GT:
|
|
@@ -210,9 +219,6 @@ const XMLLexer = class extends Transform {
|
|
|
210
219
|
}
|
|
211
220
|
|
|
212
221
|
return
|
|
213
|
-
|
|
214
|
-
default:
|
|
215
|
-
throw new Error ('Invalid state: ' + this.state)
|
|
216
222
|
|
|
217
223
|
}
|
|
218
224
|
|
|
@@ -226,7 +232,11 @@ const XMLLexer = class extends Transform {
|
|
|
226
232
|
|
|
227
233
|
this.start = pos
|
|
228
234
|
|
|
229
|
-
if (lexeme.length
|
|
235
|
+
if (lexeme.length === 0) return
|
|
236
|
+
|
|
237
|
+
this.#position.scan (lexeme)
|
|
238
|
+
|
|
239
|
+
this.push (lexeme)
|
|
230
240
|
|
|
231
241
|
}
|
|
232
242
|
|
|
@@ -237,24 +247,16 @@ const XMLLexer = class extends Transform {
|
|
|
237
247
|
callback ()
|
|
238
248
|
|
|
239
249
|
}
|
|
240
|
-
|
|
241
|
-
getPosition () {
|
|
242
|
-
|
|
243
|
-
let {beforeBody, start} = this
|
|
244
250
|
|
|
245
|
-
return beforeBody + BigInt (start)
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
|
|
249
251
|
checkMaxLength () {
|
|
250
252
|
|
|
251
253
|
const {body, maxLength} = this, size = body.length - this.start;
|
|
252
254
|
|
|
253
255
|
if (size <= maxLength) return
|
|
254
256
|
|
|
255
|
-
|
|
257
|
+
this.#position.scan (body.slice (0, size - 1))
|
|
256
258
|
|
|
257
|
-
this.destroy (new Error (
|
|
259
|
+
this.destroy (new Error (this.position.format (['XML-00001', maxLength])))
|
|
258
260
|
|
|
259
261
|
}
|
|
260
262
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const util = require ('node:util')
|
|
2
|
+
|
|
3
|
+
const VOCABULARY = new Map ([
|
|
4
|
+
|
|
5
|
+
['XML-00001', 'maxLength=%i exceeded'],
|
|
6
|
+
['XML-00002', 'Unbalanced end element'],
|
|
7
|
+
['XML-00003', 'Unmatched end element, </${%s}> expected'],
|
|
8
|
+
|
|
9
|
+
['XSD-00001', 'Unknown namespace: %s'],
|
|
10
|
+
['XSD-00002', 'The element %s is not found in %s'],
|
|
11
|
+
|
|
12
|
+
['XVC-00001', 'No nested elements allowed inside %s'],
|
|
13
|
+
['XVC-00002', 'is unexpected here; should be %s'],
|
|
14
|
+
['XVC-00003', 'Unknown attribute: %s'],
|
|
15
|
+
['XVC-00004', 'The attribute "%s" must have the value "%s", not "%s"'],
|
|
16
|
+
['XVC-00005', 'Missing required attribute: "%s"'],
|
|
17
|
+
|
|
18
|
+
['XVS-00001', `The value "%s" doesn't match the pattern %s`],
|
|
19
|
+
['XVS-00002', `The value "%s" doesn't match any of the patterns: %s`],
|
|
20
|
+
['XVS-00003', `The value '%s' is not in list: %s`],
|
|
21
|
+
['XVS-00004', `The value '%s' has the length %i, which exceeds the allowed maximum of %i`],
|
|
22
|
+
['XVS-00005', `The value '%s' has the length %i, which is less than the allowed minimum of %i`],
|
|
23
|
+
['XVS-00006', `The value '%s' has the length %i, must be exaclty %i`],
|
|
24
|
+
['XVS-00007', `The value '%s' is less than the least allowed '%s'`],
|
|
25
|
+
['XVS-00008', `The value '%s' is greater than the greatest allowed '%s'`],
|
|
26
|
+
['XVS-00009', `The value '%s' is less or equal than the threshold '%s'`],
|
|
27
|
+
['XVS-00010', `The value '%s' is greater or equal than the threshold '%s'`],
|
|
28
|
+
['XVS-00011', `No boolean value can be empty`],
|
|
29
|
+
['XVS-00012', `'%s' is not a boolean value`],
|
|
30
|
+
['XVS-00013', `No decimal value can be empty`],
|
|
31
|
+
['XVS-00014', `'%s' is not a valid decimal: '%s' can occur only at the beginning`],
|
|
32
|
+
['XVS-00015', `'%s' is not a valid decimal: 2nd period occured at position %i`],
|
|
33
|
+
['XVS-00016', `'%s' is not a valid decimal: '%s' occured at position %i`],
|
|
34
|
+
['XVS-00017', `'%s' is not a valid decimal: it has no digits at all`],
|
|
35
|
+
['XVS-00018', `'%s' has %i digits, only %i allowed`],
|
|
36
|
+
['XVS-00019', `'%s' has %i digits after period, only %i allowed`],
|
|
37
|
+
['XVS-00020', `No floating point number can be empty`],
|
|
38
|
+
['XVS-00021', `'%s' is not a floating point number`],
|
|
39
|
+
['XVS-00022', `For a date value, the time part cannot be present, but '%s' contains it`],
|
|
40
|
+
['XVS-00023', `For a dateTime value, the time part is mandatory, missing from '%s'`],
|
|
41
|
+
['XVS-00024', `For a dateTimeStamp value, both time and timezone parts are mandatory, missing from '%s'`],
|
|
42
|
+
['XVS-00025', `The year separator is not found in '%s'`],
|
|
43
|
+
['XVS-00026', `'%s': '%s' is not a valid year, must be 4 chars long`],
|
|
44
|
+
['XVS-00027', `'%s': The character at %i must be '-', not '%s'`],
|
|
45
|
+
['XVS-00028', `'%s': The day part must be 2 chars long, found '%s'`],
|
|
46
|
+
['XVS-00029', `'%s': Invalid time part: the character at position %i must be ':', not '%s'`],
|
|
47
|
+
['XVS-00030', `'%s': '%s' is not a valid year: '%s' at position %i`],
|
|
48
|
+
['XVS-00031', `'%s': Invalid month '%s'`],
|
|
49
|
+
['XVS-00032', `'%s': Invalid day '%s'`],
|
|
50
|
+
['XVS-00033', `'%s': Non existing day`],
|
|
51
|
+
['XVS-00034', `'%s': Invalid hour '%s'`],
|
|
52
|
+
['XVS-00035', `'%s': Invalid minute '%s'`],
|
|
53
|
+
['XVS-00036', `'%s': Invalid second '%s'`],
|
|
54
|
+
['XVS-00037', `'%s': Invalid TZ hour '%s'`],
|
|
55
|
+
['XVS-00038', `'%s': Invalid TZ minute '%s'`],
|
|
56
|
+
['XVS-00039', `'%s': Unless 'Z', the timezone must start with either '+' or '-', not '%s'`],
|
|
57
|
+
['XVS-00040', `'%s': Invalid timezone length '%i'`],
|
|
58
|
+
['XVS-00041', `'%s': Invalid timezone: ':' not found at position 3`],
|
|
59
|
+
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
class XMLMessages {
|
|
63
|
+
|
|
64
|
+
static VOCABULARY = VOCABULARY
|
|
65
|
+
|
|
66
|
+
static format (args) {
|
|
67
|
+
|
|
68
|
+
const code = args [0]
|
|
69
|
+
|
|
70
|
+
args [0] = VOCABULARY.get (code)
|
|
71
|
+
|
|
72
|
+
return code + ' ' + util.format.apply (util, args)
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static raise (args) {
|
|
77
|
+
|
|
78
|
+
const error = Error (XMLMessages.format (args))
|
|
79
|
+
|
|
80
|
+
error.args = args
|
|
81
|
+
|
|
82
|
+
throw error
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = XMLMessages
|
package/lib/XMLParser.js
CHANGED
|
@@ -48,6 +48,7 @@ const XMLParser = class {
|
|
|
48
48
|
this.text = ''
|
|
49
49
|
this.document = null
|
|
50
50
|
this.element = null
|
|
51
|
+
this.validationMessages = []
|
|
51
52
|
|
|
52
53
|
const {entityResolver} = this, nodes = new XMLIterator (src, {entityResolver})
|
|
53
54
|
|
|
@@ -67,9 +68,7 @@ const XMLParser = class {
|
|
|
67
68
|
|
|
68
69
|
if (this.stripSpace) this.text = this.text.trim ()
|
|
69
70
|
if (this.text.length === 0) break
|
|
70
|
-
if (this.validator) {
|
|
71
|
-
this.validator.characters (this.text)
|
|
72
|
-
}
|
|
71
|
+
if (this.validator) {this.validator.characters (this.text)}
|
|
73
72
|
(new XMLNode (this.text, null, SAXEvent.TYPES.CHARACTERS)).parent = this.element
|
|
74
73
|
this.text = ''
|
|
75
74
|
|
|
@@ -84,20 +83,28 @@ const XMLParser = class {
|
|
|
84
83
|
this.element = node
|
|
85
84
|
if (this.document === null) {
|
|
86
85
|
this.document = node
|
|
87
|
-
if (this.xs !== null)
|
|
86
|
+
if (this.xs !== null) {
|
|
87
|
+
try {
|
|
88
|
+
this.validator = new XMLValidatior (this.xs, node, nodes.position, message => this.validationMessages.push (message))
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
this.validationMessages.push (nodes.position + err.message)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
88
94
|
}
|
|
89
95
|
else {
|
|
90
|
-
if (this.
|
|
96
|
+
if (this.validator) this.validator.startElement (node)
|
|
91
97
|
}
|
|
92
98
|
break
|
|
93
99
|
|
|
94
100
|
case SAXEvent.TYPES.END_ELEMENT:
|
|
95
101
|
|
|
96
|
-
if (this.element === null)
|
|
102
|
+
if (this.element === null) throw new Error (nodes.position.format (['XML-00002']))
|
|
103
|
+
if (this.element.name != node.name) throw new Error (nodes.position.format (['XML-00003', this.element.name]))
|
|
97
104
|
node ['_ns_map'] = this.element ['_ns_map']
|
|
98
105
|
this.element.type = type
|
|
99
106
|
this.element = this.element.parent
|
|
100
|
-
if (this.
|
|
107
|
+
if (this.validator) this.validator.endElement (node)
|
|
101
108
|
break
|
|
102
109
|
|
|
103
110
|
}
|
package/lib/XMLPosition.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
const CH_LF = '\n'.charCodeAt (0)
|
|
2
|
+
const CH_SPACE = ' '.charCodeAt (0)
|
|
3
|
+
const MAX_LEN_TOTAL = 50
|
|
4
|
+
const STR_ELLIPSIS = '...'
|
|
5
|
+
const MAX_LEN = MAX_LEN_TOTAL - STR_ELLIPSIS.length
|
|
6
|
+
|
|
7
|
+
const XMLMessages = require ('./XMLMessages')
|
|
2
8
|
|
|
3
9
|
class XMLPosition {
|
|
4
10
|
|
|
@@ -6,11 +12,13 @@ class XMLPosition {
|
|
|
6
12
|
#localPosition = 0
|
|
7
13
|
#lastLine = 0
|
|
8
14
|
#lastLocalPosition = 0
|
|
15
|
+
#chunk
|
|
9
16
|
|
|
10
17
|
scan (chunk) {
|
|
11
18
|
|
|
12
19
|
this.#lastLine = this.#line
|
|
13
20
|
this.#lastLocalPosition = this.#localPosition
|
|
21
|
+
this.#chunk = chunk
|
|
14
22
|
|
|
15
23
|
for (let i = 0; i < chunk.length; i ++) switch (chunk.charCodeAt (i)) {
|
|
16
24
|
|
|
@@ -26,11 +34,49 @@ class XMLPosition {
|
|
|
26
34
|
|
|
27
35
|
}
|
|
28
36
|
|
|
29
|
-
|
|
37
|
+
get line () {
|
|
38
|
+
|
|
39
|
+
return this.#lastLine + 1
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get position () {
|
|
44
|
+
|
|
45
|
+
return this.#lastLocalPosition + 1
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get chunk () {
|
|
50
|
+
|
|
51
|
+
const {length} = this.#chunk; if (length <= MAX_LEN) return this.#chunk
|
|
52
|
+
|
|
53
|
+
return this.#chunk.substr (0, MAX_LEN) + STR_ELLIPSIS
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get src () {
|
|
58
|
+
|
|
59
|
+
const b = Buffer.from (this.chunk)
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < b.length; i ++) if (b [i] < CH_SPACE) b [i] = CH_SPACE
|
|
62
|
+
|
|
63
|
+
return ' `' + b + '`'
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
toString () {
|
|
30
68
|
|
|
31
|
-
|
|
69
|
+
const {line, position, src} = this
|
|
32
70
|
|
|
33
|
-
|
|
71
|
+
return `[${line}:${position}]${src} `
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
format (args) {
|
|
76
|
+
|
|
77
|
+
return this.toString () + XMLMessages.format (args)
|
|
78
|
+
|
|
79
|
+
}
|
|
34
80
|
|
|
35
81
|
}
|
|
36
82
|
|
package/lib/XMLReader.js
CHANGED
|
@@ -3,7 +3,6 @@ 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 XMLPosition = require ('./XMLPosition.js')
|
|
7
6
|
const XMLValidatior = require ('./validation/XMLValidatior.js')
|
|
8
7
|
|
|
9
8
|
const OPT_SRC = Symbol ('_src')
|
|
@@ -13,8 +12,6 @@ const NO_CHILDREN = []; NO_CHILDREN.push = () => {}
|
|
|
13
12
|
|
|
14
13
|
const XMLReader = class extends Transform {
|
|
15
14
|
|
|
16
|
-
#position = new XMLPosition ()
|
|
17
|
-
|
|
18
15
|
constructor (options = {}) {
|
|
19
16
|
|
|
20
17
|
options.decodeStrings = false
|
|
@@ -72,14 +69,10 @@ const XMLReader = class extends Transform {
|
|
|
72
69
|
this.filter = filter || null
|
|
73
70
|
this.map = map || null
|
|
74
71
|
|
|
75
|
-
this.line = 0
|
|
76
|
-
this.localPosition = 0
|
|
77
|
-
|
|
78
72
|
if (this.useEntities) this.entityResolver = new (require ('./EntityResolver.js')) ()
|
|
79
73
|
|
|
80
74
|
this.text = ''
|
|
81
75
|
this.element = null
|
|
82
|
-
this.position = 0
|
|
83
76
|
|
|
84
77
|
this [OPT_SRC] = null; this.on ('pipe', src => this [OPT_SRC] = src)
|
|
85
78
|
|
|
@@ -154,13 +147,20 @@ const XMLReader = class extends Transform {
|
|
|
154
147
|
if (!this.validator) switch (xmlNode.type) {
|
|
155
148
|
|
|
156
149
|
case SAXEvent.TYPES.START_ELEMENT:
|
|
157
|
-
|
|
150
|
+
try {
|
|
151
|
+
this.validator = new XMLValidatior (this.xs, xmlNode, this.lex.position, s => this.emit ('validation-message', s))
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
this.emit ('validation-message', this.lex.position + err.message)
|
|
155
|
+
}
|
|
158
156
|
|
|
159
157
|
default:
|
|
160
158
|
return
|
|
161
159
|
|
|
162
160
|
}
|
|
163
161
|
|
|
162
|
+
// if (!this.validator) return
|
|
163
|
+
|
|
164
164
|
switch (xmlNode.type) {
|
|
165
165
|
|
|
166
166
|
case SAXEvent.TYPES.START_ELEMENT : return this.validator.startElement (xmlNode)
|
|
@@ -177,17 +177,17 @@ const XMLReader = class extends Transform {
|
|
|
177
177
|
|
|
178
178
|
if (type !== null) xmlNode.type = type
|
|
179
179
|
|
|
180
|
-
if (this.xs) try {
|
|
180
|
+
if (this.xs) /*try {*/
|
|
181
181
|
|
|
182
182
|
this.validate (xmlNode)
|
|
183
|
-
|
|
183
|
+
/*
|
|
184
184
|
}
|
|
185
185
|
catch (error) {
|
|
186
186
|
|
|
187
187
|
this.destroy (error)
|
|
188
188
|
|
|
189
189
|
}
|
|
190
|
-
|
|
190
|
+
*/
|
|
191
191
|
const {filter} = this; if (filter !== null) {
|
|
192
192
|
|
|
193
193
|
if (!filter (xmlNode)) return
|
|
@@ -250,18 +250,10 @@ const XMLReader = class extends Transform {
|
|
|
250
250
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
get linePos () {
|
|
254
|
-
|
|
255
|
-
return this.#position.toJSON ()
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
|
|
259
253
|
_transform (chunk, encoding, callback) {
|
|
260
254
|
|
|
261
255
|
const {length} = chunk; if (length !== 0) {
|
|
262
256
|
|
|
263
|
-
this.#position.scan (chunk)
|
|
264
|
-
|
|
265
257
|
let e = new XMLNode (chunk, this.entityResolver), {type} = e, {element} = this
|
|
266
258
|
|
|
267
259
|
switch (type) {
|
|
@@ -284,10 +276,12 @@ const XMLReader = class extends Transform {
|
|
|
284
276
|
}
|
|
285
277
|
|
|
286
278
|
switch (type) {
|
|
287
|
-
|
|
279
|
+
|
|
288
280
|
case SAXEvent.TYPES.END_ELEMENT:
|
|
289
281
|
|
|
290
|
-
if (element === null)
|
|
282
|
+
if (element === null) return callback (new Error (this.lex.position.format (['XML-00002'])))
|
|
283
|
+
|
|
284
|
+
if (e.name != element.name) return callback (new Error (this.lex.position.format (['XML-00003', this.element.name])))
|
|
291
285
|
|
|
292
286
|
e = element
|
|
293
287
|
|
|
@@ -324,8 +318,6 @@ const XMLReader = class extends Transform {
|
|
|
324
318
|
|
|
325
319
|
}
|
|
326
320
|
|
|
327
|
-
this.position += length
|
|
328
|
-
|
|
329
321
|
}
|
|
330
322
|
|
|
331
323
|
callback ()
|
package/lib/XMLSchema.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const XMLMessages = require ('./XMLMessages.js')
|
|
1
2
|
const TYPES = Symbol ('_types')
|
|
2
3
|
|
|
3
4
|
const FORM_U = 'unqualified'
|
|
@@ -91,7 +92,7 @@ const XMLSchema = class extends Map {
|
|
|
91
92
|
|
|
92
93
|
const el = this.get (localName)
|
|
93
94
|
|
|
94
|
-
if (!el || el.localName !== 'element')
|
|
95
|
+
if (!el || el.localName !== 'element') XMLMessages.raise (['XSD-00002', localName, this.targetNamespace])
|
|
95
96
|
|
|
96
97
|
return el
|
|
97
98
|
|
package/lib/XMLSchemata.js
CHANGED
|
@@ -7,6 +7,7 @@ const XMLSchema = require ('./XMLSchema.js')
|
|
|
7
7
|
const XMLSchemaXml = require ('./XMLSchemaXml.js')
|
|
8
8
|
const XMLSchemaBuiltIn = require ('./XMLSchemaBuiltIn.js')
|
|
9
9
|
const XMLMarshaller = require ('./XMLMarshaller.js')
|
|
10
|
+
const XMLMessages = require ('./XMLMessages.js')
|
|
10
11
|
|
|
11
12
|
const XSAnyType = require ('./XSAnyType.js')
|
|
12
13
|
const {XSSimpleType} = require ('./simple/XSSimpleType.js')
|
|
@@ -117,24 +118,19 @@ const XMLSchemata = class extends Map {
|
|
|
117
118
|
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
test (v) {
|
|
121
122
|
|
|
122
|
-
|
|
123
|
+
let message = ''; for (const node of this.nodes) {
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
const result = this.xs.getSimpleType (node).test (v); if (!result) return null
|
|
125
126
|
|
|
126
|
-
|
|
127
|
+
if (message.length !== 0) message += '; '
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
catch (err) {
|
|
130
|
-
|
|
131
|
-
messages.push (err.message)
|
|
132
|
-
|
|
133
|
-
}
|
|
129
|
+
message += XMLMessages.format (result)
|
|
134
130
|
|
|
135
131
|
}
|
|
136
132
|
|
|
137
|
-
|
|
133
|
+
return message
|
|
138
134
|
|
|
139
135
|
}
|
|
140
136
|
|
|
@@ -172,7 +168,7 @@ const XMLSchemata = class extends Map {
|
|
|
172
168
|
|
|
173
169
|
getSchema (namespaceURI) {
|
|
174
170
|
|
|
175
|
-
if (!this.has (namespaceURI))
|
|
171
|
+
if (!this.has (namespaceURI)) XMLMessages.raise (['XSD-00001', namespaceURI])
|
|
176
172
|
|
|
177
173
|
return this.get (namespaceURI)
|
|
178
174
|
|
package/lib/simple/DT7.js
CHANGED
|
@@ -15,28 +15,38 @@ class DT7 {
|
|
|
15
15
|
|
|
16
16
|
const t = typeof src; if (t !== 'string') throw Error (`The Seven-property Date Time Model can only be constructed from a string, not a(n) ${t}`)
|
|
17
17
|
|
|
18
|
-
const {length} = src; if (length < MIN_LENGTH) throw Error (`${length} chars found, at least ${MIN_LENGTH} required`)
|
|
19
|
-
|
|
20
18
|
this.src = src
|
|
21
19
|
|
|
20
|
+
const {length} = src; if (length < MIN_LENGTH) this.raise ('XVS-00005', length, MIN_LENGTH)
|
|
21
|
+
|
|
22
22
|
this.parse ()
|
|
23
23
|
this.validate ()
|
|
24
24
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
raise (code, ...args) {
|
|
28
|
+
|
|
29
|
+
const err = Error ('DT7 error')
|
|
30
|
+
|
|
31
|
+
err.payload = [code, this.src, ...args]
|
|
32
|
+
|
|
33
|
+
throw err
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
27
37
|
parse () {
|
|
28
38
|
|
|
29
39
|
this.posMonth = this.src.indexOf ('-', 1)
|
|
30
40
|
|
|
31
|
-
if (this.posMonth === -1)
|
|
41
|
+
if (this.posMonth === -1) this.raise ('XVS-00025')
|
|
32
42
|
|
|
33
|
-
if (this.posMonth < 3)
|
|
43
|
+
if (this.posMonth < 3) this.raise ('XVS-00026', this.year)
|
|
34
44
|
|
|
35
|
-
if (this.src.charCodeAt (this.posDay) !== CH_MINUS)
|
|
45
|
+
if (this.src.charCodeAt (this.posDay) !== CH_MINUS) this.raise ('XVS-00027', this.posDay, this.src.charAt (this.posDay))
|
|
36
46
|
|
|
37
47
|
const {length} = this.src
|
|
38
48
|
|
|
39
|
-
if (length < this.endOfDate)
|
|
49
|
+
if (length < this.endOfDate) this.raise ('XVS-00028', this.day)
|
|
40
50
|
|
|
41
51
|
if (length !== (this.endOfDateTime = this.endOfDate)) this.parseAfterDate ()
|
|
42
52
|
|
|
@@ -46,12 +56,8 @@ class DT7 {
|
|
|
46
56
|
|
|
47
57
|
if (this.src.charCodeAt (this.endOfDate) !== CH_T) return
|
|
48
58
|
|
|
49
|
-
for (const pos of [this.endOfHour, this.endOfMinute])
|
|
50
|
-
|
|
51
|
-
if (this.src.charCodeAt (pos) !== CH_COLON)
|
|
59
|
+
for (const pos of [this.endOfHour, this.endOfMinute]) if (this.src.charCodeAt (pos) !== CH_COLON) this.raise ('XVS-00029', pos, this.src.charAt (pos))
|
|
52
60
|
|
|
53
|
-
throw Error (`Invalid time part: the character at position ${pos} must be ':', not '${this.src.charAt (pos)}'`)
|
|
54
|
-
|
|
55
61
|
this.hasTime = true
|
|
56
62
|
|
|
57
63
|
this.parseAfterMinute ()
|
|
@@ -164,13 +170,13 @@ class DT7 {
|
|
|
164
170
|
|
|
165
171
|
const {year} = this, {length} = year, base = year.charCodeAt (0) === CH_MINUS ? 1 : 0
|
|
166
172
|
|
|
167
|
-
if (year.charCodeAt (base) === CH_0 && length - base !== 4)
|
|
173
|
+
if (year.charCodeAt (base) === CH_0 && length - base !== 4) this.raise ('XVS-00026', year)
|
|
168
174
|
|
|
169
175
|
for (let i = base; i < length; i ++) {
|
|
170
176
|
|
|
171
177
|
const c = year.charCodeAt (i)
|
|
172
178
|
|
|
173
|
-
if (c > CH_9 || c < CH_0)
|
|
179
|
+
if (c > CH_9 || c < CH_0) this.raise ('XVS-00030', year, year.charAt (i), i)
|
|
174
180
|
|
|
175
181
|
}
|
|
176
182
|
|
|
@@ -180,7 +186,13 @@ class DT7 {
|
|
|
180
186
|
|
|
181
187
|
const {month} = this
|
|
182
188
|
|
|
183
|
-
if (month < '01' || month > '12')
|
|
189
|
+
if (month < '01' || month > '12') this.raise ('XVS-00031', month)
|
|
190
|
+
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
validate2digits (value, code, max = '59', min = '00') {
|
|
194
|
+
|
|
195
|
+
if (value < min || value > max) this.raise (code, value)
|
|
184
196
|
|
|
185
197
|
}
|
|
186
198
|
|
|
@@ -188,11 +200,11 @@ class DT7 {
|
|
|
188
200
|
|
|
189
201
|
const {day} = this
|
|
190
202
|
|
|
191
|
-
|
|
203
|
+
this.validate2digits (day, 'XVS-00032', '31', '01')
|
|
192
204
|
|
|
193
205
|
if (day === '31' || (this.month === '02' && day > '28')) {
|
|
194
206
|
|
|
195
|
-
if (new Date (this.src.substring (0, this.endOfDate)).getDate () != day)
|
|
207
|
+
if (new Date (this.src.substring (0, this.endOfDate)).getDate () != day) this.raise ('XVS-00033')
|
|
196
208
|
|
|
197
209
|
}
|
|
198
210
|
|
|
@@ -200,14 +212,12 @@ class DT7 {
|
|
|
200
212
|
|
|
201
213
|
validateTime () {
|
|
202
214
|
|
|
203
|
-
|
|
215
|
+
this.validate2digits (this.hour, 'XVS-00034', '23')
|
|
216
|
+
this.validate2digits (this.minute, 'XVS-00035')
|
|
204
217
|
|
|
205
|
-
|
|
206
|
-
if (minute < '00' || minute > '59') throw Error (`Invalid minute`)
|
|
218
|
+
const {second} = this, intSecond = second.length === 2 ? second : second.substring (0, 2)
|
|
207
219
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (intSecond < '00' || intSecond > '59') throw Error (`Invalid second`)
|
|
220
|
+
this.validate2digits (intSecond, 'XVS-00036')
|
|
211
221
|
|
|
212
222
|
}
|
|
213
223
|
|
|
@@ -220,24 +230,15 @@ class DT7 {
|
|
|
220
230
|
case CH_MINUS:
|
|
221
231
|
break
|
|
222
232
|
default:
|
|
223
|
-
|
|
233
|
+
this.raise ('XVS-00039', tz.charAt (0))
|
|
224
234
|
}
|
|
225
235
|
|
|
226
|
-
if (tz.length !== 6)
|
|
227
|
-
|
|
228
|
-
if (tz.charCodeAt (3) !== CH_COLON) throw Error (`Invalid timezone: ':' not found at position 3`)
|
|
229
|
-
|
|
230
|
-
{
|
|
236
|
+
if (tz.length !== 6) this.raise ('XVS-00040', tz.length)
|
|
231
237
|
|
|
232
|
-
|
|
238
|
+
if (tz.charCodeAt (3) !== CH_COLON) this.raise ('XVS-00041')
|
|
233
239
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
{
|
|
237
|
-
|
|
238
|
-
const mm = tz.substring (4, 6); if (mm < '00' || mm > '59') throw Error (`Invalid TZ minute: ${mm}`)
|
|
239
|
-
|
|
240
|
-
}
|
|
240
|
+
this.validate2digits (tz.substring (1, 3), 'XVS-00037', '13')
|
|
241
|
+
this.validate2digits (tz.substring (4, 6), 'XVS-00038')
|
|
241
242
|
|
|
242
243
|
tzCache.add (tz)
|
|
243
244
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const XMLMessages = require ('../XMLMessages')
|
|
2
|
+
|
|
1
3
|
class XSSimpleType {
|
|
2
4
|
|
|
3
5
|
constructor (xs) {
|
|
@@ -51,7 +53,7 @@ class XSSimpleType {
|
|
|
51
53
|
|
|
52
54
|
const result = this.test (s); if (result === null) return s
|
|
53
55
|
|
|
54
|
-
reasons.push (result)
|
|
56
|
+
reasons.push (XMLMessages.format (result))
|
|
55
57
|
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -95,30 +97,54 @@ class XSSimpleType {
|
|
|
95
97
|
|
|
96
98
|
let ordered = undefined
|
|
97
99
|
|
|
98
|
-
|
|
100
|
+
{
|
|
99
101
|
|
|
100
|
-
|
|
102
|
+
const {minInclusive} = this; if (minInclusive != null && (ordered = this.toOrdered (value)) < minInclusive) return ['XVS-00007', value, minInclusive]
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
{
|
|
107
|
+
|
|
108
|
+
const {maxInclusive} = this; if (maxInclusive != null && (ordered ??= this.toOrdered (value)) > maxInclusive) return ['XVS-00008', value, maxInclusive]
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
{
|
|
103
113
|
|
|
104
|
-
|
|
114
|
+
const {minExclusive} = this; if (minExclusive != null && (ordered ??= this.toOrdered (value)) <= minExclusive) return ['XVS-00009', value, minExclusive]
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
{
|
|
119
|
+
|
|
120
|
+
const {maxExclusive} = this; if (maxExclusive != null && (ordered ??= this.toOrdered (value)) >= maxExclusive) return ['XVS-00010', value, maxExclusive]
|
|
121
|
+
|
|
122
|
+
}
|
|
105
123
|
|
|
106
124
|
}
|
|
107
125
|
|
|
108
126
|
testLength (value) {
|
|
109
127
|
|
|
110
|
-
const l = this.getLength (value), {length} = this
|
|
128
|
+
const l = this.getLength (value), {length, units} = this
|
|
111
129
|
|
|
112
130
|
if (length === null) {
|
|
113
131
|
|
|
114
|
-
|
|
132
|
+
{
|
|
115
133
|
|
|
116
|
-
|
|
134
|
+
const {maxLength} = this; if (l > maxLength) return ['XVS-00004', value, l, maxLength]
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
{
|
|
139
|
+
|
|
140
|
+
const {minLength} = this; if (l < minLength) return ['XVS-00005', value, l, minLength]
|
|
141
|
+
|
|
142
|
+
}
|
|
117
143
|
|
|
118
144
|
}
|
|
119
145
|
else {
|
|
120
146
|
|
|
121
|
-
if (l !== length) return
|
|
147
|
+
if (l !== length) return ['XVS-00006', value, l, length]
|
|
122
148
|
|
|
123
149
|
}
|
|
124
150
|
|
|
@@ -136,7 +162,7 @@ class XSSimpleType {
|
|
|
136
162
|
|
|
137
163
|
const {values} = this; if (values.includes (s)) return null
|
|
138
164
|
|
|
139
|
-
return
|
|
165
|
+
return ['XVS-00003', JSON.stringify (values)]
|
|
140
166
|
|
|
141
167
|
}
|
|
142
168
|
|
|
@@ -146,17 +172,7 @@ class XSSimpleType {
|
|
|
146
172
|
|
|
147
173
|
for (const pattern of patterns) if (pattern.test (s)) return null
|
|
148
174
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return `The value '${s}' doesn't match any of the patterns: ${patterns.map (_ => '' + _).join (', ')}`
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
validateScalar (s) {
|
|
156
|
-
|
|
157
|
-
const result = this.test (s ?? '')
|
|
158
|
-
|
|
159
|
-
if (result) throw Error (result)
|
|
175
|
+
return [length === 1 ? 'XVS-00001' : 'XVS-00002', s, patterns.join (', ')]
|
|
160
176
|
|
|
161
177
|
}
|
|
162
178
|
|
|
@@ -57,13 +57,13 @@ class XSSimpleTypeBoolean extends XSSimpleType {
|
|
|
57
57
|
|
|
58
58
|
testLength (value) {
|
|
59
59
|
|
|
60
|
-
return value.length === 0 ? '
|
|
60
|
+
return value.length === 0 ? ['XVS-00011'] : null
|
|
61
61
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
testFormat (value) {
|
|
65
65
|
|
|
66
|
-
if (this.toOrdered (value) === undefined) return
|
|
66
|
+
if (this.toOrdered (value) === undefined) return ['XVS-00012', value]
|
|
67
67
|
|
|
68
68
|
return null
|
|
69
69
|
|
|
@@ -33,9 +33,20 @@ class XSSimpleTypeDate extends XSSimpleTypeDT {
|
|
|
33
33
|
|
|
34
34
|
testFormat (s) {
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
try {
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
const dt7 = new DT7 (s)
|
|
39
|
+
|
|
40
|
+
if (dt7.hour !== undefined) return ['XVS-00022', s]
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
|
|
45
|
+
if ('payload' in err) return err.payload
|
|
46
|
+
|
|
47
|
+
throw err
|
|
48
|
+
|
|
49
|
+
}
|
|
39
50
|
|
|
40
51
|
}
|
|
41
52
|
|
|
@@ -53,9 +64,20 @@ class XSSimpleTypeDateTime extends XSSimpleTypeDT {
|
|
|
53
64
|
|
|
54
65
|
testFormat (s) {
|
|
55
66
|
|
|
56
|
-
|
|
67
|
+
try {
|
|
68
|
+
|
|
69
|
+
const dt7 = new DT7 (s)
|
|
70
|
+
|
|
71
|
+
if (dt7.hour === undefined) return ['XVS-00023', s]
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
|
|
76
|
+
if ('payload' in err) return err.payload
|
|
57
77
|
|
|
58
|
-
|
|
78
|
+
throw err
|
|
79
|
+
|
|
80
|
+
}
|
|
59
81
|
|
|
60
82
|
}
|
|
61
83
|
|
|
@@ -75,9 +97,20 @@ class XSSimpleTypeDateTimeStamp extends XSSimpleTypeDateTime {
|
|
|
75
97
|
|
|
76
98
|
testFormat (s) {
|
|
77
99
|
|
|
78
|
-
|
|
100
|
+
try {
|
|
101
|
+
|
|
102
|
+
const dt7 = new DT7 (s)
|
|
79
103
|
|
|
80
|
-
|
|
104
|
+
if (dt7.hour === undefined || dt7.tz === undefined) return ['XVS-00024', s]
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
|
|
109
|
+
if ('payload' in err) return err.payload
|
|
110
|
+
|
|
111
|
+
throw err
|
|
112
|
+
|
|
113
|
+
}
|
|
81
114
|
|
|
82
115
|
}
|
|
83
116
|
|
|
@@ -53,7 +53,7 @@ class XSSimpleTypeDecimal extends XSSimpleType {
|
|
|
53
53
|
|
|
54
54
|
testLength (value) {
|
|
55
55
|
|
|
56
|
-
return value.length === 0 ? '
|
|
56
|
+
return value.length === 0 ? ['XVS-00013'] : null
|
|
57
57
|
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -67,16 +67,16 @@ class XSSimpleTypeDecimal extends XSSimpleType {
|
|
|
67
67
|
|
|
68
68
|
case CH_PLUS:
|
|
69
69
|
case CH_MINUS:
|
|
70
|
-
if (i !== 0) return
|
|
70
|
+
if (i !== 0) return ['XVS-00014', value, value.charAt (i)]
|
|
71
71
|
continue
|
|
72
72
|
|
|
73
73
|
case CH_PERIOD:
|
|
74
|
-
if (period !== -1) return
|
|
74
|
+
if (period !== -1) return ['XVS-00015', value, i]
|
|
75
75
|
period = i
|
|
76
76
|
continue
|
|
77
77
|
|
|
78
78
|
default:
|
|
79
|
-
if (c < CH_0 || c > CH_9) return
|
|
79
|
+
if (c < CH_0 || c > CH_9) return ['XVS-00016', value, value.charAt (i), i]
|
|
80
80
|
total ++
|
|
81
81
|
if (period !== -1) fraction ++
|
|
82
82
|
|
|
@@ -84,13 +84,19 @@ class XSSimpleTypeDecimal extends XSSimpleType {
|
|
|
84
84
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
if (total === 0) return
|
|
87
|
+
if (total === 0) return ['XVS-00017', value]
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
{
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
const {totalDigits} = this; if (total > totalDigits) return ['XVS-00018', value, total, totalDigits]
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
{
|
|
96
|
+
|
|
97
|
+
const {fractionDigits} = this; if (fraction > fractionDigits) return ['XVS-00019', value, fraction, fractionDigits]
|
|
98
|
+
|
|
99
|
+
}
|
|
94
100
|
|
|
95
101
|
return null
|
|
96
102
|
|
|
@@ -32,13 +32,13 @@ class XSSimpleTypeFloatingPoint extends XSSimpleType {
|
|
|
32
32
|
|
|
33
33
|
testLength (value) {
|
|
34
34
|
|
|
35
|
-
return value.length === 0 ? '
|
|
35
|
+
return value.length === 0 ? ['XVS-00020'] : null
|
|
36
36
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
testFormat (value) {
|
|
40
40
|
|
|
41
|
-
if (Number.isNaN (this.toOrdered (value)) && value !== 'NaN') return
|
|
41
|
+
if (Number.isNaN (this.toOrdered (value)) && value !== 'NaN') return ['XVS-00021', value]
|
|
42
42
|
|
|
43
43
|
return null
|
|
44
44
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const {XSINamespace} = require ('../NamespacesMap')
|
|
2
|
+
const XMLMessages = require ('../XMLMessages')
|
|
2
3
|
|
|
3
4
|
class XMLValidationState {
|
|
4
5
|
|
|
@@ -7,33 +8,10 @@ class XMLValidationState {
|
|
|
7
8
|
|
|
8
9
|
constructor (parent, parsedNode, elementDefinition) {
|
|
9
10
|
|
|
10
|
-
this.xs = (this.parent = parent).xs
|
|
11
|
+
const xs = this.xs = (this.parent = parent).xs
|
|
11
12
|
this.parsedNode = parsedNode
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
catch (cause) {
|
|
19
|
-
|
|
20
|
-
throw Error (`Validation problem with ${this.formatPosition (parsedNode)}: ${cause.message}`, {cause})
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
get getPosition () {
|
|
27
|
-
|
|
28
|
-
return this.parent.getPosition
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
setTypeFrom (element) {
|
|
33
|
-
|
|
34
|
-
const {xs} = this
|
|
35
|
-
|
|
36
|
-
const typeDefinition = xs.getTypeDefinition (element)
|
|
14
|
+
const typeDefinition = xs.getTypeDefinition (elementDefinition)
|
|
37
15
|
|
|
38
16
|
this.anyType = xs.getAnyType (typeDefinition)
|
|
39
17
|
|
|
@@ -43,25 +21,23 @@ class XMLValidationState {
|
|
|
43
21
|
|
|
44
22
|
}
|
|
45
23
|
|
|
46
|
-
|
|
24
|
+
warn (message, cause) {
|
|
47
25
|
|
|
48
|
-
|
|
26
|
+
return this.parent.warn (message, cause)
|
|
49
27
|
|
|
50
28
|
}
|
|
51
29
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const [line, char] = this.getPosition ()
|
|
30
|
+
blameNothingExpected () {
|
|
55
31
|
|
|
56
|
-
return
|
|
32
|
+
return this.warn (['XVC-00001', this.parsedNode.src])
|
|
57
33
|
|
|
58
34
|
}
|
|
59
35
|
|
|
60
36
|
blameUnexpectedTag (parsedNode, match) {
|
|
61
37
|
|
|
62
|
-
const expected = match.allExpected (parsedNode); if (expected == null) this.blameNothingExpected ()
|
|
38
|
+
const expected = match.allExpected (parsedNode); if (expected == null) return this.blameNothingExpected ()
|
|
63
39
|
|
|
64
|
-
|
|
40
|
+
return this.warn (['XVC-00002', expected])
|
|
65
41
|
|
|
66
42
|
}
|
|
67
43
|
|
|
@@ -69,9 +45,9 @@ class XMLValidationState {
|
|
|
69
45
|
|
|
70
46
|
if (this.#child !== null) return this.#child.startElement (parsedNode)
|
|
71
47
|
|
|
72
|
-
const {match} = this; if (!match)
|
|
48
|
+
const {match} = this; if (!match) return this.warn (['XVC-00001', this.anyType.debugQName])
|
|
73
49
|
|
|
74
|
-
const element = match.getElementDefinition (parsedNode); if (!element) this.blameUnexpectedTag (parsedNode, match)
|
|
50
|
+
const element = match.getElementDefinition (parsedNode); if (!element) return this.blameUnexpectedTag (parsedNode, match)
|
|
75
51
|
|
|
76
52
|
if (element.localName === 'any') return
|
|
77
53
|
|
|
@@ -85,7 +61,7 @@ class XMLValidationState {
|
|
|
85
61
|
|
|
86
62
|
if (!this.#hadText) this.characters ('')
|
|
87
63
|
|
|
88
|
-
const {match} = this; if (match && !match.isSatisfied) this.blameUnexpectedTag (parsedNode, match)
|
|
64
|
+
const {match} = this; if (match && !match.isSatisfied) return this.blameUnexpectedTag (parsedNode, match)
|
|
89
65
|
|
|
90
66
|
return null
|
|
91
67
|
|
|
@@ -103,16 +79,15 @@ class XMLValidationState {
|
|
|
103
79
|
|
|
104
80
|
this.#hadText = true
|
|
105
81
|
|
|
106
|
-
|
|
82
|
+
this.validateScalar (text, this.anyType.asSimpleType ())
|
|
107
83
|
|
|
108
|
-
|
|
84
|
+
}
|
|
109
85
|
|
|
110
|
-
|
|
111
|
-
catch (cause) {
|
|
86
|
+
validateScalar (value, type) {
|
|
112
87
|
|
|
113
|
-
|
|
88
|
+
const result = type.test (value ?? '')
|
|
114
89
|
|
|
115
|
-
|
|
90
|
+
if (result) this.warn (result)
|
|
116
91
|
|
|
117
92
|
}
|
|
118
93
|
|
|
@@ -154,7 +129,7 @@ class XMLValidationState {
|
|
|
154
129
|
|
|
155
130
|
if (attributesMap.getNamespaceURI (name) === XSINamespace) continue
|
|
156
131
|
|
|
157
|
-
|
|
132
|
+
return this.warn (['XVC-00003', name])
|
|
158
133
|
|
|
159
134
|
}
|
|
160
135
|
|
|
@@ -162,31 +137,18 @@ class XMLValidationState {
|
|
|
162
137
|
|
|
163
138
|
const {attributes: {fixed, type}} = def
|
|
164
139
|
|
|
165
|
-
if (typeof fixed === 'string' && value !== fixed)
|
|
166
|
-
|
|
167
|
-
const validateBy = typeNode => {
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
|
|
171
|
-
this.xs.getSimpleType (typeNode).validateScalar (value)
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
catch (cause) {
|
|
175
|
-
|
|
176
|
-
throw Error (`attribute '${name}": ${cause.message}`, {cause})
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
}
|
|
140
|
+
if (typeof fixed === 'string' && value !== fixed) return this.warn (['XVC-00004', name, fixed, value])
|
|
181
141
|
|
|
182
142
|
if (type) {
|
|
183
143
|
|
|
184
|
-
|
|
144
|
+
this.validateScalar (value, this.xs.getSimpleType (this.xs.getType (type)))
|
|
185
145
|
|
|
186
146
|
}
|
|
187
147
|
else {
|
|
188
148
|
|
|
189
|
-
for (const node of def.children)
|
|
149
|
+
for (const node of def.children)
|
|
150
|
+
|
|
151
|
+
this.validateScalar (value, this.xs.getSimpleType (node))
|
|
190
152
|
|
|
191
153
|
}
|
|
192
154
|
|
|
@@ -194,7 +156,7 @@ class XMLValidationState {
|
|
|
194
156
|
|
|
195
157
|
for (const [name, def] of attributes) if (!matched.has (def)) {
|
|
196
158
|
|
|
197
|
-
if (def.attributes.use === 'required')
|
|
159
|
+
if (def.attributes.use === 'required') return this.warn (['XVC-00005', name])
|
|
198
160
|
|
|
199
161
|
if ('default' in def.attributes) attributesMap.set (name, def.attributes.default)
|
|
200
162
|
|
|
@@ -4,12 +4,27 @@ class XMLValidatior {
|
|
|
4
4
|
|
|
5
5
|
#state = null
|
|
6
6
|
|
|
7
|
-
constructor (xs, rootNode,
|
|
7
|
+
constructor (xs, rootNode, position, onMessage) {
|
|
8
8
|
|
|
9
9
|
this.xs = xs
|
|
10
|
-
this.
|
|
10
|
+
this.position = position
|
|
11
11
|
|
|
12
|
-
this.
|
|
12
|
+
this.onMessage = onMessage
|
|
13
|
+
this.startElement (rootNode)
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
warn (message) {
|
|
18
|
+
|
|
19
|
+
const {position} = this
|
|
20
|
+
|
|
21
|
+
if (Array.isArray (message)) message = position.format (message)
|
|
22
|
+
|
|
23
|
+
this.onMessage (message)
|
|
24
|
+
|
|
25
|
+
return null
|
|
26
|
+
|
|
27
|
+
// throw Error (message)
|
|
13
28
|
|
|
14
29
|
}
|
|
15
30
|
|