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