1212
1313import { CData } from "./CData" ;
1414import { Catalog } from "./Catalog" ;
15- import { Constants } from "./Constants" ;
1615import { ContentHandler } from "./ContentHandler" ;
1716import { ProcessingInstruction } from "./ProcessingInstruction" ;
1817import { TextNode } from "./TextNode" ;
@@ -22,10 +21,9 @@ import { XMLDeclaration } from "./XMLDeclaration";
2221import { XMLDocument } from "./XMLDocument" ;
2322import { XMLDocumentType } from "./XMLDocumentType" ;
2423import { XMLElement } from "./XMLElement" ;
25- import { XMLNode } from "./XMLNode" ;
2624import { XMLUtils } from "./XMLUtils" ;
2725import { DTDParser } from "./dtd/DTDParser" ;
28- import { AttributeUse , Grammar , ValidationContext } from "./grammar/Grammar" ;
26+ import { Grammar } from "./grammar/Grammar" ;
2927import { GrammarHandler } from "./grammar/GrammarHandler" ;
3028
3129export class DOMBuilder implements ContentHandler {
@@ -41,15 +39,13 @@ export class DOMBuilder implements ContentHandler {
4139 grammar : Grammar | undefined ;
4240 validating : boolean = false ;
4341 private includeDefaultAttributes : boolean = true ;
44- private rootElementValidated : boolean = false ;
4542 private declaredIds : Set < string > = new Set ( ) ;
4643 private pendingIdrefs : string [ ] = [ ] ;
4744
4845 initialize ( ) : void {
4946 this . document = new XMLDocument ( ) ;
5047 this . stack = new Array ( ) ;
5148 this . inCdData = false ;
52- this . rootElementValidated = false ;
5349 this . declaredIds . clear ( ) ;
5450 this . pendingIdrefs = [ ] ;
5551 // Create initial GrammarHandler for this ContentHandler
@@ -78,21 +74,6 @@ export class DOMBuilder implements ContentHandler {
7874 this . grammar = this . grammarHandler . getGrammar ( ) ;
7975 }
8076
81- private validateRootElement ( elementName : string ) : void {
82- if ( ! this . rootElementValidated && this . validating && this . grammar ) {
83- const context = new ValidationContext ( [ ] , new Map ( ) , '' , undefined , true ) ;
84- const result = this . grammar . validateElement ( elementName , context ) ;
85-
86- if ( ! result . isValid ) {
87- result . errors . forEach ( error => {
88- console . error ( `Root element validation error: ${ error . message } ` ) ;
89- } ) ;
90- }
91-
92- this . rootElementValidated = true ;
93- }
94- }
95-
9677 private addDefaultAttributes ( elementName : string , element : XMLElement ) : void {
9778 if ( ! this . grammar ) {
9879 return ;
@@ -117,29 +98,7 @@ export class DOMBuilder implements ContentHandler {
11798 } ) ;
11899 }
119100
120- private validateRequiredAttributes ( elementName : string , attributes : XMLAttribute [ ] ) : void {
121- if ( ! this . validating || ! this . grammar ) {
122- return ;
123- }
124- const attDecls = this . grammar . getElementAttributes ( elementName ) ;
125- if ( ! attDecls || attDecls . size === 0 ) {
126- if ( attributes . length > 0 ) {
127- const attNames = attributes . map ( att => att . getName ( ) ) . join ( ', ' ) ;
128- throw new Error ( `Element '${ elementName } ' has no declared attributes but contains: [${ attNames } ]` ) ;
129- }
130- return ;
131- }
132-
133- const providedAttNames = new Set ( attributes . map ( att => att . getName ( ) ) ) ;
134-
135- attDecls . forEach ( ( attInfo , attName ) => {
136- if ( attInfo . use === AttributeUse . REQUIRED && ! providedAttNames . has ( attName ) ) {
137- throw new Error ( `Required attribute '${ attName } ' is missing from element '${ elementName } '` ) ;
138- }
139- } ) ;
140- }
141-
142- private validateAttributeValues ( elementName : string , attributes : XMLAttribute [ ] ) : void {
101+ private trackIdAttributes ( elementName : string , element : XMLElement ) : void {
143102 if ( ! this . validating || ! this . grammar ) {
144103 return ;
145104 }
@@ -149,32 +108,35 @@ export class DOMBuilder implements ContentHandler {
149108 return ;
150109 }
151110
152- // TODO: Additional DTD datatype validation (NMTOKEN, ENTITY, NOTATION) is now in DTDComposite
153-
154- for ( const attribute of attributes ) {
155- const attName = attribute . getName ( ) ;
156- const attValue = attribute . getValue ( ) ;
111+ element . getAttributes ( ) . forEach ( attr => {
112+ const attName = attr . getName ( ) ;
113+ const attValue = attr . getValue ( ) ;
157114 const attInfo = attDecls . get ( attName ) ;
158115
159- if ( attInfo ) {
160- // ID uniqueness and IDREF collection for validation
116+ if ( attInfo && attInfo . datatype ) {
117+ // ID uniqueness validation
161118 if ( attInfo . datatype === 'ID' ) {
162119 if ( this . declaredIds . has ( attValue ) ) {
163120 throw new Error ( `Duplicate ID value '${ attValue } ' found in element '${ elementName } '` ) ;
164121 }
165122 this . declaredIds . add ( attValue ) ;
166123 }
167124
125+ // Collect IDREF values for later validation (only if not already declared)
168126 if ( attInfo . datatype === 'IDREF' ) {
169- this . pendingIdrefs . push ( attValue ) ;
127+ if ( ! this . declaredIds . has ( attValue ) ) {
128+ this . pendingIdrefs . push ( attValue ) ;
129+ }
170130 } else if ( attInfo . datatype === 'IDREFS' ) {
171131 const idrefs = attValue . split ( / \s + / ) ;
172- this . pendingIdrefs . push ( ...idrefs ) ;
132+ idrefs . forEach ( idref => {
133+ if ( ! this . declaredIds . has ( idref ) ) {
134+ this . pendingIdrefs . push ( idref ) ;
135+ }
136+ } ) ;
173137 }
174- } else {
175- throw new Error ( `Undeclared attribute '${ attName } ' found in element '${ elementName } '` ) ;
176138 }
177- }
139+ } ) ;
178140 }
179141
180142 private validateIdReferences ( ) : void {
@@ -189,46 +151,6 @@ export class DOMBuilder implements ContentHandler {
189151 }
190152 }
191153
192- private validateElementContent ( elementName : string , children : XMLNode [ ] ) : void {
193- if ( ! this . validating || ! this . grammar ) {
194- return ;
195- }
196-
197- // Extract child element names
198- const childElementNames = children
199- . filter ( child => child . getNodeType ( ) === Constants . ELEMENT_NODE )
200- . map ( child => ( child as XMLElement ) . getName ( ) ) ;
201-
202- // Extract text content
203- const textNodes = children
204- . filter ( child => child . getNodeType ( ) === Constants . TEXT_NODE )
205- . map ( child => ( child as TextNode ) . getValue ( ) ) ;
206- const textContent = textNodes . join ( '' ) ;
207-
208- // Create attributes map
209- const currentElement = this . stack [ this . stack . length - 1 ] ;
210- const attributesArray = currentElement ?. getAttributes ( ) || [ ] ;
211- const attributesMap = new Map < string , string > ( ) ;
212- attributesArray . forEach ( attr => {
213- attributesMap . set ( attr . getName ( ) , attr . getValue ( ) ) ;
214- } ) ;
215-
216- // Create validation context
217- const context = new ValidationContext (
218- childElementNames ,
219- attributesMap ,
220- textContent ,
221- this . stack . length > 1 ? this . stack [ this . stack . length - 2 ] . getName ( ) : undefined
222- ) ;
223-
224- // Validate using Grammar interface
225- const result = this . grammar . validateElement ( elementName , context ) ;
226- if ( ! result . isValid ) {
227- const errorMessages = result . errors . map ( err => err . message ) . join ( '; ' ) ;
228- throw new Error ( `Element '${ elementName } ' validation failed: ${ errorMessages } ` ) ;
229- }
230- }
231-
232154 setDTDParser ( dtdParser : DTDParser ) : void {
233155 this . dtdParser = dtdParser ;
234156 }
@@ -250,10 +172,6 @@ export class DOMBuilder implements ContentHandler {
250172 }
251173
252174 startElement ( name : string , atts : XMLAttribute [ ] ) : void {
253- if ( this . stack . length === 0 ) {
254- this . validateRootElement ( name ) ;
255- }
256-
257175 let element : XMLElement = new XMLElement ( name ) ;
258176 atts . forEach ( ( att ) => {
259177 element . setAttribute ( att ) ;
@@ -264,35 +182,8 @@ export class DOMBuilder implements ContentHandler {
264182 this . addDefaultAttributes ( name , element ) ;
265183 }
266184
267- // Validate attributes when validating is enabled
268- if ( this . validating ) {
269- this . validateRequiredAttributes ( name , element . getAttributes ( ) ) ;
270- }
271-
272- // Delegate attribute validation to grammar handler (includes well-formedness and DTD validation)
273- if ( this . validating && this . grammarHandler && this . grammar ) {
274- const attributesMap = new Map < string , string > ( ) ;
275- element . getAttributes ( ) . forEach ( attr => {
276- attributesMap . set ( attr . getName ( ) , attr . getValue ( ) ) ;
277- } ) ;
278-
279- const context = new ValidationContext (
280- [ ] , // childrenNames - empty for attribute validation
281- attributesMap ,
282- '' , // textContent - empty for attribute validation
283- this . stack . length > 1 ? this . stack [ this . stack . length - 2 ] . getName ( ) : undefined ,
284- true // attributeOnly flag
285- ) ;
286-
287- const validationResult = this . grammar . validateAttributes ( name , attributesMap , context ) ;
288-
289- if ( ! validationResult . isValid ) {
290- const errorMessage = validationResult . errors . length > 0 ? validationResult . errors [ 0 ] . message : 'Attribute validation failed' ;
291- throw new Error ( errorMessage ) ;
292- }
293- }
294-
295- this . validateAttributeValues ( name , element . getAttributes ( ) ) ;
185+ // Track ID/IDREF attributes for validation
186+ this . trackIdAttributes ( name , element ) ;
296187
297188 if ( this . stack . length === 0 ) {
298189 this . document ?. setRoot ( element ) ;
@@ -303,13 +194,6 @@ export class DOMBuilder implements ContentHandler {
303194 }
304195
305196 endElement ( name : string ) : void {
306- const element = this . stack [ this . stack . length - 1 ] ;
307-
308- if ( element && this . validating ) {
309- const children = element . getChildren ? element . getChildren ( ) : [ ] ;
310- this . validateElementContent ( name , children ) ;
311- }
312-
313197 this . stack . pop ( ) ;
314198 }
315199
0 commit comments