IndentingStreamBuf.h

// IndentingStreamBuf.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2008

#pragma once
#ifndef Ceda_cxUtils_IndentingStreamBuf_H
#define Ceda_cxUtils_IndentingStreamBuf_H

#include "xchar.h"
#include "cxUtils.h"
#include <iostream>
#include <stdio.h>

#ifdef _MSC_VER
    // struct 'X' needs to have dll-interface to be used by clients of class 'Y'
    #pragma warning(disable:4251)
#endif

namespace ceda
{

///////////////////////////////////////////////////////////////////////////////////////////////////
// IndentingStreamBuf

/*
Decorates a streambuf in order to apply automatic support for indentation.  Note that 

    1) the ctor takes the streambuf to be decorated.  
    
    2) IndentingStreamBuf inherits from streambuf in order to represent a decorated
       streambuf.
       IndentingStreamBuf only supports writing and underflow() causes an assertion.

m_indent represents the current indentation (i.e. number of spaces) to be applied after every 
linefeed.  Initially it is zero.

The actual indentation is applied lazily, meaning that the indent isn't eagerly applied after
processing a linefeed.  Instead it is triggered by subsequent writing.  This is important to
ensure the indent can be changed after the linefeed and before subsequent writing.

A call to sync() flushes the IndentingStreamBuf, and this also causes the decorated streambuf
to be flushed as well (with a call to pubsync()).
*/

class cxUtils_API IndentingStreamBuf : public std::basic_streambuf<xchar>
{
public:
    IndentingStreamBuf(std::basic_streambuf<xchar>& receiver);
    ~IndentingStreamBuf();

    // Implementation of streambuf
    virtual int_type overflow(int_type nCh = EOF);
    virtual int_type underflow();
    virtual int sync();

    void ProcessBuffer(const xchar* p1, const xchar* p2);
    void Indent(ssize_t offset) { m_indent += offset; }

private:
    enum { STREAM_BUFFER_SIZE = 1024 };
    
    ssize_t m_indent;
    bool m_needToIndent;

    std::basic_streambuf<xchar>& m_receiver;
    xchar m_buffer[STREAM_BUFFER_SIZE];
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// MakeOstreamIndentable

/*
Used to decorate the streambuf in a given ostream with an IndentingStreamBuf in order to support
automatic indentation.
*/

class cxUtils_API MakeOstreamIndentable
{
public:
    MakeOstreamIndentable(std::basic_ostream<xchar>& os);
    ~MakeOstreamIndentable();
    
    std::basic_ostream<xchar>& m_os;
    std::basic_streambuf<xchar>* m_oldStreamBuf;
    IndentingStreamBuf m_isb;
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// OstreamIndenter

/*
Used to apply an indent in a lexical scope to the given ostream which MUST be directly using an
IndentingStreamBuf for its streambuf.   Otherwise there will be undefined bahviour.
*/

class cxUtils_API OstreamIndenter
{
    cxNotCloneable(OstreamIndenter)
public:
    OstreamIndenter(std::basic_ostream<xchar>& os, ssize_t indent = 4);
    ~OstreamIndenter();
private:
    IndentingStreamBuf& m_isb;
    ssize_t m_indent;
    std::basic_ostream<xchar>& m_os;
};

} // namespace ceda

#endif // include guard