xrust/parser/xml/dtd/attlistdecl.rs
1use crate::item::Node;
2use crate::parser::combinators::alt::{alt2, alt3, alt9};
3use crate::parser::combinators::delimited::delimited;
4use crate::parser::combinators::many::many0;
5use crate::parser::combinators::map::map;
6use crate::parser::combinators::opt::opt;
7use crate::parser::combinators::tag::tag;
8use crate::parser::combinators::take::take_while;
9use crate::parser::combinators::tuple::{tuple2, tuple6};
10use crate::parser::combinators::value::value;
11use crate::parser::combinators::whitespace::{whitespace0, whitespace1};
12use crate::parser::common::{is_ncnamechar, is_ncnamestartchar};
13use crate::parser::xml::chardata::chardata_unicode_codepoint;
14use crate::parser::xml::dtd::enumerated::enumeratedtype;
15use crate::parser::xml::qname::{name, qualname};
16use crate::parser::xml::reference::textreference;
17use crate::parser::{ParseError, ParseInput};
18use crate::qname::QualifiedName;
19use crate::xmldecl::{AttType, DefaultDecl};
20use std::collections::HashMap;
21
22//AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
23pub(crate) fn attlistdecl<N: Node>()
24-> impl Fn(ParseInput<N>) -> Result<(ParseInput<N>, ()), ParseError> {
25 move |(input, state)| match tuple6(
26 tag("<!ATTLIST"),
27 whitespace1(),
28 qualname(),
29 many0(attdef()),
30 whitespace0(),
31 tag(">"),
32 )((input, state))
33 {
34 Ok(((input2, mut state2), (_, _, n, ats, _, _))) => {
35 /*
36
37 "3.3 Attribute-List Declarations
38 When more than one AttlistDecl is provided for a given element type, the contents of all those provided are merged.
39 When more than one definition is provided for the same attribute of a given element type, the first declaration
40 is binding and later declarations are ignored."
41
42 So we're going to do some checks for existing ATTLIST declarations, but each one has a boolean flag to confirm
43 if it was created by an external or internal DTD. If its external we can happily overwrite, but not for internal.
44
45 */
46 /* Entities should always bind to the first value */
47 let replaceable = state2.currentlyexternal;
48
49 let mut atts = HashMap::new();
50
51 let mut count_id_attrs = 0;
52 for (qn, att, dfd) in ats {
53 match &dfd {
54 //We need to make sure that the default is valid, even if its not used.
55 DefaultDecl::FIXED(s) | DefaultDecl::Default(s) => {
56 match att {
57 AttType::ID => {
58 count_id_attrs += 1;
59 let mut ch = s.chars();
60 match ch.next() {
61 None => {}
62 Some(c) => {
63 if is_ncnamestartchar(&c) {
64 for cha in ch {
65 if !is_ncnamechar(&cha) {
66 return Err(ParseError::NotWellFormed(
67 String::from(
68 "DTD Attvalue default is invalid",
69 ),
70 ));
71 }
72 }
73 } else {
74 return Err(ParseError::NotWellFormed(String::from(
75 "DTD Attvalue default is invalid",
76 )));
77 }
78 }
79 }
80 }
81 AttType::IDREF => {
82 let mut ch = s.chars();
83 match ch.next() {
84 None => {}
85 Some(c) => {
86 if is_ncnamestartchar(&c) {
87 for cha in ch {
88 if !is_ncnamechar(&cha) {
89 return Err(ParseError::NotWellFormed(
90 String::from(
91 "DTD Attvalue default is invalid",
92 ),
93 ));
94 }
95 }
96 } else {
97 return Err(ParseError::NotWellFormed(String::from(
98 "DTD Attvalue default is invalid",
99 )));
100 }
101 }
102 }
103 }
104 AttType::IDREFS => {
105 let names = s.split(' ');
106 for name in names {
107 let mut ch = name.chars();
108 match ch.next() {
109 None => {}
110 Some(c) => {
111 if is_ncnamestartchar(&c) {
112 for cha in ch {
113 if !is_ncnamechar(&cha) {
114 return Err(ParseError::NotWellFormed(
115 String::from(
116 "DTD Attvalue default is invalid",
117 ),
118 ));
119 }
120 }
121 } else {
122 return Err(ParseError::NotWellFormed(
123 String::from("DTD Attvalue default is invalid"),
124 ));
125 }
126 }
127 }
128 }
129 }
130 AttType::ENTITY => {
131 let mut ch = s.chars();
132 match ch.next() {
133 None => {}
134 Some(c) => {
135 if is_ncnamestartchar(&c) {
136 for cha in ch {
137 if !is_ncnamechar(&cha) {
138 return Err(ParseError::NotWellFormed(
139 String::from(
140 "DTD Attvalue default is invalid",
141 ),
142 ));
143 }
144 }
145 } else {
146 return Err(ParseError::NotWellFormed(String::from(
147 "DTD Attvalue default is invalid",
148 )));
149 }
150 }
151 }
152 }
153 AttType::ENTITIES => {
154 let entities = s.split(' ');
155 for entity in entities {
156 let mut ch = entity.chars();
157 match ch.next() {
158 None => {}
159 Some(c) => {
160 if is_ncnamestartchar(&c) {
161 for cha in ch {
162 if !is_ncnamechar(&cha) {
163 return Err(ParseError::NotWellFormed(
164 String::from(
165 "DTD Attvalue default is invalid",
166 ),
167 ));
168 }
169 }
170 } else {
171 return Err(ParseError::NotWellFormed(
172 String::from("DTD Attvalue default is invalid"),
173 ));
174 }
175 }
176 }
177 }
178 }
179 _ => { /*TODO complete the rest of these */ }
180 }
181 }
182 //else do nothing
183 _ => {}
184 }
185 //xml:id datatype checking
186 if qn == QualifiedName::new(None, Some("xml".to_string()), "id".to_string())
187 && att != AttType::ID
188 {
189 return Err(ParseError::IDError(
190 "xml:id declaration in the DTD does not have type ID".to_string(),
191 ));
192 }
193 atts.insert(qn, (att, dfd, replaceable));
194 }
195 if count_id_attrs > 1 {
196 return Err(ParseError::NotWellFormed(String::from(
197 "Duplicate ID attribute declarations",
198 )));
199 }
200
201 if !atts.is_empty() {
202 match state2.dtd.attlists.get(&n) {
203 None => {
204 state2.dtd.attlists.insert(n, atts);
205 }
206 Some(al) => {
207 let mut newal = al.clone();
208 for (attname, (atttype, defaultdecl, is_editable)) in atts.iter() {
209 match newal.get(attname) {
210 None => {
211 newal.insert(
212 attname.clone(),
213 (atttype.clone(), defaultdecl.clone(), *is_editable),
214 );
215 }
216 Some((_, _, existing_is_editable)) => {
217 if *existing_is_editable {
218 newal.insert(
219 attname.clone(),
220 (atttype.clone(), defaultdecl.clone(), *is_editable),
221 );
222 }
223 }
224 }
225 }
226 state2.dtd.attlists.insert(n, newal);
227 }
228 }
229 }
230
231 Ok(((input2, state2), ()))
232 }
233 Err(err) => Err(err),
234 }
235}
236
237//AttDef ::= S Name S AttType S DefaultDecl
238fn attdef<N: Node>()
239-> impl Fn(ParseInput<N>) -> Result<(ParseInput<N>, (QualifiedName, AttType, DefaultDecl)), ParseError>
240{
241 map(
242 tuple6(
243 whitespace1(),
244 name(),
245 whitespace1(),
246 atttype(),
247 whitespace1(),
248 defaultdecl(),
249 ),
250 |(_, an, _, at, _, dd)| {
251 let qn = if an.contains(':') {
252 let mut attnamesplit = an.split(':');
253 let prefix = Some(attnamesplit.next().unwrap().to_string());
254 let local = attnamesplit.collect::<String>();
255 QualifiedName::new(None, prefix, local)
256 } else {
257 QualifiedName::new(None, None, an)
258 };
259 (qn, at, dd)
260 },
261 )
262}
263
264//AttType ::= StringType | TokenizedType | EnumeratedType
265fn atttype<N: Node>() -> impl Fn(ParseInput<N>) -> Result<(ParseInput<N>, AttType), ParseError> {
266 alt9(
267 //map(petextreference(), |_| {}), //TODO
268 value(tag("CDATA"), AttType::CDATA), //Stringtype
269 //tokenizedtype
270 value(tag("IDREFS"), AttType::IDREFS),
271 value(tag("IDREF"), AttType::IDREF),
272 value(tag("ID"), AttType::ID),
273 value(tag("ENTITY"), AttType::ENTITY),
274 value(tag("ENTITIES"), AttType::ENTITIES),
275 value(tag("NMTOKENS"), AttType::NMTOKENS),
276 value(tag("NMTOKEN"), AttType::NMTOKEN),
277 enumeratedtype(),
278 )
279}
280
281//DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
282fn defaultdecl<N: Node>()
283-> impl Fn(ParseInput<N>) -> Result<(ParseInput<N>, DefaultDecl), ParseError> {
284 alt3(
285 value(tag("#REQUIRED"), DefaultDecl::Required),
286 value(tag("#IMPLIED"), DefaultDecl::Implied),
287 map(
288 tuple2(
289 opt(tuple2(
290 value(tag("#FIXED"), "#FIXED".to_string()),
291 whitespace1(),
292 )),
293 attvalue(),
294 ),
295 |(x, y)| match x {
296 None => DefaultDecl::Default(y),
297 Some(_) => DefaultDecl::FIXED(y),
298 },
299 ),
300 )
301}
302
303//AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'"
304fn attvalue<N: Node>() -> impl Fn(ParseInput<N>) -> Result<(ParseInput<N>, String), ParseError> {
305 alt2(
306 delimited(
307 tag("\'"),
308 map(
309 many0(alt3(
310 map(chardata_unicode_codepoint(), |c| c.to_string()),
311 take_while(|c| !"&\'<".contains(c)),
312 textreference(),
313 )),
314 |v| v.join(""),
315 ),
316 tag("\'"),
317 ),
318 delimited(
319 tag("\""),
320 map(
321 many0(alt3(
322 map(chardata_unicode_codepoint(), |c| c.to_string()),
323 take_while(|c| !"&\"<".contains(c)),
324 textreference(),
325 )),
326 |v| v.join(""),
327 ),
328 tag("\""),
329 ),
330 )
331}