1 /******************************************************************************
2  *
3  * Project:  CPL - Common Portability Library
4  * Purpose:  JSon streaming writer
5  * Author:   Even Rouault, even.rouault at spatialys.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 /*! @cond Doxygen_Suppress */
30 
31 #include <vector>
32 #include <string>
33 
34 #include "cpl_conv.h"
35 #include "cpl_string.h"
36 #include "cpl_json_streaming_writer.h"
37 
CPLJSonStreamingWriter(SerializationFuncType pfnSerializationFunc,void * pUserData)38 CPLJSonStreamingWriter::CPLJSonStreamingWriter(
39                     SerializationFuncType pfnSerializationFunc,
40                     void* pUserData):
41     m_pfnSerializationFunc(pfnSerializationFunc),
42     m_pUserData(pUserData)
43 {}
44 
~CPLJSonStreamingWriter()45 CPLJSonStreamingWriter::~CPLJSonStreamingWriter()
46 {
47     CPLAssert( m_nLevel == 0 );
48     CPLAssert( m_states.empty() );
49 }
50 
Print(const std::string & text)51 void CPLJSonStreamingWriter::Print(const std::string& text)
52 {
53     if( m_pfnSerializationFunc )
54     {
55         m_pfnSerializationFunc(text.c_str(), m_pUserData);
56     }
57     else
58     {
59         m_osStr += text;
60     }
61 }
62 
SetIndentationSize(int nSpaces)63 void CPLJSonStreamingWriter::SetIndentationSize(int nSpaces)
64 {
65     CPLAssert( m_nLevel == 0 );
66     m_osIndent.clear();
67     m_osIndent.resize(nSpaces, ' ');
68 }
69 
IncIndent()70 void CPLJSonStreamingWriter::IncIndent()
71 {
72     m_nLevel ++;
73     if( m_bPretty )
74         m_osIndentAcc += m_osIndent;
75 }
76 
DecIndent()77 void CPLJSonStreamingWriter::DecIndent()
78 {
79     CPLAssert(m_nLevel > 0);
80     m_nLevel --;
81     if( m_bPretty )
82         m_osIndentAcc.resize(m_osIndentAcc.size() - m_osIndent.size());
83 }
84 
FormatString(const std::string & str)85 std::string CPLJSonStreamingWriter::FormatString(const std::string& str)
86 {
87     std::string ret;
88     ret += '"';
89     for( char ch: str )
90     {
91         switch(ch)
92         {
93             case '"' : ret += "\\\""; break;
94             case '\\': ret += "\\\\"; break;
95             case '\b': ret += "\\b";  break;
96             case '\f': ret += "\\f";  break;
97             case '\n': ret += "\\n";  break;
98             case '\r': ret += "\\r";  break;
99             case '\t': ret += "\\t";  break;
100             default:
101                 if( static_cast<unsigned char>(ch) < ' ' )
102                     ret += CPLSPrintf("\\u%04X", ch);
103                 else
104                     ret += ch;
105                 break;
106         }
107     }
108     ret += '"';
109     return ret;
110 }
111 
EmitCommaIfNeeded()112 void CPLJSonStreamingWriter::EmitCommaIfNeeded()
113 {
114     if( m_bWaitForValue )
115     {
116         m_bWaitForValue = false;
117     }
118     else if( !m_states.empty() )
119     {
120         if( !m_states.back().bFirstChild )
121         {
122             Print(",");
123             if( m_bPretty && !m_bNewLineEnabled )
124                 Print(" ");
125         }
126         if( m_bPretty && m_bNewLineEnabled )
127         {
128             Print("\n");
129             Print(m_osIndentAcc);
130         }
131         m_states.back().bFirstChild = false;
132     }
133 }
134 
StartObj()135 void CPLJSonStreamingWriter::StartObj()
136 {
137     EmitCommaIfNeeded();
138     Print("{");
139     IncIndent();
140     m_states.emplace_back(State(true));
141 }
142 
EndObj()143 void CPLJSonStreamingWriter::EndObj()
144 {
145     CPLAssert(!m_bWaitForValue);
146     CPLAssert( !m_states.empty() );
147     CPLAssert( m_states.back().bIsObj );
148     DecIndent();
149     if( !m_states.back().bFirstChild )
150     {
151         if( m_bPretty && m_bNewLineEnabled )
152         {
153             Print("\n");
154             Print(m_osIndentAcc);
155         }
156     }
157     m_states.pop_back();
158     Print("}");
159 }
160 
StartArray()161 void CPLJSonStreamingWriter::StartArray()
162 {
163     EmitCommaIfNeeded();
164     Print("[");
165     IncIndent();
166     m_states.emplace_back(State(false));
167 }
168 
EndArray()169 void CPLJSonStreamingWriter::EndArray()
170 {
171     CPLAssert( !m_states.empty() );
172     CPLAssert( !m_states.back().bIsObj );
173     DecIndent();
174     if( !m_states.back().bFirstChild )
175     {
176         if( m_bPretty && m_bNewLineEnabled )
177         {
178             Print("\n");
179             Print(m_osIndentAcc);
180         }
181     }
182     m_states.pop_back();
183     Print("]");
184 }
185 
AddObjKey(const std::string & key)186 void CPLJSonStreamingWriter::AddObjKey(const std::string& key)
187 {
188     CPLAssert( !m_states.empty() );
189     CPLAssert( m_states.back().bIsObj );
190     CPLAssert(!m_bWaitForValue);
191     EmitCommaIfNeeded();
192     Print(FormatString(key));
193     Print(m_bPretty ? ": " : ":");
194     m_bWaitForValue = true;
195 }
196 
Add(bool bVal)197 void CPLJSonStreamingWriter::Add(bool bVal)
198 {
199     EmitCommaIfNeeded();
200     Print(bVal ? "true" : "false");
201 }
202 
Add(const std::string & str)203 void CPLJSonStreamingWriter::Add(const std::string& str)
204 {
205     EmitCommaIfNeeded();
206     Print(FormatString(str));
207 }
208 
Add(const char * pszStr)209 void CPLJSonStreamingWriter::Add(const char* pszStr)
210 {
211     EmitCommaIfNeeded();
212     Print(FormatString(pszStr));
213 }
214 
Add(GIntBig nVal)215 void CPLJSonStreamingWriter::Add(GIntBig nVal)
216 {
217     EmitCommaIfNeeded();
218     Print(CPLSPrintf(CPL_FRMT_GIB, nVal));
219 }
220 
Add(GUInt64 nVal)221 void CPLJSonStreamingWriter::Add(GUInt64 nVal)
222 {
223     EmitCommaIfNeeded();
224     Print(CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nVal)));
225 }
226 
Add(float fVal,int nPrecision)227 void CPLJSonStreamingWriter::Add(float fVal, int nPrecision)
228 {
229     EmitCommaIfNeeded();
230     if( CPLIsNan(fVal) )
231     {
232         Print("\"NaN\"");
233     }
234     else if( CPLIsInf(fVal) )
235     {
236         Print( fVal > 0 ? "\"Infinity\"" : "\"-Infinity\"" );
237     }
238     else
239     {
240         char szFormatting[10];
241         snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
242         Print(CPLSPrintf(szFormatting, fVal));
243     }
244 }
245 
Add(double dfVal,int nPrecision)246 void CPLJSonStreamingWriter::Add(double dfVal, int nPrecision)
247 {
248     EmitCommaIfNeeded();
249     if( CPLIsNan(dfVal) )
250     {
251        Print("\"NaN\"");
252     }
253     else if( CPLIsInf(dfVal) )
254     {
255         Print( dfVal > 0 ? "\"Infinity\"" : "\"-Infinity\"" );
256     }
257     else
258     {
259         char szFormatting[10];
260         snprintf(szFormatting, sizeof(szFormatting), "%%.%dg", nPrecision);
261         Print(CPLSPrintf(szFormatting, dfVal));
262     }
263 }
264 
AddNull()265 void CPLJSonStreamingWriter::AddNull()
266 {
267     EmitCommaIfNeeded();
268     Print("null");
269 }
270 
271 /*! @endcond */
272