1    	#ifndef JSON_SPIRIT_WRITER_TEMPLATE
2    	#define JSON_SPIRIT_WRITER_TEMPLATE
3    	
4    	//          Copyright John W. Wilkinson 2007 - 2011
5    	// Distributed under the MIT License, see accompanying file LICENSE.txt
6    	
7    	// json spirit version 4.05
8    	
9    	#if defined(_MSC_VER) && (_MSC_VER >= 1020)
10   	# pragma once
11   	#endif
12   	
13   	#include "json_spirit_value.h"
14   	#include "json_spirit_writer_options.h"
15   	
16   	#include <sstream>
17   	#include <iomanip>
18   	#include <boost/io/ios_state.hpp>
19   	
20   	#include "include/ceph_assert.h"
21   	
22   	namespace json_spirit
23   	{
24   	    inline char to_hex_char( unsigned int c )
25   	    {
26   	        ceph_assert( c <= 0xF );
27   	
28   	        const char ch = static_cast< char >( c );
29   	
30   	        if( ch < 10 ) return '0' + ch;
31   	
32   	        return 'A' - 10 + ch;
33   	    }
34   	
35   	    template< class String_type >
36   	    String_type non_printable_to_string( unsigned int c )
37   	    {
38   	        String_type result( 6, '\\' );
39   	
40   	        result[1] = 'u';
41   	
42   	        result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4;
43   	        result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4;
44   	        result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4;
45   	        result[ 2 ] = to_hex_char( c & 0x000F );
46   	
47   	        return result;
48   	    }
49   	
50   	    template< typename Char_type, class String_type >
51   	    bool add_esc_char( Char_type c, String_type& s )
52   	    {
53   	        switch( c )
54   	        {
55   	            case '"':  s += to_str< String_type >( "\\\"" ); return true;
56   	            case '\\': s += to_str< String_type >( "\\\\" ); return true;
57   	            case '\b': s += to_str< String_type >( "\\b"  ); return true;
58   	            case '\f': s += to_str< String_type >( "\\f"  ); return true;
59   	            case '\n': s += to_str< String_type >( "\\n"  ); return true;
60   	            case '\r': s += to_str< String_type >( "\\r"  ); return true;
61   	            case '\t': s += to_str< String_type >( "\\t"  ); return true;
62   	        }
63   	
64   	        return false;
65   	    }
66   	
67   	    template< class String_type >
68   	    String_type add_esc_chars( const String_type& s, bool raw_utf8 )
69   	    {
70   	        typedef typename String_type::const_iterator Iter_type;
71   	        typedef typename String_type::value_type     Char_type;
72   	
73   	        String_type result;
74   	
75   	        const Iter_type end( s.end() );
76   	
77   	        for( Iter_type i = s.begin(); i != end; ++i )
78   	        {
79   	            const Char_type c( *i );
80   	
81   	            if( add_esc_char( c, result ) ) continue;
82   	
83   	            if( raw_utf8 )
84   	            {
85   	                result += c;
86   	            }
87   	            else
88   	            {
89   	                const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c );
90   	
91   	                if( iswprint( unsigned_c ) )
92   	                {
93   	                    result += c;
94   	                }
95   	                else
96   	                {
97   	                    result += non_printable_to_string< String_type >( unsigned_c );
98   	                }
99   	            }
100  	        }
101  	
102  	        return result;
103  	    }
104  	
105  	    template< class Ostream >
106  	    void append_double( Ostream& os, const double d, const int precision )
107  	    {
(1) Event format_changed: "showpoint" changes the format state of "os" for category showpoint.
(2) Event format_changed: "setprecision" changes the format state of "os" for category precision.
Also see events: [end_of_path]
108  	        os << std::showpoint << std::setprecision( precision ) << d;
(3) Event end_of_path: Changing format state of stream "os" for categories showpoint, precision without later restoring it.
Also see events: [format_changed][format_changed]
109  	    }
110  	
111  	    template< class String_type >
112  	    void erase_and_extract_exponent( String_type& str, String_type& exp )
113  	    {
114  	        const typename String_type::size_type exp_start= str.find( 'e' );
115  	
116  	        if( exp_start != String_type::npos )
117  	        {
118  	            exp = str.substr( exp_start );
119  	            str.erase( exp_start );
120  	        }
121  	    }
122  	
123  	    template< class String_type >
124  	    typename String_type::size_type find_first_non_zero( const String_type& str )
125  	    {
126  	        typename String_type::size_type result = str.size() - 1;
127  	
128  	        for( ; result != 0; --result )
129  	        {
130  	            if( str[ result ] != '0' )
131  	            {
132  	                break;
133  	            }
134  	        }
135  	
136  	        return result;
137  	    }
138  	
139  	    template< class String_type >
140  	    void remove_trailing( String_type& str )
141  	    {
142  	        String_type exp;
143  	
144  	        erase_and_extract_exponent( str, exp );
145  	
146  	        const typename String_type::size_type first_non_zero = find_first_non_zero( str );
147  	
148  	        if( first_non_zero != 0 )
149  	        {
150  	            const int offset = str[first_non_zero] == '.' ? 2 : 1;  // note zero digits following a decimal point is non standard
151  	            str.erase( first_non_zero + offset );
152  	        }
153  	
154  	        str += exp;
155  	    }
156  	
157  	    // this class generates the JSON text,
158  	    // it keeps track of the indentation level etc.
159  	    //
160  	    template< class Value_type, class Ostream_type >
161  	    class Generator
162  	    {
163  	        typedef typename Value_type::Config_type Config_type;
164  	        typedef typename Config_type::String_type String_type;
165  	        typedef typename Config_type::Object_type Object_type;
166  	        typedef typename Config_type::Array_type Array_type;
167  	        typedef typename String_type::value_type Char_type;
168  	        typedef typename Object_type::value_type Obj_member_type;
169  	
170  	    public:
171  	
172  	        Generator( const Value_type& value, Ostream_type& os, unsigned int options )
173  	        :   os_( os )
174  	        ,   indentation_level_( 0 )
175  	        ,   pretty_( ( options & pretty_print ) != 0 || ( options & single_line_arrays ) != 0 )
176  	        ,   raw_utf8_( ( options & raw_utf8 ) != 0 )
177  	        ,   remove_trailing_zeros_( ( options & remove_trailing_zeros ) != 0 )
178  	        ,   single_line_arrays_( ( options & single_line_arrays ) != 0 )
179  	        ,   ios_saver_( os )
180  	        {
181  	            output( value );
182  	        }
183  	
184  	    private:
185  	
186  	        void output( const Value_type& value )
187  	        {
188  	            switch( value.type() )
189  	            {
190  	                case obj_type:   output( value.get_obj() );   break;
191  	                case array_type: output( value.get_array() ); break;
192  	                case str_type:   output( value.get_str() );   break;
193  	                case bool_type:  output( value.get_bool() );  break;
194  	                case real_type:  output( value.get_real() );  break;
195  	                case int_type:   output_int( value );         break;
196  	                case null_type:  os_ << "null";               break;
197  	                default: ceph_assert( false );
198  	            }
199  	        }
200  	
201  	        void output( const Object_type& obj )
202  	        {
203  	            output_array_or_obj( obj, '{', '}' );
204  	        }
205  	
206  	        void output( const Obj_member_type& member )
207  	        {
208  	            output( Config_type::get_name( member ) ); space(); 
209  	            os_ << ':'; space(); 
210  	            output( Config_type::get_value( member ) );
211  	        }
212  	
213  	        void output_int( const Value_type& value )
214  	        {
215  	            if( value.is_uint64() )
216  	            {
217  	                os_ << value.get_uint64();
218  	            }
219  	            else
220  	            {
221  	               os_ << value.get_int64();
222  	            }
223  	        }
224  	
225  	        void output( const String_type& s )
226  	        {
227  	            os_ << '"' << add_esc_chars( s, raw_utf8_ ) << '"';
228  	        }
229  	
230  	        void output( bool b )
231  	        {
232  	            os_ << to_str< String_type >( b ? "true" : "false" );
233  	        }
234  	
235  	        void output( double d )
236  	        {
237  	            if( remove_trailing_zeros_ )
238  	            {
239  	                std::basic_ostringstream< Char_type > os;
240  	
241  	                append_double( os, d, 16 );  // note precision is 16 so that we get some trailing space that we can remove,
242  	                                             // otherwise, 0.1234 gets converted to "0.12399999..."
243  	
244  	                String_type str = os.str();
245  	
246  	                remove_trailing( str );
247  	
248  	                os_ << str;
249  	            }
250  	            else
251  	            {
252  	                append_double( os_, d, 17 );
253  	            }
254  	        }
255  	
256  	        static bool contains_composite_elements( const Array_type& arr )
257  	        {
258  	            for( typename Array_type::const_iterator i = arr.begin(); i != arr.end(); ++i )
259  	            {
260  	                const Value_type& val = *i;
261  	
262  	                if( val.type() == obj_type ||
263  	                    val.type() == array_type )
264  	                {
265  	                    return true;
266  	                }
267  	            }
268  	
269  	            return false;
270  	        }
271  	
272  	        template< class Iter >
273  	        void output_composite_item( Iter i, Iter last )
274  	        {
275  	            output( *i );
276  	
277  	            if( ++i != last )
278  	            {
279  	                os_ << ',';
280  	            }
281  	        }
282  	
283  	        void output( const Array_type& arr )
284  	        {
285  	            if( single_line_arrays_ && !contains_composite_elements( arr )  )
286  	            {
287  	                os_ << '['; space();
288  	               
289  	                for( typename Array_type::const_iterator i = arr.begin(); i != arr.end(); ++i )
290  	                {
291  	                    output_composite_item( i, arr.end() );
292  	
293  	                    space();
294  	                }
295  	
296  	                os_ << ']';
297  	            }
298  	            else
299  	            {
300  	                output_array_or_obj( arr, '[', ']' );
301  	            }
302  	        }
303  	
304  	        template< class T >
305  	        void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char )
306  	        {
307  	            os_ << start_char; new_line();
308  	
309  	            ++indentation_level_;
310  	            
311  	            for( typename T::const_iterator i = t.begin(); i != t.end(); ++i )
312  	            {
313  	                indent();
314  	
315  	                output_composite_item( i, t.end() );
316  	
317  	                new_line();
318  	            }
319  	
320  	            --indentation_level_;
321  	
322  	            indent(); os_ << end_char;
323  	        }
324  	        
325  	        void indent()
326  	        {
327  	            if( !pretty_ ) return;
328  	
329  	            for( int i = 0; i < indentation_level_; ++i )
330  	            { 
331  	                os_ << "    ";
332  	            }
333  	        }
334  	
335  	        void space()
336  	        {
337  	            if( pretty_ ) os_ << ' ';
338  	        }
339  	
340  	        void new_line()
341  	        {
342  	            if( pretty_ ) os_ << '\n';
343  	        }
344  	
345  	        Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning
346  	
347  	        Ostream_type& os_;
348  	        int indentation_level_;
349  	        bool pretty_;
350  	        bool raw_utf8_;
351  	        bool remove_trailing_zeros_;
352  	        bool single_line_arrays_;
353  	        boost::io::basic_ios_all_saver< Char_type > ios_saver_;  // so that ostream state is reset after control is returned to the caller
354  	    };
355  	
356  	    // writes JSON Value to a stream, e.g.
357  	    //
358  	    // write_stream( value, os, pretty_print );
359  	    //
360  	    template< class Value_type, class Ostream_type >
361  	    void write_stream( const Value_type& value, Ostream_type& os, unsigned int options = 0 )
362  	    {
363  	        os << std::dec;
364  	        Generator< Value_type, Ostream_type >( value, os, options );
365  	    }
366  	
367  	    // writes JSON Value to a stream, e.g.
368  	    //
369  	    // const string json_str = write( value, pretty_print );
370  	    //
371  	    template< class Value_type >
372  	    typename Value_type::String_type write_string( const Value_type& value, unsigned int options = 0 )
373  	    {
374  	        typedef typename Value_type::String_type::value_type Char_type;
375  	
376  	        std::basic_ostringstream< Char_type > os;
377  	
378  	        write_stream( value, os, options );
379  	
380  	        return os.str();
381  	    }
382  	}
383  	
384  	#endif
385