1 #include "image_io/base/data_context.h"
2
3 #include <cctype>
4 #include <iomanip>
5 #include <sstream>
6
7 #include "image_io/base/byte_data.h"
8
9 namespace photos_editing_formats {
10 namespace image_io {
11
12 namespace {
13
AddNames(const std::list<std::string> & name_list,std::stringstream * ss)14 void AddNames(const std::list<std::string>& name_list, std::stringstream* ss) {
15 for (const auto& name : name_list) {
16 *ss << name << ":";
17 }
18 }
19
20 } // namespace
21
GetInvalidLocationAndRangeErrorText() const22 std::string DataContext::GetInvalidLocationAndRangeErrorText() const {
23 std::stringstream ss;
24 ss << "Invalid location:" << location_ << " range:[" << range_.GetBegin()
25 << "," << range_.GetEnd() << ") segment_range:["
26 << segment_.GetDataRange().GetBegin() << ","
27 << segment_.GetDataRange().GetEnd() << ")";
28 return GetErrorText(ss.str(), "");
29 }
30
GetErrorText(const std::string & error_description,const std::string & expectation_description) const31 std::string DataContext::GetErrorText(
32 const std::string& error_description,
33 const std::string& expectation_description) const {
34 std::list<std::string> none;
35 return GetErrorText(none, none, error_description, expectation_description);
36 }
37
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) const38 std::string DataContext::GetErrorText(
39 const std::list<std::string>& prefix_name_list,
40 const std::list<std::string>& postfix_name_list,
41 const std::string& error_description,
42 const std::string& expectation_description) const {
43 const std::string kContinue("- ");
44 std::stringstream ss;
45
46 // Write error description if present.
47 if (!error_description.empty()) {
48 ss << error_description << std::endl;
49 }
50
51 // Write name:name:... if present.
52 std::string names_string =
53 GetNamesString(prefix_name_list, postfix_name_list);
54 if (!names_string.empty()) {
55 ss << kContinue << names_string << std::endl;
56 }
57
58 // Get the line:XX part of the line string.
59 DataLine data_line;
60 std::string line_number_string;
61 if (IsValidLocationAndRange()) {
62 data_line = line_info_map_.GetDataLine(location_);
63 line_number_string = GetLineNumberString(data_line);
64 }
65
66 // Get the line_string related ranges and the line string.
67 DataRange clipped_range, line_range;
68 size_t spaces_before_caret = line_number_string.length();
69 GetClippedAndLineRange(data_line, &clipped_range, &line_range);
70 std::string line_string =
71 GetLineString(clipped_range, line_range, &spaces_before_caret);
72
73 // Write the line string
74 ss << kContinue << line_number_string << line_string << std::endl;
75
76 // Write the caret and expectation description
77 size_t spaces_count = location_ + spaces_before_caret - line_range.GetBegin();
78 std::string spaces(spaces_count, ' ');
79 ss << kContinue << spaces << '^';
80 if (!expectation_description.empty()) {
81 ss << "expected:" << expectation_description;
82 }
83 return ss.str();
84 }
85
GetNamesString(const std::list<std::string> & prefix_name_list,const std::list<std::string> & postfix_name_list) const86 std::string DataContext::GetNamesString(
87 const std::list<std::string>& prefix_name_list,
88 const std::list<std::string>& postfix_name_list) const {
89 std::stringstream ss;
90 if (!prefix_name_list.empty() || !name_list_.empty() ||
91 !postfix_name_list.empty()) {
92 AddNames(prefix_name_list, &ss);
93 AddNames(name_list_, &ss);
94 AddNames(postfix_name_list, &ss);
95 }
96 return ss.str();
97 }
98
GetLineNumberString(const DataLine & data_line) const99 std::string DataContext::GetLineNumberString(const DataLine& data_line) const {
100 std::stringstream liness;
101 liness << "line:";
102 if (data_line.number == 0) {
103 liness << "?:";
104 } else {
105 liness << data_line.number << ":";
106 }
107 return liness.str();
108 }
109
GetClippedAndLineRange(const DataLine & data_line,DataRange * clipped_range,DataRange * line_range) const110 void DataContext::GetClippedAndLineRange(const DataLine& data_line,
111 DataRange* clipped_range,
112 DataRange* line_range) const {
113 // Lines could be really long, so provide some sane limits: some kLimit chars
114 // on either side of the current location.
115 const size_t kLimit = 25;
116 size_t line_begin, line_end;
117 *clipped_range = data_line.range.IsValid()
118 ? range_.GetIntersection(data_line.range)
119 : range_;
120 if (clipped_range->IsValid() && clipped_range->Contains(location_)) {
121 line_begin = (clipped_range->GetBegin() + kLimit < location_)
122 ? location_ - kLimit
123 : clipped_range->GetBegin();
124 line_end = std::min(line_begin + 2 * kLimit, clipped_range->GetEnd());
125 } else {
126 line_begin = location_;
127 line_end = std::min(location_ + 2 * kLimit, range_.GetEnd());
128 *clipped_range = DataRange(line_begin, line_end);
129 }
130 *line_range = DataRange(line_begin, line_end);
131 }
132
GetLineString(const DataRange & clipped_range,const DataRange & line_range,size_t * spaces_before_caret) const133 std::string DataContext::GetLineString(const DataRange& clipped_range,
134 const DataRange& line_range,
135 size_t* spaces_before_caret) const {
136 std::stringstream ss;
137 if (!IsValidLocationAndRange()) {
138 ss << "Invalid location or range";
139 return ss.str();
140 }
141
142 const char* cbytes =
143 reinterpret_cast<const char*>(segment_.GetBuffer(line_range.GetBegin()));
144 if (cbytes != nullptr) {
145 if (line_range.GetBegin() != clipped_range.GetBegin()) {
146 ss << "...";
147 *spaces_before_caret += 3;
148 }
149 for (size_t index = 0; index < line_range.GetLength(); ++index) {
150 char cbyte = cbytes[index];
151 if (isprint(cbyte)) {
152 ss << cbyte;
153 } else {
154 ss << "\\x" << ByteData::Byte2Hex(cbyte);
155 if (index + line_range.GetBegin() < location_) {
156 *spaces_before_caret += 4;
157 }
158 }
159 }
160 if (line_range.GetEnd() != clipped_range.GetEnd()) {
161 ss << "...";
162 }
163 }
164 return ss.str();
165 }
166
167 } // namespace image_io
168 } // namespace photos_editing_formats
169