//! Contains an implementation of pull-based XML parser.
use std::mem; use std::borrow::Cow; use std::io::prelude::*;
use common::{ self,
XmlVersion, Position, TextPosition,
is_name_start_char, is_name_char,
}; use name::OwnedName; use attribute::OwnedAttribute; use namespace::NamespaceStack;
use reader::events::XmlEvent; use reader::config::ParserConfig; use reader::lexer::{Lexer, Token};
mod outside_tag; mod inside_processing_instruction; mod inside_declaration; mod inside_doctype; mod inside_opening_tag; mod inside_closing_tag_name; mod inside_comment; mod inside_cdata; mod inside_reference;
/// Checks if this parser ignores the end of stream errors. pubfn is_ignoring_end_of_stream(&self) -> bool { self.config.ignore_end_of_stream }
}
impl Position for PullParser { /// Returns the position of the last event produced by the parser #[inline] fn position(&self) -> TextPosition { self.pos[0]
}
}
struct MarkupData {
name: String, // used for processing instruction name
ref_data: String, // used for reference content
version: Option<common::XmlVersion>, // used for XML declaration version
encoding: Option<String>, // used for XML declaration encoding
standalone: Option<bool>, // used for XML declaration standalone parameter
element_name: Option<OwnedName>, // used for element name
quote: Option<QuoteToken>, // used to hold opening quote for attribute value
attr_name: Option<OwnedName>, // used to hold attribute name
attributes: Vec<OwnedAttribute> // used to hold all accumulated attributes
}
impl PullParser { /// Returns next event read from the given buffer. /// /// This method should be always called with the same buffer. If you call it /// providing different buffers each time, the result will be undefined. pubfn next<R: Read>(&mutself, r: &mut R) -> Result { iflet Some(ref ev) = self.final_result { return ev.clone();
}
loop { // While lexer gives us Ok(maybe_token) -- we loop. // Upon having a complete XML-event -- we return from the whole function. matchself.lexer.next_token(r) {
Ok(maybe_token) => match maybe_token {
None => break,
Some(token) => matchself.dispatch_token(token) {
None => {} // continue
Some(Ok(XmlEvent::EndDocument)) => return { self.next_pos(); self.set_final_result(Ok(XmlEvent::EndDocument))
},
Some(Ok(xml_event)) => return { self.next_pos();
Ok(xml_event)
},
Some(Err(xml_error)) => return { self.next_pos(); self.set_final_result(Err(xml_error))
},
}
},
Err(lexer_error) => returnself.set_final_result(Err(lexer_error)),
}
}
// Handle end of stream // Forward pos to the lexer head self.next_pos(); let ev = ifself.depth() == 0 { ifself.encountered_element && self.st == State::OutsideTag { // all is ok
Ok(XmlEvent::EndDocument)
} elseif !self.encountered_element {
self_error!(self; "Unexpected end of stream: no root element found")
} else { // self.st != State::OutsideTag
self_error!(self; "Unexpected end of stream") // TODO: add expected hint?
}
} else { ifself.config.ignore_end_of_stream { self.final_result = None; self.lexer.reset_eof_handled(); return self_error!(self; "Unexpected end of stream: still inside the root element");
} else {
self_error!(self; "Unexpected end of stream: still inside the root element")
}
}; self.set_final_result(ev)
}
// This function is to be called when a terminal event is reached. // The function sets up the `self.final_result` into `Some(result)` and return `result`. fn set_final_result(&mutself, result: Result) -> Result { self.final_result = Some(result.clone());
result
}
#[inline] fn error<M: Into<Cow<'static, str>>>(&self, msg: M) -> Result {
Err((&self.lexer, msg).into())
}
/// Dispatches tokens in order to process qualified name. If qualified name cannot be parsed, /// an error is returned. /// /// # Parameters /// * `t` --- next token; /// * `on_name` --- a callback which is executed when whitespace is encountered. fn read_qualified_name<F>(&mutself, t: Token, target: QualifiedNameTarget, on_name: F) -> Option<Result> where F: Fn(&mut PullParser, Token, OwnedName) -> Option<Result> { // We can get here for the first time only when self.data.name contains zero or one character, // but first character cannot be a colon anyway ifself.buf.len() <= 1 { self.read_prefix_separator = false;
}
let invoke_callback = |this: &mut PullParser, t| { let name = this.take_buf(); match name.parse() {
Ok(name) => on_name(this, t, name),
Err(_) => Some(self_error!(this; "Qualified name is invalid: {}", name))
}
};
match t { // There can be only one colon, and not as the first character
Token::Character(':') ifself.buf_has_data() && !self.read_prefix_separator => { self.buf.push(':'); self.read_prefix_separator = true;
None
}
Token::Character(c) if c != ':' && (!self.buf_has_data() && is_name_start_char(c) || self.buf_has_data() && is_name_char(c)) => self.append_char_continue(c),
Token::EqualsSign if target == QualifiedNameTarget::AttributeNameTarget => invoke_callback(self, t),
Token::EmptyTagEnd if target == QualifiedNameTarget::OpeningTagNameTarget => invoke_callback(self, t),
/// Dispatches tokens in order to process attribute value. /// /// # Parameters /// * `t` --- next token; /// * `on_value` --- a callback which is called when terminating quote is encountered. fn read_attribute_value<F>(&mutself, t: Token, on_value: F) -> Option<Result> where F: Fn(&mut PullParser, String) -> Option<Result> { match t {
Token::Whitespace(_) ifself.data.quote.is_none() => None, // skip leading whitespace
Token::DoubleQuote | Token::SingleQuote => matchself.data.quote {
None => { // Entered attribute value self.data.quote = Some(QuoteToken::from_token(&t));
None
}
Some(q) if q.as_token() == t => { self.data.quote = None; let value = self.take_buf();
on_value(self, value)
}
_ => {
t.push_to_string(&mutself.buf);
None
}
},
Token::ReferenceStart => { let st = Box::new(self.st.clone()); self.into_state_continue(State::InsideReference(st))
}
fn emit_end_element(&mutself) -> Option<Result> { letmut name = self.data.take_element_name().unwrap();
// check whether the name prefix is bound and fix its namespace matchself.nst.get(name.borrow().prefix_repr()) {
Some("") => name.namespace = None, // default namespace
Some(ns) => name.namespace = Some(ns.into()),
None => return Some(self_error!(self; "Element {} prefix is unbound", name))
}
let op_name = self.est.pop().unwrap();
if name == op_name { self.pop_namespace = true; self.into_state_emit(State::OutsideTag, Ok(XmlEvent::EndElement { name: name }))
} else {
Some(self_error!(self; "Unexpected closing tag: {}, expected {}", name, op_name))
}
}
}
#[cfg(test)] mod tests { use std::io::BufReader;
use common::{Position, TextPosition}; use name::OwnedName; use attribute::OwnedAttribute; use reader::parser::PullParser; use reader::ParserConfig; use reader::events::XmlEvent;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.