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
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'.
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
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
135void Printer::Indent() {
136  indent_ += "  ";
137}
138
139void 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
148void Printer::PrintRaw(const string& data) {
149  WriteRaw(data.data(), data.size());
150}
151
152void Printer::PrintRaw(const char* data) {
153  if (failed_) return;
154  WriteRaw(data, strlen(data));
155}
156
157void 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