1// 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
6/* This executable is used for testing parser/writer using real JSON files.
7 */
8
9
10#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
18static std::string
19normalizeFloatingPointStr( double value )
20{
21    char buffer[32];
22    sprintf( buffer, "%.16g", value );
23    buffer[sizeof(buffer)-1] = 0;
24    std::string s( buffer );
25    std::string::size_type index = s.find_last_of( "eE" );
26    if ( index != std::string::npos )
27    {
28        std::string::size_type hasSign = (s[index+1] == '+' || s[index+1] == '-') ? 1 : 0;
29        std::string::size_type exponentStartIndex = index + 1 + hasSign;
30        std::string normalized = s.substr( 0, exponentStartIndex );
31        std::string::size_type indexDigit = s.find_first_not_of( '0', exponentStartIndex );
32        std::string exponent = "0";
33        if ( indexDigit != std::string::npos ) // There is an exponent different from 0
34        {
35            exponent = s.substr( indexDigit );
36        }
37        return normalized + exponent;
38    }
39    return s;
40}
41
42
43static std::string
44readInputTestFile( const char *path )
45{
46   FILE *file = fopen( path, "rb" );
47   if ( !file )
48      return std::string("");
49   fseek( file, 0, SEEK_END );
50   long size = ftell( file );
51   fseek( file, 0, SEEK_SET );
52   std::string text;
53   char *buffer = new char[size+1];
54   buffer[size] = 0;
55   if ( fread( buffer, 1, size, file ) == (unsigned long)size )
56      text = buffer;
57   fclose( file );
58   delete[] buffer;
59   return text;
60}
61
62static void
63printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." )
64{
65   switch ( value.type() )
66   {
67   case Json::nullValue:
68      fprintf( fout, "%s=null\n", path.c_str() );
69      break;
70   case Json::intValue:
71      fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestInt() ).c_str() );
72      break;
73   case Json::uintValue:
74      fprintf( fout, "%s=%s\n", path.c_str(), Json::valueToString( value.asLargestUInt() ).c_str() );
75      break;
76   case Json::realValue:
77       fprintf( fout, "%s=%s\n", path.c_str(), normalizeFloatingPointStr(value.asDouble()).c_str() );
78      break;
79   case Json::stringValue:
80      fprintf( fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str() );
81      break;
82   case Json::booleanValue:
83      fprintf( fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false" );
84      break;
85   case Json::arrayValue:
86      {
87         fprintf( fout, "%s=[]\n", path.c_str() );
88         int size = value.size();
89         for ( int index =0; index < size; ++index )
90         {
91            static char buffer[16];
92            sprintf( buffer, "[%d]", index );
93            printValueTree( fout, value[index], path + buffer );
94         }
95      }
96      break;
97   case Json::objectValue:
98      {
99         fprintf( fout, "%s={}\n", path.c_str() );
100         Json::Value::Members members( value.getMemberNames() );
101         std::sort( members.begin(), members.end() );
102         std::string suffix = *(path.end()-1) == '.' ? "" : ".";
103         for ( Json::Value::Members::iterator it = members.begin();
104               it != members.end();
105               ++it )
106         {
107            const std::string &name = *it;
108            printValueTree( fout, value[name], path + suffix + name );
109         }
110      }
111      break;
112   default:
113      break;
114   }
115}
116
117
118static int
119parseAndSaveValueTree( const std::string &input,
120                       const std::string &actual,
121                       const std::string &kind,
122                       Json::Value &root,
123                       const Json::Features &features,
124                       bool parseOnly )
125{
126   Json::Reader reader( features );
127   bool parsingSuccessful = reader.parse( input, root );
128   if ( !parsingSuccessful )
129   {
130      printf( "Failed to parse %s file: \n%s\n",
131              kind.c_str(),
132              reader.getFormattedErrorMessages().c_str() );
133      return 1;
134   }
135
136   if ( !parseOnly )
137   {
138      FILE *factual = fopen( actual.c_str(), "wt" );
139      if ( !factual )
140      {
141         printf( "Failed to create %s actual file.\n", kind.c_str() );
142         return 2;
143      }
144      printValueTree( factual, root );
145      fclose( factual );
146   }
147   return 0;
148}
149
150
151static int
152rewriteValueTree( const std::string &rewritePath,
153                  const Json::Value &root,
154                  std::string &rewrite )
155{
156   //Json::FastWriter writer;
157   //writer.enableYAMLCompatibility();
158   Json::StyledWriter writer;
159   rewrite = writer.write( root );
160   FILE *fout = fopen( rewritePath.c_str(), "wt" );
161   if ( !fout )
162   {
163      printf( "Failed to create rewrite file: %s\n", rewritePath.c_str() );
164      return 2;
165   }
166   fprintf( fout, "%s\n", rewrite.c_str() );
167   fclose( fout );
168   return 0;
169}
170
171
172static std::string
173removeSuffix( const std::string &path,
174              const std::string &extension )
175{
176   if ( extension.length() >= path.length() )
177      return std::string("");
178   std::string suffix = path.substr( path.length() - extension.length() );
179   if ( suffix != extension )
180      return std::string("");
181   return path.substr( 0, path.length() - extension.length() );
182}
183
184
185static void
186printConfig()
187{
188   // Print the configuration used to compile JsonCpp
189#if defined(JSON_NO_INT64)
190   printf( "JSON_NO_INT64=1\n" );
191#else
192   printf( "JSON_NO_INT64=0\n" );
193#endif
194}
195
196
197static int
198printUsage( const char *argv[] )
199{
200   printf( "Usage: %s [--strict] input-json-file", argv[0] );
201   return 3;
202}
203
204
205int
206parseCommandLine( int argc, const char *argv[],
207                  Json::Features &features, std::string &path,
208                  bool &parseOnly )
209{
210   parseOnly = false;
211   if ( argc < 2 )
212   {
213      return printUsage( argv );
214   }
215
216   int index = 1;
217   if ( std::string(argv[1]) == "--json-checker" )
218   {
219      features = Json::Features::strictMode();
220      parseOnly = true;
221      ++index;
222   }
223
224   if ( std::string(argv[1]) == "--json-config" )
225   {
226      printConfig();
227      return 3;
228   }
229
230   if ( index == argc  ||  index + 1 < argc )
231   {
232      return printUsage( argv );
233   }
234
235   path = argv[index];
236   return 0;
237}
238
239
240int main( int argc, const char *argv[] )
241{
242   std::string path;
243   Json::Features features;
244   bool parseOnly;
245   int exitCode = parseCommandLine( argc, argv, features, path, parseOnly );
246   if ( exitCode != 0 )
247   {
248      return exitCode;
249   }
250
251   try
252   {
253      std::string input = readInputTestFile( path.c_str() );
254      if ( input.empty() )
255      {
256         printf( "Failed to read input or empty input: %s\n", path.c_str() );
257         return 3;
258      }
259
260      std::string basePath = removeSuffix( argv[1], ".json" );
261      if ( !parseOnly  &&  basePath.empty() )
262      {
263         printf( "Bad input path. Path does not end with '.expected':\n%s\n", path.c_str() );
264         return 3;
265      }
266
267      std::string actualPath = basePath + ".actual";
268      std::string rewritePath = basePath + ".rewrite";
269      std::string rewriteActualPath = basePath + ".actual-rewrite";
270
271      Json::Value root;
272      exitCode = parseAndSaveValueTree( input, actualPath, "input", root, features, parseOnly );
273      if ( exitCode == 0  &&  !parseOnly )
274      {
275         std::string rewrite;
276         exitCode = rewriteValueTree( rewritePath, root, rewrite );
277         if ( exitCode == 0 )
278         {
279            Json::Value rewriteRoot;
280            exitCode = parseAndSaveValueTree( rewrite, rewriteActualPath,
281               "rewrite", rewriteRoot, features, parseOnly );
282         }
283      }
284   }
285   catch ( const std::exception &e )
286   {
287      printf( "Unhandled exception:\n%s\n", e.what() );
288      exitCode = 1;
289   }
290
291   return exitCode;
292}
293
294