#include "image_io/xml/xml_reader.h" #include #include #include #include #include "image_io/base/message.h" #include "image_io/base/message_handler.h" namespace photos_editing_formats { namespace image_io { namespace { /// The reader name used for error messages. const char kReaderName[] = "XmlReader"; } // namespace bool XmlReader::StartParse(std::unique_ptr rule) { bytes_parsed_ = 0; rule_stack_.clear(); if (!rule) { std::string text = std::string(kReaderName) + ":StartParse:NoTopLevelRule"; Message message(Message::kInternalError, 0, text); ReportError(message); return false; } rule_stack_.push_back(std::move(rule)); has_internal_or_syntax_error_ = false; has_errors_ = false; return true; } bool XmlReader::FinishParse() { if (has_internal_or_syntax_error_) { return false; } std::string error_text; if (rule_stack_.empty() || (rule_stack_.size() == 1 && rule_stack_.back()->IsPermissibleToFinish(&error_text))) { return true; } std::stringstream ss; ss << kReaderName << ":"; if (error_text.empty()) { ss << "While parsing text with rule:"; ss << rule_stack_.back()->GetName(); XmlTerminal* terminal = rule_stack_.back()->GetCurrentTerminal(); if (terminal) { if (!terminal->GetName().empty()) { ss << ":" << terminal->GetName(); } ss << ":" << terminal->GetScanner()->GetDescription(); } } else { ss << error_text; } Message message(Message::kPrematureEndOfDataError, 0, ss.str()); has_internal_or_syntax_error_ = true; ReportError(message); return false; } bool XmlReader::Parse(const std::string& value) { size_t location = GetBytesParsed(); DataRange range(location, location + value.length()); const Byte* bytes = reinterpret_cast(value.c_str()); auto segment = DataSegment::Create(range, bytes, DataSegment::kDontDelete); return Parse(location, range, *segment); } bool XmlReader::Parse(size_t start_location, const DataRange& range, const DataSegment& segment) { if (has_internal_or_syntax_error_) { return false; } XmlHandlerContext context(start_location, range, segment, *data_line_map_, handler_); InitializeContextNameList(&context); if (!context.IsValidLocationAndRange()) { DataMatchResult result; result.SetMessage(Message::kInternalError, context.GetInvalidLocationAndRangeErrorText()); ReportError(result, context); return false; } if (rule_stack_.empty()) { DataMatchResult result; result.SetMessage(Message::kInternalError, "NoActiveRule"); ReportError(result, context); return false; } if (data_line_map_ == &internal_data_line_map_) { internal_data_line_map_.FindDataLines(range, segment); } size_t bytes_remaining = range.GetEnd() - start_location; while (bytes_remaining > 0 && !rule_stack_.empty() && !has_internal_or_syntax_error_) { auto& rule = rule_stack_.back(); InitializeContextNameList(&context); DataMatchResult result = rule->Parse(context); switch (result.GetType()) { case DataMatchResult::kError: case DataMatchResult::kNone: ReportError(result, context); break; case DataMatchResult::kPartial: ReportMessageIfNeeded(result); bytes_parsed_ += result.GetBytesConsumed(); bytes_remaining -= result.GetBytesConsumed(); context.IncrementLocation(result.GetBytesConsumed()); if (rule->HasNextRule()) { // Delegation by child rule: push the next. rule_stack_.push_back(rule->ReleaseNextRule()); } break; case DataMatchResult::kPartialOutOfData: ReportMessageIfNeeded(result); bytes_parsed_ += result.GetBytesConsumed(); return true; case DataMatchResult::kFull: ReportMessageIfNeeded(result); bytes_parsed_ += result.GetBytesConsumed(); bytes_remaining -= result.GetBytesConsumed(); context.IncrementLocation(result.GetBytesConsumed()); if (rule->HasNextRule()) { // Delegation by chaining: pop the current rule and push the next. auto next_rule = rule->ReleaseNextRule(); rule_stack_.pop_back(); rule_stack_.push_back(std::move(next_rule)); } else { rule_stack_.pop_back(); } break; } } if (bytes_remaining > 0 && rule_stack_.empty()) { InitializeContextNameList(&context); std::string text = context.GetErrorText("NoActiveRule", ""); Message message(Message::kSyntaxError, 0, text); ReportError(message); return false; } return !has_internal_or_syntax_error_; } void XmlReader::InitializeContextNameList(XmlHandlerContext* context) { auto name_list = context->GetNameList(); name_list.clear(); name_list.push_back(kReaderName); if (!rule_stack_.empty()) { name_list.push_back(rule_stack_.back()->GetName()); } } void XmlReader::ReportMessageIfNeeded(const DataMatchResult& result) { if (result.HasMessage()) { ReportError(result.GetMessage()); } } void XmlReader::ReportError(const DataMatchResult& result, const DataContext& context) { if (!result.HasMessage()) { Message message(Message::kInternalError, 0, context.GetErrorText("Rule had error but no message", "")); ReportError(message); } ReportError(result.GetMessage()); } void XmlReader::ReportError(const Message& message) { if (message_handler_) { message_handler_->ReportMessage(message); } if (message.GetType() == Message::kInternalError || message.GetType() == Message::kSyntaxError) { has_internal_or_syntax_error_ = true; } if (message.IsError()) { has_errors_ = true; } } } // namespace image_io } // namespace photos_editing_formats