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