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}