xml-toolkit 1.0.61 → 1.1.0
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 +35 -45
- package/index.js +12 -6
- package/lib/XMLMarshaller.js +1 -1
- package/lib/XMLParser.js +42 -13
- package/lib/XMLReader.js +44 -0
- package/lib/XMLSchema.js +11 -0
- package/lib/XMLSchemaBuiltIn.js +20 -5
- package/lib/XMLSchemaXml.js +0 -1
- package/lib/XMLSchemata.js +94 -11
- package/lib/XSAnyType.js +98 -0
- package/lib/simple/DT7.js +248 -0
- package/lib/simple/XSSimpleType.js +259 -0
- package/lib/simple/XSSimpleTypeBoolean.js +91 -0
- package/lib/simple/XSSimpleTypeDT.js +96 -0
- package/lib/simple/XSSimpleTypeDecimal.js +130 -0
- package/lib/simple/XSSimpleTypeFloatingPoint.js +80 -0
- package/lib/simple/XSSimpleTypeInteger.js +72 -0
- package/lib/simple/XSSimpleTypeQName.js +24 -0
- package/lib/validation/Match.js +54 -0
- package/lib/validation/MatchAll.js +33 -0
- package/lib/validation/MatchAny.js +15 -0
- package/lib/validation/MatchChoice.js +74 -0
- package/lib/validation/MatchElement.js +45 -0
- package/lib/validation/MatchSequence.js +84 -0
- package/lib/validation/Occurable.js +88 -0
- package/lib/validation/XMLValidationState.js +169 -0
- package/lib/validation/XMLValidatior.js +38 -0
- package/lib/xml.xsd +1 -1
- package/package.json +1 -1
- package/lib/XSSimpleType.js +0 -329
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
const {XSSimpleType} = require ('./XSSimpleType.js')
|
|
2
|
+
|
|
3
|
+
const CH_PLUS = '+'.charCodeAt (0)
|
|
4
|
+
const CH_MINUS = '-'.charCodeAt (0)
|
|
5
|
+
const CH_PERIOD = '.'.charCodeAt (0)
|
|
6
|
+
const CH_0 = '0'.charCodeAt (0)
|
|
7
|
+
const CH_9 = '9'.charCodeAt (0)
|
|
8
|
+
|
|
9
|
+
class XSSimpleTypeDecimal extends XSSimpleType {
|
|
10
|
+
|
|
11
|
+
#fractionDigits = undefined
|
|
12
|
+
#totalDigits = undefined
|
|
13
|
+
|
|
14
|
+
get name () {
|
|
15
|
+
return 'decimal'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
isValidNumber (num) {
|
|
19
|
+
|
|
20
|
+
return !Number.isNaN (num)
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
toOrdered (value) {
|
|
25
|
+
|
|
26
|
+
return parseFloat (value)
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
set totalDigits (v) {
|
|
31
|
+
|
|
32
|
+
this.#totalDigits = parseInt (v)
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get totalDigits () {
|
|
37
|
+
|
|
38
|
+
return this.#totalDigits ?? super.totalDigits ?? Infinity
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
set fractionDigits (v) {
|
|
43
|
+
|
|
44
|
+
this.#fractionDigits = parseInt (v)
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get fractionDigits () {
|
|
49
|
+
|
|
50
|
+
return this.#fractionDigits ?? super.fractionDigits ?? Infinity
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
testLength (value) {
|
|
55
|
+
|
|
56
|
+
return value.length === 0 ? 'No decimal value can be empty' : null
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
testFormat (value) {
|
|
61
|
+
|
|
62
|
+
let period = -1, total = 0, fraction = 0
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < value.length; i ++) {
|
|
65
|
+
|
|
66
|
+
const c = value.charCodeAt (i); switch (c) {
|
|
67
|
+
|
|
68
|
+
case CH_PLUS:
|
|
69
|
+
case CH_MINUS:
|
|
70
|
+
if (i !== 0) return `'${value}' is not a valid decimal: '${value.charAt (i)}' can occur only at the beginning`
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
case CH_PERIOD:
|
|
74
|
+
if (period !== -1) return `'${value}' is not a valid decimal: 2nd period occured at position ${i}`
|
|
75
|
+
period = i
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
default:
|
|
79
|
+
if (c < CH_0 || c > CH_9) return `'${value}' is not a valid decimal: '${value.charAt (i)}' occured at position ${i}`
|
|
80
|
+
total ++
|
|
81
|
+
if (period !== -1) fraction ++
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (total === 0) return `'${value}' is not a valid decimal: is has no digits at all`
|
|
88
|
+
|
|
89
|
+
const {totalDigits, fractionDigits} = this
|
|
90
|
+
|
|
91
|
+
if (total > totalDigits) return `'${value}' has ${total} digits, only ${totalDigits} allowed`
|
|
92
|
+
|
|
93
|
+
if (fraction > fractionDigits) return `'${value}' has ${fraction} digits after period, only ${fractionDigits} allowed`
|
|
94
|
+
|
|
95
|
+
return null
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
stringify (value) {
|
|
100
|
+
|
|
101
|
+
let num = value
|
|
102
|
+
|
|
103
|
+
const {fractionDigits} = this
|
|
104
|
+
|
|
105
|
+
const t = typeof value; switch (t) {
|
|
106
|
+
|
|
107
|
+
case 'bigint':
|
|
108
|
+
return fractionDigits === 0 || fractionDigits === Infinity ?
|
|
109
|
+
String (value) :
|
|
110
|
+
`${value}.${'0'.repeat (fractionDigits)}`
|
|
111
|
+
|
|
112
|
+
case 'string':
|
|
113
|
+
num = parseFloat (value)
|
|
114
|
+
|
|
115
|
+
case 'number':
|
|
116
|
+
if (!this.isValidNumber (num)) this.blame (value, 'cannot be read as number')
|
|
117
|
+
return fractionDigits === Infinity ?
|
|
118
|
+
num.toString () :
|
|
119
|
+
num.toFixed (this.fractionDigits)
|
|
120
|
+
|
|
121
|
+
default:
|
|
122
|
+
this.blame (value, `no idea how to treat ${t} as decimal`)
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = XSSimpleTypeDecimal
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const {XSSimpleType} = require ('./XSSimpleType.js')
|
|
2
|
+
|
|
3
|
+
class XSSimpleTypeFloatingPoint extends XSSimpleType {
|
|
4
|
+
|
|
5
|
+
stringify (value) {
|
|
6
|
+
|
|
7
|
+
const verbatim = value
|
|
8
|
+
|
|
9
|
+
if (Number.isNaN (value)) return 'NaN'
|
|
10
|
+
|
|
11
|
+
if (typeof value !== 'number') value = this.toOrdered (value)
|
|
12
|
+
|
|
13
|
+
if (Number.isNaN (value)) this.blame (verbatim, 'Not a number, yet not NaN')
|
|
14
|
+
|
|
15
|
+
switch (value) {
|
|
16
|
+
case Number.POSITIVE_INFINITY: return 'INF'
|
|
17
|
+
case Number.NEGATIVE_INFINITY: return '-INF'
|
|
18
|
+
default: return String (value)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get name () {
|
|
24
|
+
return 'float'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
toOrdered (value) {
|
|
28
|
+
|
|
29
|
+
return parseFloat (value)
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
testLength (value) {
|
|
34
|
+
|
|
35
|
+
return value.length === 0 ? 'No floating point number can be empty' : null
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
testFormat (value) {
|
|
40
|
+
|
|
41
|
+
if (Number.isNaN (this.toOrdered (value)) && value !== 'NaN') return `'${value}' is not a floating point number`
|
|
42
|
+
|
|
43
|
+
return null
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class XSSimpleTypeFloat extends XSSimpleTypeFloatingPoint {
|
|
50
|
+
|
|
51
|
+
constructor (xs) {
|
|
52
|
+
|
|
53
|
+
super (xs)
|
|
54
|
+
this.maxInclusive = 3.4028235E+38
|
|
55
|
+
this.minInclusive = -this.maxInclusive
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class XSSimpleTypeDouble extends XSSimpleTypeFloatingPoint {
|
|
62
|
+
|
|
63
|
+
constructor (xs) {
|
|
64
|
+
|
|
65
|
+
super (xs)
|
|
66
|
+
this.maxInclusive = 1.7976931348623157E+308
|
|
67
|
+
this.minInclusive = -this.maxInclusive
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
XSSimpleTypeFloat,
|
|
75
|
+
XSSimpleTypeDouble,
|
|
76
|
+
byName: {
|
|
77
|
+
'float' : XSSimpleTypeFloat,
|
|
78
|
+
'double': XSSimpleTypeDouble,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const XSSimpleTypeDecimal = require ('./XSSimpleTypeDecimal')
|
|
2
|
+
|
|
3
|
+
const fromTo = (minInclusive, maxInclusive) => class extends XSSimpleTypeInteger {
|
|
4
|
+
|
|
5
|
+
constructor (xs) {
|
|
6
|
+
|
|
7
|
+
super (xs)
|
|
8
|
+
this.minInclusive = minInclusive
|
|
9
|
+
this.maxInclusive = maxInclusive
|
|
10
|
+
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class XSSimpleTypeInteger extends XSSimpleTypeDecimal {
|
|
16
|
+
|
|
17
|
+
get name () {
|
|
18
|
+
return 'integer'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
toOrdered (value) {
|
|
22
|
+
|
|
23
|
+
return BigInt (value)
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
set fractionDigits (v) {
|
|
28
|
+
|
|
29
|
+
if (v != '0') throw Error (`For an integer type, fractionDigits must be 0, not ${v}`)
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get fractionDigits () {
|
|
34
|
+
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
isValidNumber (num) {
|
|
40
|
+
|
|
41
|
+
return Number.isInteger (num)
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
|
|
49
|
+
XSSimpleTypeInteger,
|
|
50
|
+
|
|
51
|
+
byName: {
|
|
52
|
+
|
|
53
|
+
integer : XSSimpleTypeInteger,
|
|
54
|
+
|
|
55
|
+
negativeInteger : fromTo (undefined, 1n),
|
|
56
|
+
nonPositiveInteger : fromTo (undefined, 0n),
|
|
57
|
+
nonNegativeInteger : fromTo (0n, undefined),
|
|
58
|
+
positiveInteger : fromTo (1n, undefined),
|
|
59
|
+
|
|
60
|
+
unsignedLong : fromTo ( 0n, 18446744073709551615n),
|
|
61
|
+
unsignedInt : fromTo ( 0n, 4294967295n),
|
|
62
|
+
unsignedShort : fromTo ( 0n, 65535n),
|
|
63
|
+
unsignedByte : fromTo ( 0n, 255n),
|
|
64
|
+
|
|
65
|
+
long : fromTo (-9223372036854775808n, 9223372036854775807n),
|
|
66
|
+
int : fromTo (-2147483648n, 2147483647n),
|
|
67
|
+
short : fromTo (-32768n, 32767n),
|
|
68
|
+
byte : fromTo (-128n, 127n),
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const {XSSimpleType} = require ('./XSSimpleType.js')
|
|
2
|
+
const NamespacePrefixesMap = require ('../NamespacePrefixesMap.js')
|
|
3
|
+
|
|
4
|
+
class XSSimpleTypeQName extends XSSimpleType {
|
|
5
|
+
|
|
6
|
+
get name () {
|
|
7
|
+
return 'QName'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
stringify (value) {
|
|
11
|
+
|
|
12
|
+
if (typeof value !== 'object' || !('localName' in value)) this.blame (value, 'not a {localName} object')
|
|
13
|
+
|
|
14
|
+
const {xs} = this
|
|
15
|
+
|
|
16
|
+
if (!xs.ns) xs.ns = new NamespacePrefixesMap (xs)
|
|
17
|
+
|
|
18
|
+
return xs.ns.QName (value.localName, value.namespaceURI)
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = XSSimpleTypeQName
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
class Match {
|
|
2
|
+
|
|
3
|
+
constructor (occurable) {
|
|
4
|
+
|
|
5
|
+
this.occurable = occurable
|
|
6
|
+
this.occured = 0
|
|
7
|
+
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get isSatisfied () {
|
|
11
|
+
|
|
12
|
+
return this.occured >= this.occurable.minOccurs
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get isMaxedOut () {
|
|
17
|
+
|
|
18
|
+
return this.occured >= this.occurable.maxOccurs
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
allExpected (node) {
|
|
23
|
+
|
|
24
|
+
const all = new Set ([...this.expected (node ['_ns_map'])])
|
|
25
|
+
|
|
26
|
+
switch (all.size) {
|
|
27
|
+
|
|
28
|
+
case 0: return null
|
|
29
|
+
|
|
30
|
+
case 1: for (const first of all) return first
|
|
31
|
+
|
|
32
|
+
default: return `(${[...all].sort ().join (' | ')})`
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// toString () {
|
|
39
|
+
|
|
40
|
+
// const {occurable: {minOccurs, maxOccurs}, occured} = this
|
|
41
|
+
|
|
42
|
+
// return `[${minOccurs} .. ${maxOccurs}]: ${occured}`
|
|
43
|
+
|
|
44
|
+
// }
|
|
45
|
+
|
|
46
|
+
getElementDefinition (node) {
|
|
47
|
+
|
|
48
|
+
return this.isMaxedOut ? null : this.getNextElementDefinition (node)
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = Match
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const Match = require ('./Match')
|
|
2
|
+
|
|
3
|
+
class All extends Match {
|
|
4
|
+
|
|
5
|
+
getNextElementDefinition (node) {
|
|
6
|
+
|
|
7
|
+
for (const child of this.children) if (child.occured === 0) {
|
|
8
|
+
|
|
9
|
+
const el = child.getElementDefinition (node); if (el === null) continue
|
|
10
|
+
|
|
11
|
+
this.occured = 1; for (const child of this.children) if (child.occured === 0) this.occured = 0
|
|
12
|
+
|
|
13
|
+
return el
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return null
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
* expected (nsMap) {
|
|
22
|
+
|
|
23
|
+
for (const child of this.children)
|
|
24
|
+
|
|
25
|
+
if (!child.isSatisfied)
|
|
26
|
+
|
|
27
|
+
for (const n of child.expected (nsMap)) yield n
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = All
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const Match = require ('./Match')
|
|
2
|
+
|
|
3
|
+
class MatchChoice extends Match {
|
|
4
|
+
|
|
5
|
+
#filteredChildren = null
|
|
6
|
+
|
|
7
|
+
constructor (occurable) {
|
|
8
|
+
|
|
9
|
+
super (occurable)
|
|
10
|
+
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get filteredChildren () {
|
|
14
|
+
|
|
15
|
+
if (this.#filteredChildren === null) this.#filteredChildren = this.children
|
|
16
|
+
|
|
17
|
+
return this.#filteredChildren
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getNextElementDefinition (node) {
|
|
22
|
+
|
|
23
|
+
for (const child of this.filteredChildren) {
|
|
24
|
+
|
|
25
|
+
const el = child.getElementDefinition (node); if (el === null) continue
|
|
26
|
+
|
|
27
|
+
if (child.isSatisfied) {
|
|
28
|
+
|
|
29
|
+
this.occured ++
|
|
30
|
+
|
|
31
|
+
for (const child of (this.#filteredChildren = this.children)) child.occured = 0
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
|
|
36
|
+
this.#filteredChildren = [child]
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return el
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return null
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get isSatisfied () {
|
|
49
|
+
|
|
50
|
+
const {occured, occurable: {minOccurs}} = this
|
|
51
|
+
|
|
52
|
+
if (occured >= minOccurs) return true
|
|
53
|
+
|
|
54
|
+
if (occured < minOccurs - 1) return false
|
|
55
|
+
|
|
56
|
+
for (const child of (this.filteredChildren)) if (child.isSatisfied) return true
|
|
57
|
+
|
|
58
|
+
return false
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
* expected (nsMap) {
|
|
63
|
+
|
|
64
|
+
for (const child of this.filteredChildren)
|
|
65
|
+
|
|
66
|
+
// if (!child.isSatisfied)
|
|
67
|
+
|
|
68
|
+
for (const n of child.expected ()) yield n
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = MatchChoice
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const Match = require ('./Match')
|
|
2
|
+
|
|
3
|
+
class MatchElement extends Match {
|
|
4
|
+
|
|
5
|
+
getNextElementDefinition ({namespaceURI, localName}) {
|
|
6
|
+
|
|
7
|
+
const {node: {attributes: {name}, targetNamespace}} = this.occurable
|
|
8
|
+
|
|
9
|
+
if (localName !== name || namespaceURI != targetNamespace) return null
|
|
10
|
+
|
|
11
|
+
this.occured ++
|
|
12
|
+
|
|
13
|
+
return this.occurable.node
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get clarkNotation () {
|
|
18
|
+
|
|
19
|
+
const {occurable: {node: {attributes: {name}, targetNamespace}}} = this
|
|
20
|
+
|
|
21
|
+
return `{${targetNamespace}}${name}`
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
* expected (nsMap) {
|
|
26
|
+
|
|
27
|
+
if (this.isMaxedOut) return
|
|
28
|
+
|
|
29
|
+
const {occurable: {node: {attributes: {name}, targetNamespace}}} = this
|
|
30
|
+
|
|
31
|
+
if (!nsMap) return yield this.clarkNotation
|
|
32
|
+
|
|
33
|
+
for (const qName of nsMap.getQNames (name, targetNamespace)) return yield `<${qName}>`
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// toString () {
|
|
38
|
+
|
|
39
|
+
// return this.clarkNotation + ' ' + super.toString ()
|
|
40
|
+
|
|
41
|
+
// }
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = MatchElement
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const Match = require ('./Match')
|
|
2
|
+
|
|
3
|
+
class MatchSequence extends Match {
|
|
4
|
+
|
|
5
|
+
constructor (occurable) {
|
|
6
|
+
|
|
7
|
+
super (occurable)
|
|
8
|
+
|
|
9
|
+
this.index = 0
|
|
10
|
+
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getElementDefinition (node) {
|
|
14
|
+
|
|
15
|
+
const {children} = this, {length} = children; if (length === 0) return null
|
|
16
|
+
|
|
17
|
+
while (!this.isMaxedOut) {
|
|
18
|
+
|
|
19
|
+
const child = children [this.index], el = child.getElementDefinition (node)
|
|
20
|
+
|
|
21
|
+
if (el !== null) return el
|
|
22
|
+
|
|
23
|
+
if (!child.isSatisfied) return null
|
|
24
|
+
|
|
25
|
+
if ((++ this.index) < length) continue
|
|
26
|
+
|
|
27
|
+
this.index = 0
|
|
28
|
+
|
|
29
|
+
this.occured ++
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return null
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get isSatisfied () {
|
|
38
|
+
|
|
39
|
+
const {children} = this, {length} = children; if (length === 0) return true
|
|
40
|
+
|
|
41
|
+
for (let i = this.index + 1; i < length; i ++) {
|
|
42
|
+
|
|
43
|
+
if (children [i].isSatisfied) continue
|
|
44
|
+
|
|
45
|
+
// console.log (children [i])
|
|
46
|
+
|
|
47
|
+
return false
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (this.occured + 1) >= this.occurable.minOccurs
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
* expected (nsMap) {
|
|
56
|
+
|
|
57
|
+
const {children} = this, {length} = children; if (length === 0) return
|
|
58
|
+
|
|
59
|
+
for (const n of children [this.index].expected (nsMap)) yield n
|
|
60
|
+
|
|
61
|
+
if (!children [this.index].isSatisfied) return
|
|
62
|
+
|
|
63
|
+
for (let i = this.index + 1; i < length; i ++) {
|
|
64
|
+
|
|
65
|
+
const child = children [i]
|
|
66
|
+
|
|
67
|
+
for (const n of child.expected (nsMap)) yield n
|
|
68
|
+
|
|
69
|
+
// if (child.minOccurs > 0) break
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// toString () {
|
|
76
|
+
|
|
77
|
+
// return `<${this.children.join ('; ')}>@${this.index} ${super.toString ()}`
|
|
78
|
+
|
|
79
|
+
// }
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = MatchSequence
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const CLASS_MAP = {
|
|
2
|
+
'all': require ('./MatchAll'),
|
|
3
|
+
'any': require ('./MatchAny'),
|
|
4
|
+
'choice': require ('./MatchChoice'),
|
|
5
|
+
'element': require ('./MatchElement'),
|
|
6
|
+
'sequence': require ('./MatchSequence'),
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const parseOccurs = v => v == null ? 1 : v === 'unbounded' ? Infinity : parseInt (v)
|
|
10
|
+
|
|
11
|
+
class Occurable {
|
|
12
|
+
|
|
13
|
+
node
|
|
14
|
+
|
|
15
|
+
constructor (arg, xs, minOccurs, maxOccurs) {
|
|
16
|
+
|
|
17
|
+
if (xs) this.xs = xs
|
|
18
|
+
|
|
19
|
+
this.children = []
|
|
20
|
+
|
|
21
|
+
if (Array.isArray (arg)) {
|
|
22
|
+
|
|
23
|
+
this.type = 'sequence'
|
|
24
|
+
this.minOccurs = 1
|
|
25
|
+
this.maxOccurs = 1
|
|
26
|
+
|
|
27
|
+
for (const o of arg) {
|
|
28
|
+
|
|
29
|
+
if (o.type === 'sequence' && o.minOccurs === 1 && o.maxOccurs === 1) {
|
|
30
|
+
|
|
31
|
+
this.children = this.children.concat (o.children)
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
|
|
36
|
+
this.children.push (o)
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
|
|
45
|
+
const node = arg
|
|
46
|
+
|
|
47
|
+
this.node = node
|
|
48
|
+
|
|
49
|
+
this.type = node.localName
|
|
50
|
+
|
|
51
|
+
this.minOccurs = parseOccurs (minOccurs ?? node.attributes ['minOccurs'])
|
|
52
|
+
this.maxOccurs = parseOccurs (maxOccurs ?? node.attributes ['maxOccurs'])
|
|
53
|
+
|
|
54
|
+
this.scan ()
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
scan () {
|
|
61
|
+
|
|
62
|
+
for (let node of this.node.children) switch (node.localName) {
|
|
63
|
+
case 'any':
|
|
64
|
+
case 'all':
|
|
65
|
+
case 'choice':
|
|
66
|
+
case 'sequence':
|
|
67
|
+
case 'element':
|
|
68
|
+
const {minOccurs, maxOccurs} = node.attributes
|
|
69
|
+
while ('ref' in node.attributes) node = this.xs.getByReference (node.attributes.ref)
|
|
70
|
+
this.children.push (new Occurable (node, this.xs, minOccurs, maxOccurs))
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
createMatch () {
|
|
77
|
+
|
|
78
|
+
const {type} = this, m = new CLASS_MAP [type] (this)
|
|
79
|
+
|
|
80
|
+
if (type !== 'element') m.children = this.children.map (o => o.createMatch ())
|
|
81
|
+
|
|
82
|
+
return m
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = Occurable
|