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