mirror of https://github.com/procxx/kepka.git
				
				
				
			
		
			
				
	
	
		
			191 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  *  Created by Phil on 19/07/2017.
 | |
|  *
 | |
|  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
 | |
|  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | |
|  */
 | |
| 
 | |
| #include "catch_xmlwriter.h"
 | |
| 
 | |
| #include <iomanip>
 | |
| 
 | |
| namespace Catch {
 | |
| 
 | |
|     XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
 | |
|     :   m_str( str ),
 | |
|         m_forWhat( forWhat )
 | |
|     {}
 | |
| 
 | |
|     void XmlEncode::encodeTo( std::ostream& os ) const {
 | |
| 
 | |
|         // Apostrophe escaping not necessary if we always use " to write attributes
 | |
|         // (see: http://www.w3.org/TR/xml/#syntax)
 | |
| 
 | |
|         for( std::size_t i = 0; i < m_str.size(); ++ i ) {
 | |
|             char c = m_str[i];
 | |
|             switch( c ) {
 | |
|                 case '<':   os << "<"; break;
 | |
|                 case '&':   os << "&"; break;
 | |
| 
 | |
|                 case '>':
 | |
|                     // See: http://www.w3.org/TR/xml/#syntax
 | |
|                     if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
 | |
|                         os << ">";
 | |
|                     else
 | |
|                         os << c;
 | |
|                     break;
 | |
| 
 | |
|                 case '\"':
 | |
|                     if( m_forWhat == ForAttributes )
 | |
|                         os << """;
 | |
|                     else
 | |
|                         os << c;
 | |
|                     break;
 | |
| 
 | |
|                 default:
 | |
|                     // Escape control chars - based on contribution by @espenalb in PR #465 and
 | |
|                     // by @mrpi PR #588
 | |
|                     if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
 | |
|                         // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
 | |
|                         os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
 | |
|                            << static_cast<int>( c );
 | |
|                     }
 | |
|                     else
 | |
|                         os << c;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
 | |
|         xmlEncode.encodeTo( os );
 | |
|         return os;
 | |
|     }
 | |
| 
 | |
|     XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer )
 | |
|     :   m_writer( writer )
 | |
|     {}
 | |
| 
 | |
|     XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
 | |
|     :   m_writer( other.m_writer ){
 | |
|         other.m_writer = nullptr;
 | |
|     }
 | |
|     XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
 | |
|         if ( m_writer ) {
 | |
|             m_writer->endElement();
 | |
|         }
 | |
|         m_writer = other.m_writer;
 | |
|         other.m_writer = nullptr;
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     XmlWriter::ScopedElement::~ScopedElement() {
 | |
|         if( m_writer )
 | |
|             m_writer->endElement();
 | |
|     }
 | |
| 
 | |
|     XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) {
 | |
|         m_writer->writeText( text, indent );
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
 | |
|     {
 | |
|         writeDeclaration();
 | |
|     }
 | |
| 
 | |
|     XmlWriter::~XmlWriter() {
 | |
|         while( !m_tags.empty() )
 | |
|             endElement();
 | |
|     }
 | |
| 
 | |
|     XmlWriter& XmlWriter::startElement( std::string const& name ) {
 | |
|         ensureTagClosed();
 | |
|         newlineIfNecessary();
 | |
|         m_os << m_indent << '<' << name;
 | |
|         m_tags.push_back( name );
 | |
|         m_indent += "  ";
 | |
|         m_tagIsOpen = true;
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) {
 | |
|         ScopedElement scoped( this );
 | |
|         startElement( name );
 | |
|         return scoped;
 | |
|     }
 | |
| 
 | |
|     XmlWriter& XmlWriter::endElement() {
 | |
|         newlineIfNecessary();
 | |
|         m_indent = m_indent.substr( 0, m_indent.size()-2 );
 | |
|         if( m_tagIsOpen ) {
 | |
|             m_os << "/>";
 | |
|             m_tagIsOpen = false;
 | |
|         }
 | |
|         else {
 | |
|             m_os << m_indent << "</" << m_tags.back() << ">";
 | |
|         }
 | |
|         m_os << std::endl;
 | |
|         m_tags.pop_back();
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) {
 | |
|         if( !name.empty() && !attribute.empty() )
 | |
|             m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) {
 | |
|         m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) {
 | |
|         if( !text.empty() ){
 | |
|             bool tagWasOpen = m_tagIsOpen;
 | |
|             ensureTagClosed();
 | |
|             if( tagWasOpen && indent )
 | |
|                 m_os << m_indent;
 | |
|             m_os << XmlEncode( text );
 | |
|             m_needsNewline = true;
 | |
|         }
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     XmlWriter& XmlWriter::writeComment( std::string const& text ) {
 | |
|         ensureTagClosed();
 | |
|         m_os << m_indent << "<!--" << text << "-->";
 | |
|         m_needsNewline = true;
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     void XmlWriter::writeStylesheetRef( std::string const& url ) {
 | |
|         m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
 | |
|     }
 | |
| 
 | |
|     XmlWriter& XmlWriter::writeBlankLine() {
 | |
|         ensureTagClosed();
 | |
|         m_os << '\n';
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     void XmlWriter::ensureTagClosed() {
 | |
|         if( m_tagIsOpen ) {
 | |
|             m_os << ">" << std::endl;
 | |
|             m_tagIsOpen = false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void XmlWriter::writeDeclaration() {
 | |
|         m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
 | |
|     }
 | |
| 
 | |
|     void XmlWriter::newlineIfNecessary() {
 | |
|         if( m_needsNewline ) {
 | |
|             m_os << std::endl;
 | |
|             m_needsNewline = false;
 | |
|         }
 | |
|     }
 | |
| }
 |