Catch/include/internal/catch_xmlwriter.hpp
Phil Nash 89d1e6c4f1 Tweaks to allow headers to be glued together.
Added Python script to generate single header.
Added new XCode project that runs self test against single header
2011-05-24 08:23:02 +01:00

333 lines
9.0 KiB
C++

/*
* catch_xmlwriter.hpp
* Catch
*
* Created by Phil on 09/12/2010.
* Copyright 2010 Two Blue Cubes Ltd. All rights reserved.
*
* 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)
*/
#ifndef TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
#include <sstream>
#include <string>
#include <vector>
namespace Catch
{
class XmlWriter
{
public:
class ScopedElement
{
public:
///////////////////////////////////////////////////////////////////
ScopedElement
(
XmlWriter* writer
)
: m_writer( writer )
{
}
///////////////////////////////////////////////////////////////////
ScopedElement
(
const ScopedElement& other
)
: m_writer( other.m_writer )
{
other.m_writer = NULL;
}
///////////////////////////////////////////////////////////////////
~ScopedElement
()
{
if( m_writer )
m_writer->endElement();
}
///////////////////////////////////////////////////////////////////
ScopedElement& writeText
(
const std::string& text
)
{
m_writer->writeText( text );
return *this;
}
///////////////////////////////////////////////////////////////////
template<typename T>
ScopedElement& writeAttribute
(
const std::string& name,
const T& attribute
)
{
m_writer->writeAttribute( name, attribute );
return *this;
}
private:
mutable XmlWriter* m_writer;
};
///////////////////////////////////////////////////////////////////////
XmlWriter
()
: m_tagIsOpen( false ),
m_needsNewline( false ),
m_os( &std::cout )
{
}
///////////////////////////////////////////////////////////////////////
XmlWriter
(
std::ostream& os
)
: m_tagIsOpen( false ),
m_needsNewline( false ),
m_os( &os )
{
}
///////////////////////////////////////////////////////////////////////
~XmlWriter
()
{
while( !m_tags.empty() )
{
endElement();
}
}
///////////////////////////////////////////////////////////////////////
XmlWriter& operator =
(
const XmlWriter& other
)
{
XmlWriter temp( other );
swap( temp );
return *this;
}
///////////////////////////////////////////////////////////////////////
void swap
(
XmlWriter& other
)
{
std::swap( m_tagIsOpen, other.m_tagIsOpen );
std::swap( m_needsNewline, other.m_needsNewline );
std::swap( m_tags, other.m_tags );
std::swap( m_indent, other.m_indent );
std::swap( m_os, other.m_os );
}
///////////////////////////////////////////////////////////////////////
XmlWriter& startElement
(
const std::string& name
)
{
ensureTagClosed();
newlineIfNecessary();
stream() << m_indent << "<" << name;
m_tags.push_back( name );
m_indent += " ";
m_tagIsOpen = true;
return *this;
}
///////////////////////////////////////////////////////////////////////
ScopedElement scopedElement
(
const std::string& name
)
{
ScopedElement scoped( this );
startElement( name );
return scoped;
}
///////////////////////////////////////////////////////////////////////
XmlWriter& endElement
()
{
newlineIfNecessary();
m_indent = m_indent.substr( 0, m_indent.size()-2 );
if( m_tagIsOpen )
{
stream() << "/>\n";
m_tagIsOpen = false;
}
else
{
stream() << m_indent << "</" << m_tags.back() << ">\n";
}
m_tags.pop_back();
return *this;
}
///////////////////////////////////////////////////////////////////////
XmlWriter& writeAttribute
(
const std::string& name,
const std::string& attribute
)
{
if( !name.empty() && !attribute.empty() )
{
stream() << " " << name << "=\"";
writeEncodedText( attribute );
stream() << "\"";
}
return *this;
}
///////////////////////////////////////////////////////////////////////
XmlWriter& writeAttribute
(
const std::string& name,
bool attribute
)
{
stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\"";
return *this;
}
///////////////////////////////////////////////////////////////////////
template<typename T>
XmlWriter& writeAttribute
(
const std::string& name,
const T& attribute
)
{
if( !name.empty() )
{
stream() << " " << name << "=\"" << attribute << "\"";
}
return *this;
}
///////////////////////////////////////////////////////////////////////
XmlWriter& writeText
(
const std::string& text
)
{
if( !text.empty() )
{
bool tagWasOpen = m_tagIsOpen;
ensureTagClosed();
if( tagWasOpen )
stream() << m_indent;
writeEncodedText( text );
m_needsNewline = true;
}
return *this;
}
///////////////////////////////////////////////////////////////////////
XmlWriter& writeComment
(
const std::string& text
)
{
ensureTagClosed();
stream() << m_indent << "<!--" << text << "-->";
m_needsNewline = true;
return *this;
}
///////////////////////////////////////////////////////////////////////
XmlWriter& writeBlankLine
()
{
ensureTagClosed();
stream() << "\n";
return *this;
}
private:
///////////////////////////////////////////////////////////////////////
std::ostream& stream
()
{
return *m_os;
}
///////////////////////////////////////////////////////////////////////
void ensureTagClosed
()
{
if( m_tagIsOpen )
{
stream() << ">\n";
m_tagIsOpen = false;
}
}
///////////////////////////////////////////////////////////////////////
void newlineIfNecessary
()
{
if( m_needsNewline )
{
stream() << "\n";
m_needsNewline = false;
}
}
///////////////////////////////////////////////////////////////////////
void writeEncodedText
(
const std::string& text
)
{
// !TBD finish this
if( !findReplaceableString( text, "<", "&lt;" ) &&
!findReplaceableString( text, "&", "&amp;" ) &&
!findReplaceableString( text, "\"", "&quote;" ) )
{
stream() << text;
}
}
///////////////////////////////////////////////////////////////////////
bool findReplaceableString
(
const std::string& text,
const std::string& replaceWhat,
const std::string& replaceWith
)
{
std::string::size_type pos = text.find_first_of( replaceWhat );
if( pos != std::string::npos )
{
stream() << text.substr( 0, pos ) << replaceWith;
writeEncodedText( text.substr( pos+1 ) );
return true;
}
return false;
}
bool m_tagIsOpen;
bool m_needsNewline;
std::vector<std::string> m_tags;
std::string m_indent;
std::ostream* m_os;
};
}
#endif // TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED