blob: 467cb82463da5b3ee02a182a8fc77ae3b07c1479 [file] [log] [blame]
#include "image_io/xml/xml_reader.h"
#include <iomanip>
#include <sstream>
#include <string>
#include <utility>
#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<XmlRule> 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<const Byte*>(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