| // Copyright 2016 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "tools/gn/xml_element_writer.h" | 
 |  | 
 | #include <memory> | 
 |  | 
 | XmlAttributes::XmlAttributes() = default; | 
 |  | 
 | XmlAttributes::XmlAttributes(const base::StringPiece& attr_key, | 
 |                              const base::StringPiece& attr_value) { | 
 |   add(attr_key, attr_value); | 
 | } | 
 |  | 
 | XmlAttributes& XmlAttributes::add(const base::StringPiece& attr_key, | 
 |                                   const base::StringPiece& attr_value) { | 
 |   push_back(std::make_pair(attr_key, attr_value)); | 
 |   return *this; | 
 | } | 
 |  | 
 | XmlElementWriter::XmlElementWriter(std::ostream& out, | 
 |                                    const std::string& tag, | 
 |                                    const XmlAttributes& attributes) | 
 |     : XmlElementWriter(out, tag, attributes, 0) {} | 
 |  | 
 | XmlElementWriter::XmlElementWriter(std::ostream& out, | 
 |                                    const std::string& tag, | 
 |                                    const XmlAttributes& attributes, | 
 |                                    int indent) | 
 |     : out_(out), | 
 |       tag_(tag), | 
 |       indent_(indent), | 
 |       opening_tag_finished_(false), | 
 |       one_line_(true) { | 
 |   out << std::string(indent, ' ') << '<' << tag; | 
 |   for (auto attribute : attributes) | 
 |     out << ' ' << attribute.first << "=\"" << attribute.second << '"'; | 
 | } | 
 |  | 
 | XmlElementWriter::~XmlElementWriter() { | 
 |   if (!opening_tag_finished_) { | 
 |     // The XML spec does not require a space before the closing slash. However, | 
 |     // Eclipse is unable to parse XML settings files if there is no space. | 
 |     out_ << " />" << std::endl; | 
 |   } else { | 
 |     if (!one_line_) | 
 |       out_ << std::string(indent_, ' '); | 
 |     out_ << "</" << tag_ << '>' << std::endl; | 
 |   } | 
 | } | 
 |  | 
 | void XmlElementWriter::Text(const base::StringPiece& content) { | 
 |   StartContent(false); | 
 |   out_ << content; | 
 | } | 
 |  | 
 | std::unique_ptr<XmlElementWriter> XmlElementWriter::SubElement( | 
 |     const std::string& tag) { | 
 |   return SubElement(tag, XmlAttributes()); | 
 | } | 
 |  | 
 | std::unique_ptr<XmlElementWriter> XmlElementWriter::SubElement( | 
 |     const std::string& tag, | 
 |     const XmlAttributes& attributes) { | 
 |   StartContent(true); | 
 |   return std::make_unique<XmlElementWriter>(out_, tag, attributes, indent_ + 2); | 
 | } | 
 |  | 
 | std::ostream& XmlElementWriter::StartContent(bool start_new_line) { | 
 |   if (!opening_tag_finished_) { | 
 |     out_ << '>'; | 
 |     opening_tag_finished_ = true; | 
 |  | 
 |     if (start_new_line && one_line_) { | 
 |       out_ << std::endl; | 
 |       one_line_ = false; | 
 |     } | 
 |   } | 
 |  | 
 |   return out_; | 
 | } | 
 |  | 
 | std::string XmlEscape(const std::string& value) { | 
 |   std::string result; | 
 |   for (char c : value) { | 
 |     switch (c) { | 
 |       case '\n': | 
 |         result += "
"; | 
 |         break; | 
 |       case '\r': | 
 |         result += "
"; | 
 |         break; | 
 |       case '\t': | 
 |         result += "	"; | 
 |         break; | 
 |       case '"': | 
 |         result += """; | 
 |         break; | 
 |       case '<': | 
 |         result += "<"; | 
 |         break; | 
 |       case '>': | 
 |         result += ">"; | 
 |         break; | 
 |       case '&': | 
 |         result += "&"; | 
 |         break; | 
 |       default: | 
 |         result += c; | 
 |     } | 
 |   } | 
 |   return result; | 
 | } |