1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 // Author: kenton@google.com (Kenton Varda) 32 // Based on original Protocol Buffers design by 33 // Sanjay Ghemawat, Jeff Dean, and others. 34 // 35 // Utility class for writing text to a ZeroCopyOutputStream. 36 37 #ifndef GOOGLE_PROTOBUF_IO_PRINTER_H__ 38 #define GOOGLE_PROTOBUF_IO_PRINTER_H__ 39 40 41 #include <map> 42 #include <string> 43 #include <vector> 44 45 #include <google/protobuf/stubs/common.h> 46 #include <google/protobuf/port_def.inc> 47 48 namespace google { 49 namespace protobuf { 50 namespace io { 51 52 class ZeroCopyOutputStream; // zero_copy_stream.h 53 54 // Records annotations about a Printer's output. 55 class PROTOBUF_EXPORT AnnotationCollector { 56 public: 57 // Annotation is a offset range and a payload pair. 58 typedef std::pair<std::pair<size_t, size_t>, std::string> Annotation; 59 60 // Records that the bytes in file_path beginning with begin_offset and ending 61 // before end_offset are associated with the SourceCodeInfo-style path. 62 virtual void AddAnnotation(size_t begin_offset, size_t end_offset, 63 const std::string& file_path, 64 const std::vector<int>& path) = 0; 65 66 // TODO(gerbens) I don't see why we need virtuals here. Just a vector of 67 // range, payload pairs stored in a context should suffice. AddAnnotationNew(Annotation & a)68 virtual void AddAnnotationNew(Annotation& a) {} 69 ~AnnotationCollector()70 virtual ~AnnotationCollector() {} 71 }; 72 73 // Records annotations about a Printer's output to the given protocol buffer, 74 // assuming that the buffer has an ::Annotation message exposing path, 75 // source_file, begin and end fields. 76 template <typename AnnotationProto> 77 class AnnotationProtoCollector : public AnnotationCollector { 78 public: 79 // annotation_proto is the protocol buffer to which new Annotations should be 80 // added. It is not owned by the AnnotationProtoCollector. AnnotationProtoCollector(AnnotationProto * annotation_proto)81 explicit AnnotationProtoCollector(AnnotationProto* annotation_proto) 82 : annotation_proto_(annotation_proto) {} 83 84 // Override for AnnotationCollector::AddAnnotation. AddAnnotation(size_t begin_offset,size_t end_offset,const std::string & file_path,const std::vector<int> & path)85 virtual void AddAnnotation(size_t begin_offset, size_t end_offset, 86 const std::string& file_path, 87 const std::vector<int>& path) { 88 typename AnnotationProto::Annotation* annotation = 89 annotation_proto_->add_annotation(); 90 for (int i = 0; i < path.size(); ++i) { 91 annotation->add_path(path[i]); 92 } 93 annotation->set_source_file(file_path); 94 annotation->set_begin(begin_offset); 95 annotation->set_end(end_offset); 96 } 97 // Override for AnnotationCollector::AddAnnotation. AddAnnotationNew(Annotation & a)98 virtual void AddAnnotationNew(Annotation& a) { 99 auto* annotation = annotation_proto_->add_annotation(); 100 annotation->ParseFromString(a.second); 101 annotation->set_begin(a.first.first); 102 annotation->set_end(a.first.second); 103 } 104 105 private: 106 // The protocol buffer to which new annotations should be added. 107 AnnotationProto* const annotation_proto_; 108 }; 109 110 // This simple utility class assists in code generation. It basically 111 // allows the caller to define a set of variables and then output some 112 // text with variable substitutions. Example usage: 113 // 114 // Printer printer(output, '$'); 115 // map<string, string> vars; 116 // vars["name"] = "Bob"; 117 // printer.Print(vars, "My name is $name$."); 118 // 119 // The above writes "My name is Bob." to the output stream. 120 // 121 // Printer aggressively enforces correct usage, crashing (with assert failures) 122 // in the case of undefined variables in debug builds. This helps greatly in 123 // debugging code which uses it. 124 // 125 // If a Printer is constructed with an AnnotationCollector, it will provide it 126 // with annotations that connect the Printer's output to paths that can identify 127 // various descriptors. In the above example, if person_ is a descriptor that 128 // identifies Bob, we can associate the output string "My name is Bob." with 129 // a source path pointing to that descriptor with: 130 // 131 // printer.Annotate("name", person_); 132 // 133 // The AnnotationCollector will be sent an annotation linking the output range 134 // covering "Bob" to the logical path provided by person_. Tools may use 135 // this association to (for example) link "Bob" in the output back to the 136 // source file that defined the person_ descriptor identifying Bob. 137 // 138 // Annotate can only examine variables substituted during the last call to 139 // Print. It is invalid to refer to a variable that was used multiple times 140 // in a single Print call. 141 // 142 // In full generality, one may specify a range of output text using a beginning 143 // substitution variable and an ending variable. The resulting annotation will 144 // span from the first character of the substituted value for the beginning 145 // variable to the last character of the substituted value for the ending 146 // variable. For example, the Annotate call above is equivalent to this one: 147 // 148 // printer.Annotate("name", "name", person_); 149 // 150 // This is useful if multiple variables combine to form a single span of output 151 // that should be annotated with the same source path. For example: 152 // 153 // Printer printer(output, '$'); 154 // map<string, string> vars; 155 // vars["first"] = "Alice"; 156 // vars["last"] = "Smith"; 157 // printer.Print(vars, "My name is $first$ $last$."); 158 // printer.Annotate("first", "last", person_); 159 // 160 // This code would associate the span covering "Alice Smith" in the output with 161 // the person_ descriptor. 162 // 163 // Note that the beginning variable must come before (or overlap with, in the 164 // case of zero-sized substitution values) the ending variable. 165 // 166 // It is also sometimes useful to use variables with zero-sized values as 167 // markers. This avoids issues with multiple references to the same variable 168 // and also allows annotation ranges to span literal text from the Print 169 // templates: 170 // 171 // Printer printer(output, '$'); 172 // map<string, string> vars; 173 // vars["foo"] = "bar"; 174 // vars["function"] = "call"; 175 // vars["mark"] = ""; 176 // printer.Print(vars, "$function$($foo$,$foo$)$mark$"); 177 // printer.Annotate("function", "mark", call_); 178 // 179 // This code associates the span covering "call(bar,bar)" in the output with the 180 // call_ descriptor. 181 182 class PROTOBUF_EXPORT Printer { 183 public: 184 // Create a printer that writes text to the given output stream. Use the 185 // given character as the delimiter for variables. 186 Printer(ZeroCopyOutputStream* output, char variable_delimiter); 187 188 // Create a printer that writes text to the given output stream. Use the 189 // given character as the delimiter for variables. If annotation_collector 190 // is not null, Printer will provide it with annotations about code written 191 // to the stream. annotation_collector is not owned by Printer. 192 Printer(ZeroCopyOutputStream* output, char variable_delimiter, 193 AnnotationCollector* annotation_collector); 194 195 ~Printer(); 196 197 // Link a substitution variable emitted by the last call to Print to the 198 // object described by descriptor. 199 template <typename SomeDescriptor> Annotate(const char * varname,const SomeDescriptor * descriptor)200 void Annotate(const char* varname, const SomeDescriptor* descriptor) { 201 Annotate(varname, varname, descriptor); 202 } 203 204 // Link the output range defined by the substitution variables as emitted by 205 // the last call to Print to the object described by descriptor. The range 206 // begins at begin_varname's value and ends after the last character of the 207 // value substituted for end_varname. 208 template <typename SomeDescriptor> Annotate(const char * begin_varname,const char * end_varname,const SomeDescriptor * descriptor)209 void Annotate(const char* begin_varname, const char* end_varname, 210 const SomeDescriptor* descriptor) { 211 if (annotation_collector_ == NULL) { 212 // Annotations aren't turned on for this Printer, so don't pay the cost 213 // of building the location path. 214 return; 215 } 216 std::vector<int> path; 217 descriptor->GetLocationPath(&path); 218 Annotate(begin_varname, end_varname, descriptor->file()->name(), path); 219 } 220 221 // Link a substitution variable emitted by the last call to Print to the file 222 // with path file_name. Annotate(const char * varname,const std::string & file_name)223 void Annotate(const char* varname, const std::string& file_name) { 224 Annotate(varname, varname, file_name); 225 } 226 227 // Link the output range defined by the substitution variables as emitted by 228 // the last call to Print to the file with path file_name. The range begins 229 // at begin_varname's value and ends after the last character of the value 230 // substituted for end_varname. Annotate(const char * begin_varname,const char * end_varname,const std::string & file_name)231 void Annotate(const char* begin_varname, const char* end_varname, 232 const std::string& file_name) { 233 if (annotation_collector_ == NULL) { 234 // Annotations aren't turned on for this Printer. 235 return; 236 } 237 std::vector<int> empty_path; 238 Annotate(begin_varname, end_varname, file_name, empty_path); 239 } 240 241 // Print some text after applying variable substitutions. If a particular 242 // variable in the text is not defined, this will crash. Variables to be 243 // substituted are identified by their names surrounded by delimiter 244 // characters (as given to the constructor). The variable bindings are 245 // defined by the given map. 246 void Print(const std::map<std::string, std::string>& variables, 247 const char* text); 248 249 // Like the first Print(), except the substitutions are given as parameters. 250 template <typename... Args> Print(const char * text,const Args &...args)251 void Print(const char* text, const Args&... args) { 252 std::map<std::string, std::string> vars; 253 PrintInternal(&vars, text, args...); 254 } 255 256 // Indent text by two spaces. After calling Indent(), two spaces will be 257 // inserted at the beginning of each line of text. Indent() may be called 258 // multiple times to produce deeper indents. 259 void Indent(); 260 261 // Reduces the current indent level by two spaces, or crashes if the indent 262 // level is zero. 263 void Outdent(); 264 265 // Write a string to the output buffer. 266 // This method does not look for newlines to add indentation. 267 void PrintRaw(const std::string& data); 268 269 // Write a zero-delimited string to output buffer. 270 // This method does not look for newlines to add indentation. 271 void PrintRaw(const char* data); 272 273 // Write some bytes to the output buffer. 274 // This method does not look for newlines to add indentation. 275 void WriteRaw(const char* data, int size); 276 277 // FormatInternal is a helper function not meant to use directly, use 278 // compiler::cpp::Formatter instead. This function is meant to support 279 // formatting text using named variables (eq. "$foo$) from a lookup map (vars) 280 // and variables directly supplied by arguments (eq "$1$" meaning first 281 // argument which is the zero index element of args). 282 void FormatInternal(const std::vector<std::string>& args, 283 const std::map<std::string, std::string>& vars, 284 const char* format); 285 286 // True if any write to the underlying stream failed. (We don't just 287 // crash in this case because this is an I/O failure, not a programming 288 // error.) failed()289 bool failed() const { return failed_; } 290 291 private: 292 // Link the output range defined by the substitution variables as emitted by 293 // the last call to Print to the object found at the SourceCodeInfo-style path 294 // in a file with path file_path. The range begins at the start of 295 // begin_varname's value and ends after the last character of the value 296 // substituted for end_varname. Note that begin_varname and end_varname 297 // may refer to the same variable. 298 void Annotate(const char* begin_varname, const char* end_varname, 299 const std::string& file_path, const std::vector<int>& path); 300 301 // Base case PrintInternal(std::map<std::string,std::string> * vars,const char * text)302 void PrintInternal(std::map<std::string, std::string>* vars, 303 const char* text) { 304 Print(*vars, text); 305 } 306 307 template <typename... Args> PrintInternal(std::map<std::string,std::string> * vars,const char * text,const char * key,const std::string & value,const Args &...args)308 void PrintInternal(std::map<std::string, std::string>* vars, const char* text, 309 const char* key, const std::string& value, 310 const Args&... args) { 311 (*vars)[key] = value; 312 PrintInternal(vars, text, args...); 313 } 314 315 // Copy size worth of bytes from data to buffer_. 316 void CopyToBuffer(const char* data, int size); 317 push_back(char c)318 void push_back(char c) { 319 if (failed_) return; 320 if (buffer_size_ == 0) { 321 if (!Next()) return; 322 } 323 *buffer_++ = c; 324 buffer_size_--; 325 offset_++; 326 } 327 328 bool Next(); 329 330 inline void IndentIfAtStart(); 331 const char* WriteVariable( 332 const std::vector<std::string>& args, 333 const std::map<std::string, std::string>& vars, const char* format, 334 int* arg_index, 335 std::vector<AnnotationCollector::Annotation>* annotations); 336 337 const char variable_delimiter_; 338 339 ZeroCopyOutputStream* const output_; 340 char* buffer_; 341 int buffer_size_; 342 // The current position, in bytes, in the output stream. This is equivalent 343 // to the total number of bytes that have been written so far. This value is 344 // used to calculate annotation ranges in the substitutions_ map below. 345 size_t offset_; 346 347 std::string indent_; 348 bool at_start_of_line_; 349 bool failed_; 350 351 // A map from variable name to [start, end) offsets in the output buffer. 352 // These refer to the offsets used for a variable after the last call to 353 // Print. If a variable was used more than once, the entry used in 354 // this map is set to a negative-length span. For singly-used variables, the 355 // start offset is the beginning of the substitution; the end offset is the 356 // last byte of the substitution plus one (such that (end - start) is the 357 // length of the substituted string). 358 std::map<std::string, std::pair<size_t, size_t> > substitutions_; 359 360 // Keeps track of the keys in substitutions_ that need to be updated when 361 // indents are inserted. These are keys that refer to the beginning of the 362 // current line. 363 std::vector<std::string> line_start_variables_; 364 365 // Returns true and sets range to the substitution range in the output for 366 // varname if varname was used once in the last call to Print. If varname 367 // was not used, or if it was used multiple times, returns false (and 368 // fails a debug assertion). 369 bool GetSubstitutionRange(const char* varname, 370 std::pair<size_t, size_t>* range); 371 372 // If non-null, annotation_collector_ is used to store annotations about 373 // generated code. 374 AnnotationCollector* const annotation_collector_; 375 376 GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Printer); 377 }; 378 379 } // namespace io 380 } // namespace protobuf 381 } // namespace google 382 383 #include <google/protobuf/port_undef.inc> 384 385 #endif // GOOGLE_PROTOBUF_IO_PRINTER_H__ 386