v / vlib / encoding / xml / validation.v
96 lines · 86 sloc · 2.63 KB · c51d30bf5309653c6b573ec815268e69a78ea8cc
Raw
1module xml
2
3fn (node XMLNode) validate(elements map[string]DTDElement, entities map[string]string) !XMLNode {
4 mut children := []XMLNodeContents{cap: node.children.len}
5
6 valid_elements := elements[node.name].definition
7 mut validate_node_children := node.name in elements
8
9 // Check if the node will match everything
10 if valid_elements.len == 1 && valid_elements[0] == '#PCDATA' {
11 validate_node_children = false
12 }
13
14 for child in node.children {
15 match child {
16 XMLNode {
17 if validate_node_children {
18 name := child.name
19 if name !in valid_elements {
20 return error('Invalid child element ${name} for ${node.name}')
21 }
22 }
23 children << child.validate(elements, entities)!
24 }
25 string {
26 children << unescape_text(child, entities: entities)!
27 }
28 else {
29 // Ignore other nodes
30 children << child
31 }
32 }
33 }
34
35 return XMLNode{
36 name: node.name
37 attributes: node.attributes
38 children: children
39 }
40}
41
42// validate checks the document is well-formed and valid. It returns a new
43// document with the parsed entities expanded when validation is successful.
44// Otherwise it returns an error.
45pub fn (doc XMLDocument) validate() !XMLDocument {
46 // The document is well-formed because we were able to parse it properly.
47 match doc.doctype.dtd {
48 DocumentTypeDefinition {
49 // Store the element and entity definitions
50 mut elements := map[string]DTDElement{}
51 mut entities := default_entities.clone()
52 mut reverse_entities := default_entities_reverse.clone()
53
54 for item in doc.doctype.dtd.list {
55 match item {
56 DTDElement {
57 name := item.name
58 if name in elements {
59 return error('Duplicate element definition for ${name}')
60 }
61 elements[name] = item
62 }
63 DTDEntity {
64 name := item.name
65 if name in entities {
66 return error('Duplicate entity definition for ${name}')
67 }
68 entities[name] = item.value
69 reverse_entities[item.value] = name
70 }
71 }
72 }
73
74 // Now validate the document against the elements and entities.
75 new_root := doc.root.validate(elements, entities)!
76
77 // Check the DOCTYPE name matches the root name
78 if doc.doctype.name != '' && doc.doctype.name != new_root.name {
79 return error('Root element ${new_root.name} does not match DOCTYPE ${doc.doctype.name}')
80 }
81
82 return XMLDocument{
83 version: doc.version
84 encoding: doc.encoding
85 doctype: doc.doctype
86 comments: doc.comments
87 root: new_root
88 parsed_reverse_entities: reverse_entities
89 }
90 }
91 string {
92 // TODO: Validate the document against the DTD string.
93 return doc
94 }
95 }
96}
97