1 /* 2 * Created by Phil on 18/4/2013. 3 * Copyright 2013 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 */ 8 // Only use header guard if we are not using an outer namespace 9 #ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE 10 # ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED 11 # ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED 12 # define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED 13 # endif 14 # else 15 # define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED 16 # endif 17 #endif 18 #ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED 19 #include <string> 20 #include <vector> 21 #include <sstream> 22 23 // Use optional outer namespace 24 #ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE 25 namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { 26 #endif 27 28 namespace Tbc { 29 30 #ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH 31 const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; 32 #else 33 const unsigned int consoleWidth = 80; 34 #endif 35 36 struct TextAttributes { TextAttributesTextAttributes37 TextAttributes() 38 : initialIndent( std::string::npos ), 39 indent( 0 ), 40 width( consoleWidth-1 ) 41 {} 42 setInitialIndentTextAttributes43 TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } setIndentTextAttributes44 TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } setWidthTextAttributes45 TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } 46 47 std::size_t initialIndent; // indent of first line, or npos 48 std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos 49 std::size_t width; // maximum width of text, including indent. Longer text will wrap 50 }; 51 52 class Text { 53 public: 54 Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) attr(_attr)55 : attr( _attr ) 56 { 57 const std::string wrappableBeforeChars = "[({<\t"; 58 const std::string wrappableAfterChars = "])}>-,./|\\"; 59 const std::string wrappableInsteadOfChars = " \n\r"; 60 std::string indent = _attr.initialIndent != std::string::npos 61 ? std::string( _attr.initialIndent, ' ' ) 62 : std::string( _attr.indent, ' ' ); 63 64 typedef std::string::const_iterator iterator; 65 iterator it = _str.begin(); 66 const iterator strEnd = _str.end(); 67 68 while( it != strEnd ) { 69 70 if( lines.size() >= 1000 ) { 71 lines.push_back( "... message truncated due to excessive size" ); 72 return; 73 } 74 75 76 std::string suffix; 77 std::size_t width = (std::min)( static_cast<size_t>( strEnd-it ), _attr.width-static_cast<size_t>( indent.size() ) ); 78 iterator itEnd = it+width; 79 iterator itNext = _str.end(); 80 81 iterator itNewLine = std::find( it, itEnd, '\n' ); 82 if( itNewLine != itEnd ) 83 itEnd = itNewLine; 84 85 if( itEnd != strEnd ) { 86 bool foundWrapPoint = false; 87 iterator findIt = itEnd; 88 do { 89 if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { 90 itEnd = findIt+1; 91 itNext = findIt+1; 92 foundWrapPoint = true; 93 } 94 else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { 95 itEnd = findIt; 96 itNext = findIt; 97 foundWrapPoint = true; 98 } 99 else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { 100 itNext = findIt+1; 101 itEnd = findIt; 102 foundWrapPoint = true; 103 } 104 if( findIt == it ) 105 break; 106 else 107 --findIt; 108 } 109 while( !foundWrapPoint ); 110 111 if( !foundWrapPoint ) { 112 // No good wrap char, so we'll break mid word and add a hyphen 113 --itEnd; 114 itNext = itEnd; 115 suffix = "-"; 116 } 117 else { 118 while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) 119 --itEnd; 120 } 121 } 122 lines.push_back( indent + std::string( it, itEnd ) + suffix ); 123 124 if( indent.size() != _attr.indent ) 125 indent = std::string( _attr.indent, ' ' ); 126 it = itNext; 127 } 128 } 129 130 131 132 typedef std::vector<std::string>::const_iterator const_iterator; 133 begin()134 const_iterator begin() const { return lines.begin(); } end()135 const_iterator end() const { return lines.end(); } last()136 std::string const& last() const { return lines.back(); } size()137 std::size_t size() const { return lines.size(); } 138 std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } toString()139 std::string toString() const { 140 std::ostringstream oss; 141 oss << *this; 142 return oss.str(); 143 } 144 145 inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { 146 for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); 147 it != itEnd; ++it ) { 148 if( it != _text.begin() ) 149 _stream << "\n"; 150 _stream << *it; 151 } 152 return _stream; 153 } 154 155 156 private: 157 std::string str; 158 TextAttributes attr; 159 std::vector<std::string> lines; 160 }; 161 162 } // end namespace Tbc 163 164 #ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE 165 } // end outer namespace 166 #endif 167 168 #endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED 169