blob: 9ab90deec60d76720276e369b0f1101e6782edd8 [file] [log] [blame]
temporal40ee5512008-07-10 02:12:20 +00001// Protocol Buffers - Google's data interchange format
kenton@google.com24bf56f2008-09-24 20:31:01 +00002// Copyright 2008 Google Inc. All rights reserved.
temporal40ee5512008-07-10 02:12:20 +00003// http://code.google.com/p/protobuf/
4//
kenton@google.com24bf56f2008-09-24 20:31:01 +00005// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
temporal40ee5512008-07-10 02:12:20 +00008//
kenton@google.com24bf56f2008-09-24 20:31:01 +00009// * 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.
temporal40ee5512008-07-10 02:12:20 +000018//
kenton@google.com24bf56f2008-09-24 20:31:01 +000019// 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.
temporal40ee5512008-07-10 02:12:20 +000030
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
40namespace google {
41namespace protobuf {
42namespace io {
43
44Printer::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
53Printer::~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
60void 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'.
kenton@google.comfccb1462009-12-18 02:11:36 +000068 WriteRaw(text + pos, i - pos + 1);
temporal40ee5512008-07-10 02:12:20 +000069 pos = i + 1;
70
kenton@google.comfccb1462009-12-18 02:11:36 +000071 // Setting this true will cause the next WriteRaw() to insert an indent
temporal40ee5512008-07-10 02:12:20 +000072 // 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.
kenton@google.comfccb1462009-12-18 02:11:36 +000079 WriteRaw(text + pos, i - pos);
temporal40ee5512008-07-10 02:12:20 +000080 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.
kenton@google.comfccb1462009-12-18 02:11:36 +000093 WriteRaw(&variable_delimiter_, 1);
temporal40ee5512008-07-10 02:12:20 +000094 } 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 {
kenton@google.comfccb1462009-12-18 02:11:36 +0000100 WriteRaw(iter->second.data(), iter->second.size());
temporal40ee5512008-07-10 02:12:20 +0000101 }
102 }
103
104 // Advance past this variable.
105 i = endpos;
106 pos = endpos + 1;
107 }
108 }
109
110 // Write the rest.
kenton@google.comfccb1462009-12-18 02:11:36 +0000111 WriteRaw(text + pos, size - pos);
temporal40ee5512008-07-10 02:12:20 +0000112}
113
114void Printer::Print(const char* text) {
115 static map<string, string> empty;
116 Print(empty, text);
117}
118
119void 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
126void 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
liujisi@google.com33165fe2010-11-02 13:14:58 +0000135void Printer::Print(const char* text,
136 const char* variable1, const string& value1,
137 const char* variable2, const string& value2,
138 const char* variable3, const string& value3) {
139 map<string, string> vars;
140 vars[variable1] = value1;
141 vars[variable2] = value2;
142 vars[variable3] = value3;
143 Print(vars, text);
144}
145
temporal40ee5512008-07-10 02:12:20 +0000146void Printer::Indent() {
147 indent_ += " ";
148}
149
150void Printer::Outdent() {
151 if (indent_.empty()) {
152 GOOGLE_LOG(DFATAL) << " Outdent() without matching Indent().";
153 return;
154 }
155
156 indent_.resize(indent_.size() - 2);
157}
158
kenton@google.comfccb1462009-12-18 02:11:36 +0000159void Printer::PrintRaw(const string& data) {
160 WriteRaw(data.data(), data.size());
161}
162
163void Printer::PrintRaw(const char* data) {
164 if (failed_) return;
165 WriteRaw(data, strlen(data));
166}
167
168void Printer::WriteRaw(const char* data, int size) {
temporal40ee5512008-07-10 02:12:20 +0000169 if (failed_) return;
170 if (size == 0) return;
171
172 if (at_start_of_line_) {
173 // Insert an indent.
174 at_start_of_line_ = false;
kenton@google.comfccb1462009-12-18 02:11:36 +0000175 WriteRaw(indent_.data(), indent_.size());
temporal40ee5512008-07-10 02:12:20 +0000176 if (failed_) return;
177 }
178
179 while (size > buffer_size_) {
180 // Data exceeds space in the buffer. Copy what we can and request a
181 // new buffer.
182 memcpy(buffer_, data, buffer_size_);
183 data += buffer_size_;
184 size -= buffer_size_;
185 void* void_buffer;
186 failed_ = !output_->Next(&void_buffer, &buffer_size_);
187 if (failed_) return;
188 buffer_ = reinterpret_cast<char*>(void_buffer);
189 }
190
191 // Buffer is big enough to receive the data; copy it.
192 memcpy(buffer_, data, size);
193 buffer_ += size;
194 buffer_size_ -= size;
195}
196
197} // namespace io
198} // namespace protobuf
199} // namespace google