xml-toolkit 1.1.3 → 1.1.5

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/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,
@@ -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
 
@@ -32,23 +33,9 @@ const XMLIterator = class {
32
33
 
33
34
  }
34
35
 
35
- get linePos () {
36
-
37
- const {pos} = this
38
-
39
- let l = 1, c = -1
36
+ get position () {
40
37
 
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
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/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.position = 0
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.position = pos
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.position + 1; pos < body.length; pos ++) {
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.position = pos
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 !== 0) this.push (lexeme)
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
- const s = body.slice (0, size - 1), dump = JSON.stringify ([s])
257
+ this.#position.scan (body.slice (0, size - 1))
256
258
 
257
- this.destroy (new Error (`The fragment ${dump} at position ${this.getPosition ()} exceeds maxLength=${maxLength}`))
259
+ this.destroy (new Error (this.position.format (['XML-00001', maxLength])))
258
260
 
259
261
  }
260
262
 
@@ -0,0 +1,57 @@
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
+ ['XVC-00001', 'No nested elements allowed inside %s'],
10
+ ['XVC-00002', 'is unexpected here; should be %s'],
11
+ ['XVC-00003', 'Unknown attribute: %s'],
12
+ ['XVC-00004', 'The attribute "%s" must have the value "%s", not "%s"'],
13
+ ['XVC-00005', 'Missing required attribute: "%s"'],
14
+
15
+ ['XVS-00001', `The value "%s" doesn't match the pattern %s`],
16
+ ['XVS-00002', `The value "%s" doesn't match any of the patterns: %s`],
17
+ ['XVS-00003', `The value '%s' is not in list: %s`],
18
+ ['XVS-00004', `The value '%s' has the length %i, which exceeds the allowed maximum of %i`],
19
+ ['XVS-00005', `The value '%s' has the length %i, which is less than the allowed minimum of %i`],
20
+ ['XVS-00006', `The value '%s' has the length %i, must be exaclty %i`],
21
+ ['XVS-00007', `The value '%s' is less than the least allowed '%s'`],
22
+ ['XVS-00008', `The value '%s' is greater than the greatest allowed '%s'`],
23
+ ['XVS-00009', `The value '%s' is less or equal than the threshold '%s'`],
24
+ ['XVS-00010', `The value '%s' is greater or equal than the threshold '%s'`],
25
+ ['XVS-00011', `No boolean value can be empty`],
26
+ ['XVS-00012', `'%s' is not a boolean value`],
27
+ ['XVS-00013', `No decimal value can be empty`],
28
+ ['XVS-00014', `'%s' is not a valid decimal: '%s' can occur only at the beginning`],
29
+ ['XVS-00015', `'%s' is not a valid decimal: 2nd period occured at position %i`],
30
+ ['XVS-00016', `'%s' is not a valid decimal: '%s' occured at position %i`],
31
+ ['XVS-00017', `'%s' is not a valid decimal: is has no digits at all`],
32
+ ['XVS-00018', `'%s' has %i digits, only %i allowed`],
33
+ ['XVS-00019', `'%s' has %i digits after period, only %i allowed`],
34
+ ['XVS-00020', `No floating point number can be empty`],
35
+ ['XVS-00021', `'%s' is not a floating point number`],
36
+ ['XVS-00022', `For a date value, the time part cannot be present, but '%s' contains it`],
37
+ ['XVS-00023', `For a dateTime value, the time part is mandatory, missing from '%s'`],
38
+ ['XVS-00024', `For a dateTimeStamp value, both time and timezone parts are mandatory, missing from '%s'`],
39
+ ['XVS-00025', `The year separator is not found in '%s'`],
40
+
41
+ ])
42
+
43
+ class XMLMessages {
44
+
45
+ static VOCABULARY = VOCABULARY
46
+
47
+ static format (args) {
48
+
49
+ args [0] = VOCABULARY.get (args [0])
50
+
51
+ return util.format.apply (util, args)
52
+
53
+ }
54
+
55
+ }
56
+
57
+ module.exports = XMLMessages
package/lib/XMLParser.js CHANGED
@@ -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, () => nodes.linePos)
87
+ if (this.xs !== null) this.validator = new XMLValidatior (this.xs, node, nodes.position)
88
88
  }
89
89
  else {
90
90
  if (this.xs !== null) this.validator.startElement (node)
@@ -93,7 +93,8 @@ 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 (nodes.position.format (['XML-00002']))
97
+ if (this.element.name != node.name) throw new Error (nodes.position.format (['XML-00003', this.element.name]))
97
98
  node ['_ns_map'] = this.element ['_ns_map']
98
99
  this.element.type = type
99
100
  this.element = this.element.parent
@@ -0,0 +1,83 @@
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')
8
+
9
+ class XMLPosition {
10
+
11
+ #line = 0
12
+ #localPosition = 0
13
+ #lastLine = 0
14
+ #lastLocalPosition = 0
15
+ #chunk
16
+
17
+ scan (chunk) {
18
+
19
+ this.#lastLine = this.#line
20
+ this.#lastLocalPosition = this.#localPosition
21
+ this.#chunk = chunk
22
+
23
+ for (let i = 0; i < chunk.length; i ++) switch (chunk.charCodeAt (i)) {
24
+
25
+ case CH_LF:
26
+ this.#line ++
27
+ this.#localPosition = 0
28
+ break
29
+
30
+ default:
31
+ this.#localPosition ++
32
+
33
+ }
34
+
35
+ }
36
+
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 () {
68
+
69
+ const {line, position, src} = this
70
+
71
+ return `[${line}:${position}]${src} `
72
+
73
+ }
74
+
75
+ format (args) {
76
+
77
+ return this.toString () + XMLMessages.format (args)
78
+
79
+ }
80
+
81
+ }
82
+
83
+ module.exports = XMLPosition
package/lib/XMLReader.js CHANGED
@@ -1,8 +1,8 @@
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
6
  const XMLValidatior = require ('./validation/XMLValidatior.js')
7
7
 
8
8
  const OPT_SRC = Symbol ('_src')
@@ -10,8 +10,6 @@ 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
-
15
13
  const XMLReader = class extends Transform {
16
14
 
17
15
  constructor (options = {}) {
@@ -70,16 +68,11 @@ const XMLReader = class extends Transform {
70
68
  this.useNamespaces = options.useNamespaces
71
69
  this.filter = filter || null
72
70
  this.map = map || null
73
-
74
- this.lineNo = 1
75
- this.lineStart = 0
76
-
71
+
77
72
  if (this.useEntities) this.entityResolver = new (require ('./EntityResolver.js')) ()
78
73
 
79
74
  this.text = ''
80
75
  this.element = null
81
- this.position = 0
82
- this.pos = 0 // TODO: cleanup
83
76
 
84
77
  this [OPT_SRC] = null; this.on ('pipe', src => this [OPT_SRC] = src)
85
78
 
@@ -154,7 +147,7 @@ const XMLReader = class extends Transform {
154
147
  if (!this.validator) switch (xmlNode.type) {
155
148
 
156
149
  case SAXEvent.TYPES.START_ELEMENT:
157
- this.validator = new XMLValidatior (this.xs, xmlNode, () => this.linePos)
150
+ this.validator = new XMLValidatior (this.xs, xmlNode, this.lex.position)
158
151
 
159
152
  default:
160
153
  return
@@ -250,28 +243,10 @@ const XMLReader = class extends Transform {
250
243
 
251
244
  }
252
245
 
253
- get linePos () {
254
-
255
- return [this.lineNo, this.pos - this.lineStart]
256
-
257
- }
258
-
259
246
  _transform (chunk, encoding, callback) {
260
247
 
261
248
  const {length} = chunk; if (length !== 0) {
262
249
 
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
-
275
250
  let e = new XMLNode (chunk, this.entityResolver), {type} = e, {element} = this
276
251
 
277
252
  switch (type) {
@@ -294,10 +269,12 @@ const XMLReader = class extends Transform {
294
269
  }
295
270
 
296
271
  switch (type) {
297
-
272
+
298
273
  case SAXEvent.TYPES.END_ELEMENT:
299
274
 
300
- if (element === null) return callback (new Error (`Unbalanced end element tag "${chunk}" occured at position ${this.position}`))
275
+ if (element === null) return callback (new Error (this.lex.position.format (['XML-00002'])))
276
+
277
+ if (e.name != element.name) return callback (new Error (this.lex.position.format (['XML-00003', this.element.name])))
301
278
 
302
279
  e = element
303
280
 
@@ -334,8 +311,6 @@ const XMLReader = class extends Transform {
334
311
 
335
312
  }
336
313
 
337
- this.position += length
338
-
339
314
  }
340
315
 
341
316
  callback ()
@@ -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
- validateScalar (v) {
121
+ test (v) {
121
122
 
122
- const messages = []; for (const node of this.nodes) {
123
+ let message = ''; for (const node of this.nodes) {
123
124
 
124
- try {
125
+ const result = this.xs.getSimpleType (node).test (v); if (!result) return null
125
126
 
126
- return this.xs.getSimpleType (node).validateScalar (v)
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
- throw Error (messages.join ('; '))
133
+ return message
138
134
 
139
135
  }
140
136
 
package/lib/simple/DT7.js CHANGED
@@ -9,13 +9,23 @@ const CH_9 = '9'.charCodeAt (0)
9
9
 
10
10
  const tzCache = new Set (['Z', '+14:00', '-14:00'])
11
11
 
12
+ const die = payload => {
13
+
14
+ const err = Error ('DT7 error')
15
+
16
+ err.payload = payload
17
+
18
+ throw err
19
+
20
+ }
21
+
12
22
  class DT7 {
13
23
 
14
24
  constructor (src) {
15
25
 
16
26
  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
27
 
18
- const {length} = src; if (length < MIN_LENGTH) throw Error (`${length} chars found, at least ${MIN_LENGTH} required`)
28
+ const {length} = src; if (length < MIN_LENGTH) die (['XVS-00005', src, length, MIN_LENGTH])
19
29
 
20
30
  this.src = src
21
31
 
@@ -28,8 +38,8 @@ class DT7 {
28
38
 
29
39
  this.posMonth = this.src.indexOf ('-', 1)
30
40
 
31
- if (this.posMonth === -1) throw Error (`The year separator is not found`)
32
-
41
+ if (this.posMonth === -1) die (['XVS-00025', this.src])
42
+
33
43
  if (this.posMonth < 3) throw Error (`'${this.year}' is not a valid year: cannot be shorter than 4 characters`)
34
44
 
35
45
  if (this.src.charCodeAt (this.posDay) !== CH_MINUS) throw Error (`The character at ${this.posDay} must be '-', not '${this.src.charAt (this.posDay)}'`)
@@ -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
- if (this.minInclusive != null && (ordered = this.toOrdered (value)) < this.minInclusive) throw Error (`The value '${value}' is less than allowed '${this.minInclusive}'`)
100
+ {
99
101
 
100
- if (this.maxInclusive != null && (ordered ??= this.toOrdered (value)) > this.maxInclusive) throw Error (`The value '${value}' is greater than allowed '${this.maxInclusive}'`)
102
+ const {minInclusive} = this; if (minInclusive != null && (ordered = this.toOrdered (value)) < minInclusive) return ['XVS-00007', value, minInclusive]
101
103
 
102
- if (this.minExclusive != null && (ordered ??= this.toOrdered (value)) <= this.minExclusive) throw Error (`The value '${value}' is less or equal than the threshold '${this.minExclusive}'`)
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
- if (this.maxExclusive != null && (ordered ??= this.toOrdered (value)) >= this.maxExclusive) throw Error (`The value '${value}' is greater or equal than the threshold '${this.maxExclusive}'`)
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
- if (l > this.maxLength) return `The value '${value}' is ${l} ${this.units} long, which exceeds the allowed maximum of ${this.maxLength}`
132
+ {
115
133
 
116
- if (l < this.minLength) return `The value '${value}' is ${l} ${this.units} long, which is less than the allowed minimum of ${this.minLength}`
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 `The value '${value}' is ${l} ${this.units} long, must be exaclty ${length}`
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 `The value '${s}' is not in list: ${JSON.stringify (values)}`
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
- if (length === 1) return `The value '${s}' doesn't match the pattern '${patterns [0]}'`
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 ? 'No boolean value can be empty' : null
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 `'${value}' is not a boolean value`
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
- const dt7 = new DT7 (s)
36
+ try {
37
+
38
+ const dt7 = new DT7 (s)
39
+
40
+ if (dt7.hour !== undefined) return ['XVS-00022', s]
41
+
42
+ }
43
+ catch (err) {
37
44
 
38
- if (dt7.hour !== undefined) return `For a date value, the time part cannot be present`
45
+ if ('payload' in err) return err.payload
46
+
47
+ throw err
48
+
49
+ }
39
50
 
40
51
  }
41
52
 
@@ -55,7 +66,7 @@ class XSSimpleTypeDateTime extends XSSimpleTypeDT {
55
66
 
56
67
  const dt7 = new DT7 (s)
57
68
 
58
- if (dt7.hour === undefined) return `For a dateTime value, the time part is mandatory`
69
+ if (dt7.hour === undefined) return ['XVS-00023', s]
59
70
 
60
71
  }
61
72
 
@@ -77,7 +88,7 @@ class XSSimpleTypeDateTimeStamp extends XSSimpleTypeDateTime {
77
88
 
78
89
  const dt7 = new DT7 (s)
79
90
 
80
- if (dt7.hour === undefined || dt7.tz === undefined) return `For a dateTimeStamp value, both time and timezone parts are mandatory`
91
+ if (dt7.hour === undefined || dt7.tz === undefined) return ['XVS-00024', s]
81
92
 
82
93
  }
83
94
 
@@ -53,7 +53,7 @@ class XSSimpleTypeDecimal extends XSSimpleType {
53
53
 
54
54
  testLength (value) {
55
55
 
56
- return value.length === 0 ? 'No decimal value can be empty' : null
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 `'${value}' is not a valid decimal: '${value.charAt (i)}' can occur only at the beginning`
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 `'${value}' is not a valid decimal: 2nd period occured at position ${i}`
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 `'${value}' is not a valid decimal: '${value.charAt (i)}' occured at position ${i}`
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 `'${value}' is not a valid decimal: is has no digits at all`
87
+ if (total === 0) return ['XVS-00017', value]
88
88
 
89
- const {totalDigits, fractionDigits} = this
89
+ {
90
90
 
91
- if (total > totalDigits) return `'${value}' has ${total} digits, only ${totalDigits} allowed`
91
+ const {totalDigits} = this; if (total > totalDigits) return ['XVS-00018', value, total, totalDigits]
92
92
 
93
- if (fraction > fractionDigits) return `'${value}' has ${fraction} digits after period, only ${fractionDigits} allowed`
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 ? 'No floating point number can be empty' : null
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 `'${value}' is not a floating point number`
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
 
@@ -17,16 +18,16 @@ class XMLValidationState {
17
18
  }
18
19
  catch (cause) {
19
20
 
20
- throw Error (`Validation problem with ${this.formatPosition (parsedNode)}: ${cause.message}`, {cause})
21
+ this.warn (cause.message)
21
22
 
22
23
  }
23
24
 
24
25
  }
25
26
 
26
- get getPosition () {
27
-
28
- return this.parent.getPosition
29
-
27
+ warn (message, cause) {
28
+
29
+ this.parent.warn (message, cause)
30
+
30
31
  }
31
32
 
32
33
  setTypeFrom (element) {
@@ -45,15 +46,7 @@ class XMLValidationState {
45
46
 
46
47
  blameNothingExpected () {
47
48
 
48
- throw Error (`No nested elements allowed inside ${this.parsedNode.src}`)
49
-
50
- }
51
-
52
- formatPosition ({src}) {
53
-
54
- const [line, char] = this.getPosition ()
55
-
56
- return `${src} at line ${line}, position ${char}`
49
+ this.warn (['XVC-00001', this.parsedNode.src])
57
50
 
58
51
  }
59
52
 
@@ -61,7 +54,7 @@ class XMLValidationState {
61
54
 
62
55
  const expected = match.allExpected (parsedNode); if (expected == null) this.blameNothingExpected ()
63
56
 
64
- throw Error (`Unexpected ${this.formatPosition (parsedNode)}, expected: ${expected}`)
57
+ this.warn (['XVC-00002', expected])
65
58
 
66
59
  }
67
60
 
@@ -69,7 +62,7 @@ class XMLValidationState {
69
62
 
70
63
  if (this.#child !== null) return this.#child.startElement (parsedNode)
71
64
 
72
- const {match} = this; if (!match) throw Error (`No nested elements allowed inside ${this.anyType.debugQName}`)
65
+ const {match} = this; if (!match) this.warn (['XVC-00001', this.anyType.debugQName])
73
66
 
74
67
  const element = match.getElementDefinition (parsedNode); if (!element) this.blameUnexpectedTag (parsedNode, match)
75
68
 
@@ -103,16 +96,15 @@ class XMLValidationState {
103
96
 
104
97
  this.#hadText = true
105
98
 
106
- try {
99
+ this.validateScalar (text, this.anyType.asSimpleType ())
107
100
 
108
- this.anyType.asSimpleType ().validateScalar (text)
101
+ }
109
102
 
110
- }
111
- catch (cause) {
103
+ validateScalar (value, type) {
112
104
 
113
- throw Error (`Validation problem with the contents of ${this.formatPosition (this.parsedNode)}: ${cause.message}`, {cause})
105
+ const result = type.test (value ?? '')
114
106
 
115
- }
107
+ if (result) this.warn (result)
116
108
 
117
109
  }
118
110
 
@@ -154,7 +146,7 @@ class XMLValidationState {
154
146
 
155
147
  if (attributesMap.getNamespaceURI (name) === XSINamespace) continue
156
148
 
157
- throw Error (`Unknown attribute: "${name}"`)
149
+ this.warn (['XVC-00003', name])
158
150
 
159
151
  }
160
152
 
@@ -162,31 +154,18 @@ class XMLValidationState {
162
154
 
163
155
  const {attributes: {fixed, type}} = def
164
156
 
165
- if (typeof fixed === 'string' && value !== fixed) throw Error (`The attribute "${name}" must have the value "${fixed}", not "${value}"`)
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
- }
157
+ if (typeof fixed === 'string' && value !== fixed) this.warn (['XVC-00004', name, fixed, value])
181
158
 
182
159
  if (type) {
183
160
 
184
- validateBy (this.xs.getType (type))
161
+ this.validateScalar (value, this.xs.getSimpleType (this.xs.getType (type)))
185
162
 
186
163
  }
187
164
  else {
188
165
 
189
- for (const node of def.children) validateBy (node)
166
+ for (const node of def.children)
167
+
168
+ this.validateScalar (value, this.xs.getSimpleType (node))
190
169
 
191
170
  }
192
171
 
@@ -194,7 +173,7 @@ class XMLValidationState {
194
173
 
195
174
  for (const [name, def] of attributes) if (!matched.has (def)) {
196
175
 
197
- if (def.attributes.use === 'required') throw Error (`Missing required attribute: "${name}"`)
176
+ if (def.attributes.use === 'required') this.warn (['XVC-00005', name])
198
177
 
199
178
  if ('default' in def.attributes) attributesMap.set (name, def.attributes.default)
200
179
 
@@ -4,15 +4,23 @@ class XMLValidatior {
4
4
 
5
5
  #state = null
6
6
 
7
- constructor (xs, rootNode, getPosition) {
7
+ constructor (xs, rootNode, position) {
8
8
 
9
9
  this.xs = xs
10
- this.getPosition = getPosition
10
+ this.position = position
11
11
 
12
12
  this.startElement (rootNode)
13
13
 
14
14
  }
15
15
 
16
+ warn (message) {
17
+
18
+ const {position} = this
19
+
20
+ throw Error (Array.isArray (message) ? position.format (message) : this.position + message)
21
+
22
+ }
23
+
16
24
  startElement (parsedNode) {
17
25
 
18
26
  if (this.#state === null) return this.#state = new XMLValidationState (this, parsedNode, this.xs.getSchema (parsedNode.namespaceURI).getElement (parsedNode.localName))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xml-toolkit",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "XML parser, marshaller, SOAP adapter",
5
5
  "main": "index.js",
6
6
  "files": [