Skip to content

Commit 6440199

Browse files
committed
Refatored schema validation
1 parent af442f4 commit 6440199

16 files changed

Lines changed: 1854 additions & 1069 deletions

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ ts/test.ts
88
notes/
99
catalog/
1010
tests/xmlschema2006-11-06
11-
tests/xmltest
11+
tests/xmltest
12+
w3c-schema-test-report.json
13+
dtd-test-report.json

ts/grammar/CompositeGrammar.ts

Lines changed: 536 additions & 119 deletions
Large diffs are not rendered by default.

ts/grammar/GrammarHandler.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ export class GrammarHandler {
4949
schemaParser.setCrossSchemaResolver((qualifiedName: string) => {
5050
return this.compositeGrammar.resolveCrossSchemaGroup(qualifiedName);
5151
});
52+
schemaParser.setCrossSchemaAttributeGroupResolver((qualifiedName: string) => {
53+
return this.compositeGrammar.resolveAttributeGroup(qualifiedName);
54+
});
5255
}
5356

5457
setCatalog(catalog: Catalog): void {

ts/schema/AnyParticle.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,4 +269,23 @@ export class AnyParticle implements ValidationParticle {
269269
// Unprefixed element - could be in default namespace or no namespace
270270
return undefined;
271271
}
272+
273+
toBNF(): string {
274+
const name: string = 'any';
275+
const min: number = this.minOccurs;
276+
const max: number = this.maxOccurs;
277+
278+
if (min === 1 && max === 1) {
279+
return name;
280+
} else if (min === 0 && max === 1) {
281+
return `${name}?`;
282+
} else if (min === 0 && max === -1) {
283+
return `${name}*`;
284+
} else if (min === 1 && max === -1) {
285+
return `${name}+`;
286+
} else {
287+
const maxStr: string = max === -1 ? 'unbounded' : max.toString();
288+
return `${name}{${min},${maxStr}}`;
289+
}
290+
}
272291
}

ts/schema/AttributeGroup.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 - 2025 Maxprograms.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Affero General Public License as published
6+
* by the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Affero General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Affero General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*******************************************************************************/
17+
18+
import { SchemaAttributeDecl } from "./Attribute";
19+
20+
export class AttributeGroup {
21+
private name: string;
22+
private attributes: Map<string, SchemaAttributeDecl> = new Map();
23+
private attributeGroupRefs: string[] = [];
24+
private targetNamespace?: string;
25+
26+
constructor(name: string, targetNamespace?: string) {
27+
this.name = name;
28+
this.targetNamespace = targetNamespace;
29+
}
30+
31+
getName(): string {
32+
return this.name;
33+
}
34+
35+
setName(name: string): void {
36+
this.name = name;
37+
}
38+
39+
getTargetNamespace(): string | undefined {
40+
return this.targetNamespace;
41+
}
42+
43+
setTargetNamespace(namespace: string): void {
44+
this.targetNamespace = namespace;
45+
}
46+
47+
addAttribute(attribute: SchemaAttributeDecl): void {
48+
this.attributes.set(attribute.getName(), attribute);
49+
}
50+
51+
getAttribute(name: string): SchemaAttributeDecl | undefined {
52+
return this.attributes.get(name);
53+
}
54+
55+
getAttributes(): Map<string, SchemaAttributeDecl> {
56+
return new Map(this.attributes);
57+
}
58+
59+
addAttributeGroupRef(ref: string): void {
60+
this.attributeGroupRefs.push(ref);
61+
}
62+
63+
getAttributeGroupRefs(): string[] {
64+
return [...this.attributeGroupRefs];
65+
}
66+
67+
removeAttribute(name: string): boolean {
68+
return this.attributes.delete(name);
69+
}
70+
71+
hasAttribute(name: string): boolean {
72+
return this.attributes.has(name);
73+
}
74+
75+
getAttributeCount(): number {
76+
return this.attributes.size;
77+
}
78+
79+
clear(): void {
80+
this.attributes.clear();
81+
this.attributeGroupRefs.length = 0;
82+
}
83+
84+
toString(): string {
85+
const ns = this.targetNamespace ? `{${this.targetNamespace}}` : '';
86+
return `AttributeGroup[${ns}${this.name}]`;
87+
}
88+
}

ts/schema/BuiltinTypes.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,22 @@ export class BuiltinTypes {
3939
this.createType("token", xsNamespace, this.validateToken);
4040
this.createType("language", xsNamespace, this.validateLanguage);
4141
this.createType("Name", xsNamespace, this.validateName);
42-
this.createType("NCName", xsNamespace, this.validateNCName);
42+
this.createType("NCName", xsNamespace, BuiltinTypes.validateNCName);
4343
this.createType("ID", xsNamespace, this.validateID);
4444
this.createType("IDREF", xsNamespace, this.validateIDREF);
45-
this.createType("ENTITY", xsNamespace, this.validateNCName);
45+
this.createType("ENTITY", xsNamespace, BuiltinTypes.validateNCName);
4646
this.createType("NMTOKEN", xsNamespace, this.validateNMTOKEN);
4747

4848
// Numeric types
4949
this.createType("decimal", xsNamespace, this.validateDecimal);
50-
this.createType("integer", xsNamespace, this.validateInteger);
50+
this.createType("integer", xsNamespace, BuiltinTypes.validateInteger);
5151
this.createType("nonPositiveInteger", xsNamespace, this.validateNonPositiveInteger);
5252
this.createType("negativeInteger", xsNamespace, this.validateNegativeInteger);
5353
this.createType("long", xsNamespace, this.validateLong);
5454
this.createType("int", xsNamespace, this.validateInt);
5555
this.createType("short", xsNamespace, this.validateShort);
5656
this.createType("byte", xsNamespace, this.validateByte);
57-
this.createType("nonNegativeInteger", xsNamespace, this.validateNonNegativeInteger);
57+
this.createType("nonNegativeInteger", xsNamespace, BuiltinTypes.validateNonNegativeInteger);
5858
this.createType("positiveInteger", xsNamespace, this.validatePositiveInteger);
5959
this.createType("unsignedLong", xsNamespace, this.validateUnsignedLong);
6060
this.createType("unsignedInt", xsNamespace, this.validateUnsignedInt);
@@ -120,7 +120,7 @@ export class BuiltinTypes {
120120

121121
private static validateToken(value: string): ValidationResult {
122122
// normalizedString + no leading/trailing/consecutive spaces
123-
const normalized = this.validateNormalizedString(value);
123+
const normalized = BuiltinTypes.validateNormalizedString(value);
124124
if (!normalized.isValid) {
125125
return normalized;
126126
}
@@ -150,24 +150,24 @@ export class BuiltinTypes {
150150
}
151151

152152
private static validateNCName(value: string): ValidationResult {
153-
// Name without colons
154-
const nameResult = this.validateName(value);
153+
// Name without colon
154+
const nameResult = BuiltinTypes.validateName(value);
155155
if (!nameResult.isValid) {
156156
return nameResult;
157157
}
158158

159159
if (value.includes(':')) {
160-
return ValidationResult.error("NCName cannot contain colons");
160+
return ValidationResult.error("NCName cannot contain colon");
161161
}
162162
return ValidationResult.success();
163163
}
164164

165165
private static validateID(value: string): ValidationResult {
166-
return this.validateNCName(value);
166+
return BuiltinTypes.validateNCName(value);
167167
}
168168

169169
private static validateIDREF(value: string): ValidationResult {
170-
return this.validateNCName(value);
170+
return BuiltinTypes.validateNCName(value);
171171
}
172172

173173
private static validateNMTOKEN(value: string): ValidationResult {
@@ -197,7 +197,7 @@ export class BuiltinTypes {
197197
}
198198

199199
private static validateNonPositiveInteger(value: string): ValidationResult {
200-
const intResult = this.validateInteger(value);
200+
const intResult = BuiltinTypes.validateInteger(value);
201201
if (!intResult.isValid) {
202202
return intResult;
203203
}
@@ -210,7 +210,7 @@ export class BuiltinTypes {
210210
}
211211

212212
private static validateNegativeInteger(value: string): ValidationResult {
213-
const intResult = this.validateInteger(value);
213+
const intResult = BuiltinTypes.validateInteger(value);
214214
if (!intResult.isValid) {
215215
return intResult;
216216
}
@@ -223,7 +223,7 @@ export class BuiltinTypes {
223223
}
224224

225225
private static validateLong(value: string): ValidationResult {
226-
const intResult = this.validateInteger(value);
226+
const intResult = BuiltinTypes.validateInteger(value);
227227
if (!intResult.isValid) {
228228
return intResult;
229229
}
@@ -236,7 +236,7 @@ export class BuiltinTypes {
236236
}
237237

238238
private static validateInt(value: string): ValidationResult {
239-
const intResult = this.validateInteger(value);
239+
const intResult = BuiltinTypes.validateInteger(value);
240240
if (!intResult.isValid) {
241241
return intResult;
242242
}
@@ -249,7 +249,7 @@ export class BuiltinTypes {
249249
}
250250

251251
private static validateShort(value: string): ValidationResult {
252-
const intResult = this.validateInteger(value);
252+
const intResult = BuiltinTypes.validateInteger(value);
253253
if (!intResult.isValid) {
254254
return intResult;
255255
}
@@ -262,7 +262,7 @@ export class BuiltinTypes {
262262
}
263263

264264
private static validateByte(value: string): ValidationResult {
265-
const intResult = this.validateInteger(value);
265+
const intResult = BuiltinTypes.validateInteger(value);
266266
if (!intResult.isValid) {
267267
return intResult;
268268
}
@@ -275,7 +275,7 @@ export class BuiltinTypes {
275275
}
276276

277277
private static validateNonNegativeInteger(value: string): ValidationResult {
278-
const intResult = this.validateInteger(value);
278+
const intResult = BuiltinTypes.validateInteger(value);
279279
if (!intResult.isValid) {
280280
return intResult;
281281
}
@@ -288,7 +288,7 @@ export class BuiltinTypes {
288288
}
289289

290290
private static validatePositiveInteger(value: string): ValidationResult {
291-
const intResult = this.validateInteger(value);
291+
const intResult = BuiltinTypes.validateInteger(value);
292292
if (!intResult.isValid) {
293293
return intResult;
294294
}
@@ -301,7 +301,7 @@ export class BuiltinTypes {
301301
}
302302

303303
private static validateUnsignedLong(value: string): ValidationResult {
304-
const nonNegResult = this.validateNonNegativeInteger(value);
304+
const nonNegResult = BuiltinTypes.validateNonNegativeInteger(value);
305305
if (!nonNegResult.isValid) {
306306
return nonNegResult;
307307
}
@@ -314,7 +314,7 @@ export class BuiltinTypes {
314314
}
315315

316316
private static validateUnsignedInt(value: string): ValidationResult {
317-
const nonNegResult = this.validateNonNegativeInteger(value);
317+
const nonNegResult = BuiltinTypes.validateNonNegativeInteger(value);
318318
if (!nonNegResult.isValid) {
319319
return nonNegResult;
320320
}
@@ -327,7 +327,7 @@ export class BuiltinTypes {
327327
}
328328

329329
private static validateUnsignedShort(value: string): ValidationResult {
330-
const nonNegResult = this.validateNonNegativeInteger(value);
330+
const nonNegResult = BuiltinTypes.validateNonNegativeInteger(value);
331331
if (!nonNegResult.isValid) {
332332
return nonNegResult;
333333
}
@@ -340,7 +340,7 @@ export class BuiltinTypes {
340340
}
341341

342342
private static validateUnsignedByte(value: string): ValidationResult {
343-
const nonNegResult = this.validateNonNegativeInteger(value);
343+
const nonNegResult = BuiltinTypes.validateNonNegativeInteger(value);
344344
if (!nonNegResult.isValid) {
345345
return nonNegResult;
346346
}
@@ -497,7 +497,7 @@ export class BuiltinTypes {
497497
}
498498

499499
for (const part of parts) {
500-
const ncNameResult = this.validateNCName(part);
500+
const ncNameResult = BuiltinTypes.validateNCName(part);
501501
if (!ncNameResult.isValid) {
502502
return ValidationResult.error("Invalid QName: " + ncNameResult.errors[0].message);
503503
}

0 commit comments

Comments
 (0)