blob: 3d58cd22803bc5de081d1116754b75a589999f2f [file] [log] [blame]
#include "image_io/base/data_context.h"
#include <cctype>
#include <iomanip>
#include <sstream>
#include "image_io/base/byte_data.h"
namespace photos_editing_formats {
namespace image_io {
namespace {
void AddNames(const std::list<std::string>& name_list, std::stringstream* ss) {
for (const auto& name : name_list) {
*ss << name << ":";
}
}
} // namespace
std::string DataContext::GetInvalidLocationAndRangeErrorText() const {
std::stringstream ss;
ss << "Invalid location:" << location_ << " range:[" << range_.GetBegin()
<< "," << range_.GetEnd() << ") segment_range:["
<< segment_.GetDataRange().GetBegin() << ","
<< segment_.GetDataRange().GetEnd() << ")";
return GetErrorText(ss.str(), "");
}
std::string DataContext::GetErrorText(
const std::string& error_description,
const std::string& expectation_description) const {
std::list<std::string> none;
return GetErrorText(none, none, error_description, expectation_description);
}
std::string DataContext::GetErrorText(
const std::list<std::string>& prefix_name_list,
const std::list<std::string>& postfix_name_list,
const std::string& error_description,
const std::string& expectation_description) const {
const std::string kContinue("- ");
std::stringstream ss;
// Write error description if present.
if (!error_description.empty()) {
ss << error_description << std::endl;
}
// Write name:name:... if present.
std::string names_string =
GetNamesString(prefix_name_list, postfix_name_list);
if (!names_string.empty()) {
ss << kContinue << names_string << std::endl;
}
// Get the line:XX part of the line string.
DataLine data_line;
std::string line_number_string;
if (IsValidLocationAndRange()) {
data_line = line_info_map_.GetDataLine(location_);
line_number_string = GetLineNumberString(data_line);
}
// Get the line_string related ranges and the line string.
DataRange clipped_range, line_range;
size_t spaces_before_caret = line_number_string.length();
GetClippedAndLineRange(data_line, &clipped_range, &line_range);
std::string line_string =
GetLineString(clipped_range, line_range, &spaces_before_caret);
// Write the line string
ss << kContinue << line_number_string << line_string << std::endl;
// Write the caret and expectation description
size_t spaces_count = location_ + spaces_before_caret - line_range.GetBegin();
std::string spaces(spaces_count, ' ');
ss << kContinue << spaces << '^';
if (!expectation_description.empty()) {
ss << "expected:" << expectation_description;
}
return ss.str();
}
std::string DataContext::GetNamesString(
const std::list<std::string>& prefix_name_list,
const std::list<std::string>& postfix_name_list) const {
std::stringstream ss;
if (!prefix_name_list.empty() || !name_list_.empty() ||
!postfix_name_list.empty()) {
AddNames(prefix_name_list, &ss);
AddNames(name_list_, &ss);
AddNames(postfix_name_list, &ss);
}
return ss.str();
}
std::string DataContext::GetLineNumberString(const DataLine& data_line) const {
std::stringstream liness;
liness << "line:";
if (data_line.number == 0) {
liness << "?:";
} else {
liness << data_line.number << ":";
}
return liness.str();
}
void DataContext::GetClippedAndLineRange(const DataLine& data_line,
DataRange* clipped_range,
DataRange* line_range) const {
// Lines could be really long, so provide some sane limits: some kLimit chars
// on either side of the current location.
const size_t kLimit = 25;
size_t line_begin, line_end;
*clipped_range = data_line.range.IsValid()
? range_.GetIntersection(data_line.range)
: range_;
if (clipped_range->IsValid() && clipped_range->Contains(location_)) {
line_begin = (clipped_range->GetBegin() + kLimit < location_)
? location_ - kLimit
: clipped_range->GetBegin();
line_end = std::min(line_begin + 2 * kLimit, clipped_range->GetEnd());
} else {
line_begin = location_;
line_end = std::min(location_ + 2 * kLimit, range_.GetEnd());
*clipped_range = DataRange(line_begin, line_end);
}
*line_range = DataRange(line_begin, line_end);
}
std::string DataContext::GetLineString(const DataRange& clipped_range,
const DataRange& line_range,
size_t* spaces_before_caret) const {
std::stringstream ss;
if (!IsValidLocationAndRange()) {
ss << "Invalid location or range";
return ss.str();
}
const char* cbytes =
reinterpret_cast<const char*>(segment_.GetBuffer(line_range.GetBegin()));
if (cbytes != nullptr) {
if (line_range.GetBegin() != clipped_range.GetBegin()) {
ss << "...";
*spaces_before_caret += 3;
}
for (size_t index = 0; index < line_range.GetLength(); ++index) {
char cbyte = cbytes[index];
if (isprint(cbyte)) {
ss << cbyte;
} else {
ss << "\\x" << ByteData::Byte2Hex(cbyte);
if (index + line_range.GetBegin() < location_) {
*spaces_before_caret += 4;
}
}
}
if (line_range.GetEnd() != clipped_range.GetEnd()) {
ss << "...";
}
}
return ss.str();
}
} // namespace image_io
} // namespace photos_editing_formats