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