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