blob: 81805c24dd90afee853e9650644fdf5fde7cd6dd [file] [log] [blame]
Baptiste Lepilleur7469f1d2010-04-20 21:35:19 +00001// Copyright 2007-2010 Baptiste Lepilleur
2// Distributed under MIT license, or public domain if desired and
3// recognized in your jurisdiction.
4// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5
Baptiste Lepilleurfa130ef2010-12-24 12:47:14 +00006/* This executable is used for testing parser/writer using real JSON files.
7 */
8
9
Christopher Dunnf9864232007-06-14 21:01:26 +000010#include <json/json.h>
11#include <algorithm> // sort
12#include <stdio.h>
13
14#if defined(_MSC_VER) && _MSC_VER >= 1310
15# pragma warning( disable: 4996 ) // disable fopen deprecation warning
16#endif
17
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000018static std::string
19normalizeFloatingPointStr( double value )
20{
21 char buffer[32];
Aaron Jacobsd2618802013-08-08 23:08:28 +000022#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
23 sprintf_s( buffer, sizeof(buffer), "%.16g", value );
24#else
Aaron Jacobs36400ac2013-08-08 00:39:32 +000025 snprintf( buffer, sizeof(buffer), "%.16g", value );
Aaron Jacobsd2618802013-08-08 23:08:28 +000026#endif
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000027 buffer[sizeof(buffer)-1] = 0;
28 std::string s( buffer );
29 std::string::size_type index = s.find_last_of( "eE" );
30 if ( index != std::string::npos )
31 {
32 std::string::size_type hasSign = (s[index+1] == '+' || s[index+1] == '-') ? 1 : 0;
33 std::string::size_type exponentStartIndex = index + 1 + hasSign;
34 std::string normalized = s.substr( 0, exponentStartIndex );
35 std::string::size_type indexDigit = s.find_first_not_of( '0', exponentStartIndex );
36 std::string exponent = "0";
37 if ( indexDigit != std::string::npos ) // There is an exponent different from 0
38 {
39 exponent = s.substr( indexDigit );
40 }
41 return normalized + exponent;
42 }
43 return s;
44}
45
46
Christopher Dunnf9864232007-06-14 21:01:26 +000047static std::string
48readInputTestFile( const char *path )
49{
50 FILE *file = fopen( path, "rb" );
51 if ( !file )
52 return std::string("");
53 fseek( file, 0, SEEK_END );
54 long size = ftell( file );
55 fseek( file, 0, SEEK_SET );
56 std::string text;
57 char *buffer = new char[size+1];
58 buffer[size] = 0;
59 if ( fread( buffer, 1, size, file ) == (unsigned long)size )
60 text = buffer;
61 fclose( file );
62 delete[] buffer;
63 return text;
64}
65
Christopher Dunnf9864232007-06-14 21:01:26 +000066static void
67printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." )
68{
69 switch ( value.type() )
70 {
71 case Json::nullValue:
72 fprintf( fout, "%s=null\n", path.c_str() );
73 break;
74 case Json::intValue:
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +000075 fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestInt() ).c_str() );
Christopher Dunnf9864232007-06-14 21:01:26 +000076 break;
77 case Json::uintValue:
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +000078 fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestUInt() ).c_str() );
Christopher Dunnf9864232007-06-14 21:01:26 +000079 break;
80 case Json::realValue:
Baptiste Lepilleurf0b24e72011-05-26 20:14:32 +000081 fprintf( fout, "%s=%s\n", path.c_str(), normalizeFloatingPointStr(value.asDouble()).c_str() );
Christopher Dunnf9864232007-06-14 21:01:26 +000082 break;
83 case Json::stringValue:
84 fprintf( fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str() );
85 break;
86 case Json::booleanValue:
87 fprintf( fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false" );
88 break;
89 case Json::arrayValue:
90 {
91 fprintf( fout, "%s=[]\n", path.c_str() );
92 int size = value.size();
93 for ( int index =0; index < size; ++index )
94 {
95 static char buffer[16];
Aaron Jacobsd2618802013-08-08 23:08:28 +000096#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
97 sprintf_s( buffer, sizeof(buffer), "[%d]", index );
98#else
Aaron Jacobs36400ac2013-08-08 00:39:32 +000099 snprintf( buffer, sizeof(buffer), "[%d]", index );
Aaron Jacobsd2618802013-08-08 23:08:28 +0000100#endif
Christopher Dunnf9864232007-06-14 21:01:26 +0000101 printValueTree( fout, value[index], path + buffer );
102 }
103 }
104 break;
105 case Json::objectValue:
106 {
107 fprintf( fout, "%s={}\n", path.c_str() );
108 Json::Value::Members members( value.getMemberNames() );
109 std::sort( members.begin(), members.end() );
110 std::string suffix = *(path.end()-1) == '.' ? "" : ".";
111 for ( Json::Value::Members::iterator it = members.begin();
112 it != members.end();
113 ++it )
114 {
115 const std::string &name = *it;
116 printValueTree( fout, value[name], path + suffix + name );
117 }
118 }
119 break;
120 default:
121 break;
122 }
123}
124
125
126static int
127parseAndSaveValueTree( const std::string &input,
128 const std::string &actual,
129 const std::string &kind,
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000130 Json::Value &root,
131 const Json::Features &features,
132 bool parseOnly )
Christopher Dunnf9864232007-06-14 21:01:26 +0000133{
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000134 Json::Reader reader( features );
Christopher Dunnf9864232007-06-14 21:01:26 +0000135 bool parsingSuccessful = reader.parse( input, root );
136 if ( !parsingSuccessful )
137 {
138 printf( "Failed to parse %s file: \n%s\n",
139 kind.c_str(),
Baptiste Lepilleurb2e8ccc2011-05-01 16:27:55 +0000140 reader.getFormattedErrorMessages().c_str() );
Christopher Dunnf9864232007-06-14 21:01:26 +0000141 return 1;
142 }
143
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000144 if ( !parseOnly )
Christopher Dunnf9864232007-06-14 21:01:26 +0000145 {
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000146 FILE *factual = fopen( actual.c_str(), "wt" );
147 if ( !factual )
148 {
149 printf( "Failed to create %s actual file.\n", kind.c_str() );
150 return 2;
151 }
152 printValueTree( factual, root );
153 fclose( factual );
Christopher Dunnf9864232007-06-14 21:01:26 +0000154 }
Christopher Dunnf9864232007-06-14 21:01:26 +0000155 return 0;
156}
157
158
159static int
160rewriteValueTree( const std::string &rewritePath,
161 const Json::Value &root,
162 std::string &rewrite )
163{
164 //Json::FastWriter writer;
165 //writer.enableYAMLCompatibility();
166 Json::StyledWriter writer;
167 rewrite = writer.write( root );
168 FILE *fout = fopen( rewritePath.c_str(), "wt" );
169 if ( !fout )
170 {
171 printf( "Failed to create rewrite file: %s\n", rewritePath.c_str() );
172 return 2;
173 }
174 fprintf( fout, "%s\n", rewrite.c_str() );
175 fclose( fout );
176 return 0;
177}
178
179
180static std::string
181removeSuffix( const std::string &path,
182 const std::string &extension )
183{
184 if ( extension.length() >= path.length() )
185 return std::string("");
186 std::string suffix = path.substr( path.length() - extension.length() );
187 if ( suffix != extension )
188 return std::string("");
189 return path.substr( 0, path.length() - extension.length() );
190}
191
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000192
193static void
194printConfig()
195{
196 // Print the configuration used to compile JsonCpp
197#if defined(JSON_NO_INT64)
198 printf( "JSON_NO_INT64=1\n" );
199#else
200 printf( "JSON_NO_INT64=0\n" );
201#endif
202}
203
204
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000205static int
206printUsage( const char *argv[] )
Christopher Dunnf9864232007-06-14 21:01:26 +0000207{
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000208 printf( "Usage: %s [--strict] input-json-file", argv[0] );
209 return 3;
210}
211
212
213int
214parseCommandLine( int argc, const char *argv[],
215 Json::Features &features, std::string &path,
216 bool &parseOnly )
217{
218 parseOnly = false;
219 if ( argc < 2 )
Christopher Dunnf9864232007-06-14 21:01:26 +0000220 {
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000221 return printUsage( argv );
Christopher Dunnf9864232007-06-14 21:01:26 +0000222 }
223
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000224 int index = 1;
225 if ( std::string(argv[1]) == "--json-checker" )
226 {
227 features = Json::Features::strictMode();
228 parseOnly = true;
229 ++index;
230 }
231
Baptiste Lepilleur201fb2c2010-04-19 07:37:41 +0000232 if ( std::string(argv[1]) == "--json-config" )
233 {
234 printConfig();
235 return 3;
236 }
237
Baptiste Lepilleur88681472009-11-18 21:38:54 +0000238 if ( index == argc || index + 1 < argc )
239 {
240 return printUsage( argv );
241 }
242
243 path = argv[index];
244 return 0;
245}
246
247
248int main( int argc, const char *argv[] )
249{
250 std::string path;
251 Json::Features features;
252 bool parseOnly;
253 int exitCode = parseCommandLine( argc, argv, features, path, parseOnly );
254 if ( exitCode != 0 )
255 {
256 return exitCode;
257 }
258
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +0000259 try
Christopher Dunnf9864232007-06-14 21:01:26 +0000260 {
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +0000261 std::string input = readInputTestFile( path.c_str() );
262 if ( input.empty() )
Christopher Dunnf9864232007-06-14 21:01:26 +0000263 {
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +0000264 printf( "Failed to read input or empty input: %s\n", path.c_str() );
265 return 3;
Christopher Dunnf9864232007-06-14 21:01:26 +0000266 }
Baptiste Lepilleur842e9ac2010-12-27 17:45:23 +0000267
268 std::string basePath = removeSuffix( argv[1], ".json" );
269 if ( !parseOnly && basePath.empty() )
270 {
271 printf( "Bad input path. Path does not end with '.expected':\n%s\n", path.c_str() );
272 return 3;
273 }
274
275 std::string actualPath = basePath + ".actual";
276 std::string rewritePath = basePath + ".rewrite";
277 std::string rewriteActualPath = basePath + ".actual-rewrite";
278
279 Json::Value root;
280 exitCode = parseAndSaveValueTree( input, actualPath, "input", root, features, parseOnly );
281 if ( exitCode == 0 && !parseOnly )
282 {
283 std::string rewrite;
284 exitCode = rewriteValueTree( rewritePath, root, rewrite );
285 if ( exitCode == 0 )
286 {
287 Json::Value rewriteRoot;
288 exitCode = parseAndSaveValueTree( rewrite, rewriteActualPath,
289 "rewrite", rewriteRoot, features, parseOnly );
290 }
291 }
292 }
293 catch ( const std::exception &e )
294 {
295 printf( "Unhandled exception:\n%s\n", e.what() );
296 exitCode = 1;
Christopher Dunnf9864232007-06-14 21:01:26 +0000297 }
298
299 return exitCode;
300}
Christopher Dunn77cd8382014-04-19 21:41:03 +0000301// vim: et ts=4 sts=4 sw=4 tw=0