1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // http://code.google.com/p/protobuf/
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 #include <google/protobuf/io/printer.h>
36 #include <google/protobuf/io/zero_copy_stream.h>
37 #include <google/protobuf/stubs/common.h>
38 #include <google/protobuf/stubs/strutil.h>
39
40 namespace google {
41 namespace protobuf {
42 namespace io {
43
Printer(ZeroCopyOutputStream * output,char variable_delimiter)44 Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter)
45 : variable_delimiter_(variable_delimiter),
46 output_(output),
47 buffer_(NULL),
48 buffer_size_(0),
49 at_start_of_line_(true),
50 failed_(false) {
51 }
52
~Printer()53 Printer::~Printer() {
54 // Only BackUp() if we're sure we've successfully called Next() at least once.
55 if (buffer_size_ > 0) {
56 output_->BackUp(buffer_size_);
57 }
58 }
59
Print(const map<string,string> & variables,const char * text)60 void Printer::Print(const map<string, string>& variables, const char* text) {
61 int size = strlen(text);
62 int pos = 0; // The number of bytes we've written so far.
63
64 for (int i = 0; i < size; i++) {
65 if (text[i] == '\n') {
66 // Saw newline. If there is more text, we may need to insert an indent
67 // here. So, write what we have so far, including the '\n'.
68 WriteRaw(text + pos, i - pos + 1);
69 pos = i + 1;
70
71 // Setting this true will cause the next WriteRaw() to insert an indent
72 // first.
73 at_start_of_line_ = true;
74
75 } else if (text[i] == variable_delimiter_) {
76 // Saw the start of a variable name.
77
78 // Write what we have so far.
79 WriteRaw(text + pos, i - pos);
80 pos = i + 1;
81
82 // Find closing delimiter.
83 const char* end = strchr(text + pos, variable_delimiter_);
84 if (end == NULL) {
85 GOOGLE_LOG(DFATAL) << " Unclosed variable name.";
86 end = text + pos;
87 }
88 int endpos = end - text;
89
90 string varname(text + pos, endpos - pos);
91 if (varname.empty()) {
92 // Two delimiters in a row reduce to a literal delimiter character.
93 WriteRaw(&variable_delimiter_, 1);
94 } else {
95 // Replace with the variable's value.
96 map<string, string>::const_iterator iter = variables.find(varname);
97 if (iter == variables.end()) {
98 GOOGLE_LOG(DFATAL) << " Undefined variable: " << varname;
99 } else {
100 WriteRaw(iter->second.data(), iter->second.size());
101 }
102 }
103
104 // Advance past this variable.
105 i = endpos;
106 pos = endpos + 1;
107 }
108 }
109
110 // Write the rest.
111 WriteRaw(text + pos, size - pos);
112 }
113
Print(const char * text)114 void Printer::Print(const char* text) {
115 static map<string, string> empty;
116 Print(empty, text);
117 }
118
Print(const char * text,const char * variable,const string & value)119 void Printer::Print(const char* text,
120 const char* variable, const string& value) {
121 map<string, string> vars;
122 vars[variable] = value;
123 Print(vars, text);
124 }
125
Print(const char * text,const char * variable1,const string & value1,const char * variable2,const string & value2)126 void Printer::Print(const char* text,
127 const char* variable1, const string& value1,
128 const char* variable2, const string& value2) {
129 map<string, string> vars;
130 vars[variable1] = value1;
131 vars[variable2] = value2;
132 Print(vars, text);
133 }
134
Indent()135 void Printer::Indent() {
136 indent_ += " ";
137 }
138
Outdent()139 void Printer::Outdent() {
140 if (indent_.empty()) {
141 GOOGLE_LOG(DFATAL) << " Outdent() without matching Indent().";
142 return;
143 }
144
145 indent_.resize(indent_.size() - 2);
146 }
147
PrintRaw(const string & data)148 void Printer::PrintRaw(const string& data) {
149 WriteRaw(data.data(), data.size());
150 }
151
PrintRaw(const char * data)152 void Printer::PrintRaw(const char* data) {
153 if (failed_) return;
154 WriteRaw(data, strlen(data));
155 }
156
WriteRaw(const char * data,int size)157 void Printer::WriteRaw(const char* data, int size) {
158 if (failed_) return;
159 if (size == 0) return;
160
161 if (at_start_of_line_) {
162 // Insert an indent.
163 at_start_of_line_ = false;
164 WriteRaw(indent_.data(), indent_.size());
165 if (failed_) return;
166 }
167
168 while (size > buffer_size_) {
169 // Data exceeds space in the buffer. Copy what we can and request a
170 // new buffer.
171 memcpy(buffer_, data, buffer_size_);
172 data += buffer_size_;
173 size -= buffer_size_;
174 void* void_buffer;
175 failed_ = !output_->Next(&void_buffer, &buffer_size_);
176 if (failed_) return;
177 buffer_ = reinterpret_cast<char*>(void_buffer);
178 }
179
180 // Buffer is big enough to receive the data; copy it.
181 memcpy(buffer_, data, size);
182 buffer_ += size;
183 buffer_size_ -= size;
184 }
185
186 } // namespace io
187 } // namespace protobuf
188 } // namespace google
189