The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

634 lines
19KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. class JSONParser
  19. {
  20. public:
  21. static Result parseAny (String::CharPointerType& t, var& result)
  22. {
  23. t = t.findEndOfWhitespace();
  24. String::CharPointerType t2 (t);
  25. switch (t2.getAndAdvance())
  26. {
  27. case '{': t = t2; return parseObject (t, result);
  28. case '[': t = t2; return parseArray (t, result);
  29. case '"': t = t2; return parseString (t, result);
  30. case '-':
  31. t2 = t2.findEndOfWhitespace();
  32. if (! CharacterFunctions::isDigit (*t2))
  33. break;
  34. t = t2;
  35. return parseNumber (t, result, true);
  36. case '0': case '1': case '2': case '3': case '4':
  37. case '5': case '6': case '7': case '8': case '9':
  38. return parseNumber (t, result, false);
  39. case 't': // "true"
  40. if (t2.getAndAdvance() == 'r' && t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'e')
  41. {
  42. t = t2;
  43. result = var (true);
  44. return Result::ok();
  45. }
  46. break;
  47. case 'f': // "false"
  48. if (t2.getAndAdvance() == 'a' && t2.getAndAdvance() == 'l'
  49. && t2.getAndAdvance() == 's' && t2.getAndAdvance() == 'e')
  50. {
  51. t = t2;
  52. result = var (false);
  53. return Result::ok();
  54. }
  55. break;
  56. case 'n': // "null"
  57. if (t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'l' && t2.getAndAdvance() == 'l')
  58. {
  59. t = t2;
  60. result = var::null;
  61. return Result::ok();
  62. }
  63. break;
  64. default:
  65. break;
  66. }
  67. return createFail ("Syntax error", &t);
  68. }
  69. private:
  70. static Result createFail (const char* const message, const String::CharPointerType* location = nullptr)
  71. {
  72. String m (message);
  73. if (location != nullptr)
  74. m << ": \"" << String (*location, 20) << '"';
  75. return Result::fail (m);
  76. }
  77. static Result parseNumber (String::CharPointerType& t, var& result, const bool isNegative)
  78. {
  79. String::CharPointerType oldT (t);
  80. int64 intValue = t.getAndAdvance() - '0';
  81. jassert (intValue >= 0 && intValue < 10);
  82. for (;;)
  83. {
  84. String::CharPointerType previousChar (t);
  85. const juce_wchar c = t.getAndAdvance();
  86. const int digit = ((int) c) - '0';
  87. if (isPositiveAndBelow (digit, 10))
  88. {
  89. intValue = intValue * 10 + digit;
  90. continue;
  91. }
  92. if (c == 'e' || c == 'E' || c == '.')
  93. {
  94. t = oldT;
  95. const double asDouble = CharacterFunctions::readDoubleValue (t);
  96. result = isNegative ? -asDouble : asDouble;
  97. return Result::ok();
  98. }
  99. if (CharacterFunctions::isWhitespace (c)
  100. || c == ',' || c == '}' || c == ']' || c == 0)
  101. {
  102. t = previousChar;
  103. break;
  104. }
  105. return createFail ("Syntax error in number", &oldT);
  106. }
  107. const int64 correctedValue = isNegative ? -intValue : intValue;
  108. if ((intValue >> 31) != 0)
  109. result = correctedValue;
  110. else
  111. result = (int) correctedValue;
  112. return Result::ok();
  113. }
  114. static Result parseObject (String::CharPointerType& t, var& result)
  115. {
  116. DynamicObject* const resultObject = new DynamicObject();
  117. result = resultObject;
  118. NamedValueSet& resultProperties = resultObject->getProperties();
  119. for (;;)
  120. {
  121. t = t.findEndOfWhitespace();
  122. String::CharPointerType oldT (t);
  123. const juce_wchar c = t.getAndAdvance();
  124. if (c == '}')
  125. break;
  126. if (c == 0)
  127. return createFail ("Unexpected end-of-input in object declaration");
  128. if (c == '"')
  129. {
  130. var propertyNameVar;
  131. Result r (parseString (t, propertyNameVar));
  132. if (r.failed())
  133. return r;
  134. const String propertyName (propertyNameVar.toString());
  135. if (propertyName.isNotEmpty())
  136. {
  137. t = t.findEndOfWhitespace();
  138. oldT = t;
  139. const juce_wchar c2 = t.getAndAdvance();
  140. if (c2 != ':')
  141. return createFail ("Expected ':', but found", &oldT);
  142. resultProperties.set (propertyName, var::null);
  143. var* propertyValue = resultProperties.getVarPointer (propertyName);
  144. Result r2 (parseAny (t, *propertyValue));
  145. if (r2.failed())
  146. return r2;
  147. t = t.findEndOfWhitespace();
  148. oldT = t;
  149. const juce_wchar nextChar = t.getAndAdvance();
  150. if (nextChar == ',')
  151. continue;
  152. else if (nextChar == '}')
  153. break;
  154. }
  155. }
  156. return createFail ("Expected object member declaration, but found", &oldT);
  157. }
  158. return Result::ok();
  159. }
  160. static Result parseArray (String::CharPointerType& t, var& result)
  161. {
  162. result = var (Array<var>());
  163. Array<var>* const destArray = result.getArray();
  164. for (;;)
  165. {
  166. t = t.findEndOfWhitespace();
  167. String::CharPointerType oldT (t);
  168. const juce_wchar c = t.getAndAdvance();
  169. if (c == ']')
  170. break;
  171. if (c == 0)
  172. return createFail ("Unexpected end-of-input in array declaration");
  173. t = oldT;
  174. destArray->add (var::null);
  175. Result r (parseAny (t, destArray->getReference (destArray->size() - 1)));
  176. if (r.failed())
  177. return r;
  178. t = t.findEndOfWhitespace();
  179. oldT = t;
  180. const juce_wchar nextChar = t.getAndAdvance();
  181. if (nextChar == ',')
  182. continue;
  183. else if (nextChar == ']')
  184. break;
  185. return createFail ("Expected object array item, but found", &oldT);
  186. }
  187. return Result::ok();
  188. }
  189. static Result parseString (String::CharPointerType& t, var& result)
  190. {
  191. Array<juce_wchar> buffer;
  192. buffer.ensureStorageAllocated (256);
  193. for (;;)
  194. {
  195. juce_wchar c = t.getAndAdvance();
  196. if (c == '"')
  197. break;
  198. if (c == '\\')
  199. {
  200. c = t.getAndAdvance();
  201. switch (c)
  202. {
  203. case '"':
  204. case '\\':
  205. case '/': break;
  206. case 'b': c = '\b'; break;
  207. case 'f': c = '\f'; break;
  208. case 'n': c = '\n'; break;
  209. case 'r': c = '\r'; break;
  210. case 't': c = '\t'; break;
  211. case 'u':
  212. {
  213. c = 0;
  214. for (int i = 4; --i >= 0;)
  215. {
  216. const int digitValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance());
  217. if (digitValue < 0)
  218. return createFail ("Syntax error in unicode escape sequence");
  219. c = (juce_wchar) ((c << 4) + digitValue);
  220. }
  221. break;
  222. }
  223. }
  224. }
  225. if (c == 0)
  226. return createFail ("Unexpected end-of-input in string constant");
  227. buffer.add (c);
  228. }
  229. buffer.add (0);
  230. result = String (CharPointer_UTF32 (buffer.getRawDataPointer()));
  231. return Result::ok();
  232. }
  233. };
  234. //==============================================================================
  235. class JSONFormatter
  236. {
  237. public:
  238. static void write (OutputStream& out, const var& v,
  239. const int indentLevel, const bool allOnOneLine)
  240. {
  241. if (v.isString())
  242. {
  243. writeString (out, v.toString().getCharPointer());
  244. }
  245. else if (v.isVoid())
  246. {
  247. out << "null";
  248. }
  249. else if (v.isBool())
  250. {
  251. out << (static_cast<bool> (v) ? "true" : "false");
  252. }
  253. else if (v.isArray())
  254. {
  255. writeArray (out, *v.getArray(), indentLevel, allOnOneLine);
  256. }
  257. else if (v.isObject())
  258. {
  259. if (DynamicObject* const object = v.getDynamicObject())
  260. writeObject (out, *object, indentLevel, allOnOneLine);
  261. else
  262. jassertfalse; // Only DynamicObjects can be converted to JSON!
  263. }
  264. else
  265. {
  266. jassert (! v.isMethod()); // Can't convert an object with methods to JSON!
  267. out << v.toString();
  268. }
  269. }
  270. private:
  271. enum { indentSize = 2 };
  272. static void writeEscapedChar (OutputStream& out, const unsigned short value)
  273. {
  274. out << "\\u" << String::toHexString ((int) value).paddedLeft ('0', 4);
  275. }
  276. static void writeString (OutputStream& out, String::CharPointerType t)
  277. {
  278. out << '"';
  279. for (;;)
  280. {
  281. const juce_wchar c (t.getAndAdvance());
  282. switch (c)
  283. {
  284. case 0: out << '"'; return;
  285. case '\"': out << "\\\""; break;
  286. case '\\': out << "\\\\"; break;
  287. case '\b': out << "\\b"; break;
  288. case '\f': out << "\\f"; break;
  289. case '\t': out << "\\t"; break;
  290. case '\r': out << "\\r"; break;
  291. case '\n': out << "\\n"; break;
  292. default:
  293. if (c >= 32 && c < 127)
  294. {
  295. out << (char) c;
  296. }
  297. else
  298. {
  299. if (CharPointer_UTF16::getBytesRequiredFor (c) > 2)
  300. {
  301. CharPointer_UTF16::CharType chars[2];
  302. CharPointer_UTF16 utf16 (chars);
  303. utf16.write (c);
  304. for (int i = 0; i < 2; ++i)
  305. writeEscapedChar (out, (unsigned short) chars[i]);
  306. }
  307. else
  308. {
  309. writeEscapedChar (out, (unsigned short) c);
  310. }
  311. }
  312. break;
  313. }
  314. }
  315. }
  316. static void writeSpaces (OutputStream& out, int numSpaces)
  317. {
  318. out.writeRepeatedByte (' ', (size_t) numSpaces);
  319. }
  320. static void writeArray (OutputStream& out, const Array<var>& array,
  321. const int indentLevel, const bool allOnOneLine)
  322. {
  323. out << '[';
  324. if (! allOnOneLine)
  325. out << newLine;
  326. for (int i = 0; i < array.size(); ++i)
  327. {
  328. if (! allOnOneLine)
  329. writeSpaces (out, indentLevel + indentSize);
  330. write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine);
  331. if (i < array.size() - 1)
  332. {
  333. if (allOnOneLine)
  334. out << ", ";
  335. else
  336. out << ',' << newLine;
  337. }
  338. else if (! allOnOneLine)
  339. out << newLine;
  340. }
  341. if (! allOnOneLine)
  342. writeSpaces (out, indentLevel);
  343. out << ']';
  344. }
  345. static void writeObject (OutputStream& out, DynamicObject& object,
  346. const int indentLevel, const bool allOnOneLine)
  347. {
  348. NamedValueSet& props = object.getProperties();
  349. out << '{';
  350. if (! allOnOneLine)
  351. out << newLine;
  352. LinkedListPointer<NamedValueSet::NamedValue>* i = &(props.values);
  353. for (;;)
  354. {
  355. NamedValueSet::NamedValue* const v = i->get();
  356. if (v == nullptr)
  357. break;
  358. if (! allOnOneLine)
  359. writeSpaces (out, indentLevel + indentSize);
  360. writeString (out, v->name);
  361. out << ": ";
  362. write (out, v->value, indentLevel + indentSize, allOnOneLine);
  363. if (v->nextListItem.get() != nullptr)
  364. {
  365. if (allOnOneLine)
  366. out << ", ";
  367. else
  368. out << ',' << newLine;
  369. }
  370. else if (! allOnOneLine)
  371. out << newLine;
  372. i = &(v->nextListItem);
  373. }
  374. if (! allOnOneLine)
  375. writeSpaces (out, indentLevel);
  376. out << '}';
  377. }
  378. };
  379. //==============================================================================
  380. var JSON::parse (const String& text)
  381. {
  382. var result;
  383. String::CharPointerType t (text.getCharPointer());
  384. if (! JSONParser::parseAny (t, result))
  385. result = var::null;
  386. return result;
  387. }
  388. var JSON::parse (InputStream& input)
  389. {
  390. return parse (input.readEntireStreamAsString());
  391. }
  392. var JSON::parse (const File& file)
  393. {
  394. return parse (file.loadFileAsString());
  395. }
  396. Result JSON::parse (const String& text, var& result)
  397. {
  398. String::CharPointerType t (text.getCharPointer());
  399. return JSONParser::parseAny (t, result);
  400. }
  401. String JSON::toString (const var& data, const bool allOnOneLine)
  402. {
  403. MemoryOutputStream mo (1024);
  404. JSONFormatter::write (mo, data, 0, allOnOneLine);
  405. return mo.toString();
  406. }
  407. void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine)
  408. {
  409. JSONFormatter::write (output, data, 0, allOnOneLine);
  410. }
  411. //==============================================================================
  412. //==============================================================================
  413. #if JUCE_UNIT_TESTS
  414. class JSONTests : public UnitTest
  415. {
  416. public:
  417. JSONTests() : UnitTest ("JSON") {}
  418. static String createRandomWideCharString (Random& r)
  419. {
  420. juce_wchar buffer[40] = { 0 };
  421. for (int i = 0; i < numElementsInArray (buffer) - 1; ++i)
  422. {
  423. if (r.nextBool())
  424. {
  425. do
  426. {
  427. buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
  428. }
  429. while (! CharPointer_UTF16::canRepresent (buffer[i]));
  430. }
  431. else
  432. buffer[i] = (juce_wchar) (1 + r.nextInt (0xff));
  433. }
  434. return CharPointer_UTF32 (buffer);
  435. }
  436. static String createRandomIdentifier (Random& r)
  437. {
  438. char buffer[30] = { 0 };
  439. for (int i = 0; i < numElementsInArray (buffer) - 1; ++i)
  440. {
  441. static const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
  442. buffer[i] = chars [r.nextInt (sizeof (chars) - 1)];
  443. }
  444. return CharPointer_ASCII (buffer);
  445. }
  446. static var createRandomVar (Random& r, int depth)
  447. {
  448. switch (r.nextInt (depth > 3 ? 6 : 8))
  449. {
  450. case 0: return var::null;
  451. case 1: return r.nextInt();
  452. case 2: return r.nextInt64();
  453. case 3: return r.nextBool();
  454. case 4: return r.nextDouble();
  455. case 5: return createRandomWideCharString (r);
  456. case 6:
  457. {
  458. var v (createRandomVar (r, depth + 1));
  459. for (int i = 1 + r.nextInt (30); --i >= 0;)
  460. v.append (createRandomVar (r, depth + 1));
  461. return v;
  462. }
  463. case 7:
  464. {
  465. DynamicObject* o = new DynamicObject();
  466. for (int i = r.nextInt (30); --i >= 0;)
  467. o->setProperty (createRandomIdentifier (r), createRandomVar (r, depth + 1));
  468. return o;
  469. }
  470. default:
  471. return var::null;
  472. }
  473. }
  474. void runTest()
  475. {
  476. beginTest ("JSON");
  477. Random r;
  478. r.setSeedRandomly();
  479. expect (JSON::parse (String::empty) == var::null);
  480. expect (JSON::parse ("{}").isObject());
  481. expect (JSON::parse ("[]").isArray());
  482. expect (JSON::parse ("1234").isInt());
  483. expect (JSON::parse ("12345678901234").isInt64());
  484. expect (JSON::parse ("1.123e3").isDouble());
  485. expect (JSON::parse ("-1234").isInt());
  486. expect (JSON::parse ("-12345678901234").isInt64());
  487. expect (JSON::parse ("-1.123e3").isDouble());
  488. for (int i = 100; --i >= 0;)
  489. {
  490. var v;
  491. if (i > 0)
  492. v = createRandomVar (r, 0);
  493. const bool oneLine = r.nextBool();
  494. String asString (JSON::toString (v, oneLine));
  495. var parsed = JSON::parse (asString);
  496. String parsedString (JSON::toString (parsed, oneLine));
  497. expect (asString.isNotEmpty() && parsedString == asString);
  498. }
  499. }
  500. };
  501. static JSONTests JSONUnitTests;
  502. #endif