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.

635 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. DynamicObject* const object = v.getDynamicObject();
  260. jassert (object != nullptr); // Only DynamicObjects can be converted to JSON!
  261. writeObject (out, *object, indentLevel, allOnOneLine);
  262. }
  263. else
  264. {
  265. jassert (! v.isMethod()); // Can't convert an object with methods to JSON!
  266. out << v.toString();
  267. }
  268. }
  269. private:
  270. enum { indentSize = 2 };
  271. static void writeEscapedChar (OutputStream& out, const unsigned short value)
  272. {
  273. out << "\\u" << String::toHexString ((int) value).paddedLeft ('0', 4);
  274. }
  275. static void writeString (OutputStream& out, String::CharPointerType t)
  276. {
  277. out << '"';
  278. for (;;)
  279. {
  280. const juce_wchar c (t.getAndAdvance());
  281. switch (c)
  282. {
  283. case 0: out << '"'; return;
  284. case '\"': out << "\\\""; break;
  285. case '\\': out << "\\\\"; break;
  286. case '\b': out << "\\b"; break;
  287. case '\f': out << "\\f"; break;
  288. case '\t': out << "\\t"; break;
  289. case '\r': out << "\\r"; break;
  290. case '\n': out << "\\n"; break;
  291. default:
  292. if (c >= 32 && c < 127)
  293. {
  294. out << (char) c;
  295. }
  296. else
  297. {
  298. if (CharPointer_UTF16::getBytesRequiredFor (c) > 2)
  299. {
  300. CharPointer_UTF16::CharType chars[2];
  301. CharPointer_UTF16 utf16 (chars);
  302. utf16.write (c);
  303. for (int i = 0; i < 2; ++i)
  304. writeEscapedChar (out, (unsigned short) chars[i]);
  305. }
  306. else
  307. {
  308. writeEscapedChar (out, (unsigned short) c);
  309. }
  310. }
  311. break;
  312. }
  313. }
  314. }
  315. static void writeSpaces (OutputStream& out, int numSpaces)
  316. {
  317. out.writeRepeatedByte (' ', numSpaces);
  318. }
  319. static void writeArray (OutputStream& out, const Array<var>& array,
  320. const int indentLevel, const bool allOnOneLine)
  321. {
  322. out << '[';
  323. if (! allOnOneLine)
  324. out << newLine;
  325. for (int i = 0; i < array.size(); ++i)
  326. {
  327. if (! allOnOneLine)
  328. writeSpaces (out, indentLevel + indentSize);
  329. write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine);
  330. if (i < array.size() - 1)
  331. {
  332. if (allOnOneLine)
  333. out << ", ";
  334. else
  335. out << ',' << newLine;
  336. }
  337. else if (! allOnOneLine)
  338. out << newLine;
  339. }
  340. if (! allOnOneLine)
  341. writeSpaces (out, indentLevel);
  342. out << ']';
  343. }
  344. static void writeObject (OutputStream& out, DynamicObject& object,
  345. const int indentLevel, const bool allOnOneLine)
  346. {
  347. NamedValueSet& props = object.getProperties();
  348. out << '{';
  349. if (! allOnOneLine)
  350. out << newLine;
  351. LinkedListPointer<NamedValueSet::NamedValue>* i = &(props.values);
  352. for (;;)
  353. {
  354. NamedValueSet::NamedValue* const v = i->get();
  355. if (v == nullptr)
  356. break;
  357. if (! allOnOneLine)
  358. writeSpaces (out, indentLevel + indentSize);
  359. writeString (out, v->name);
  360. out << ": ";
  361. write (out, v->value, indentLevel + indentSize, allOnOneLine);
  362. if (v->nextListItem.get() != nullptr)
  363. {
  364. if (allOnOneLine)
  365. out << ", ";
  366. else
  367. out << ',' << newLine;
  368. }
  369. else if (! allOnOneLine)
  370. out << newLine;
  371. i = &(v->nextListItem);
  372. }
  373. if (! allOnOneLine)
  374. writeSpaces (out, indentLevel);
  375. out << '}';
  376. }
  377. };
  378. //==============================================================================
  379. var JSON::parse (const String& text)
  380. {
  381. var result;
  382. String::CharPointerType t (text.getCharPointer());
  383. if (! JSONParser::parseAny (t, result))
  384. result = var::null;
  385. return result;
  386. }
  387. var JSON::parse (InputStream& input)
  388. {
  389. return parse (input.readEntireStreamAsString());
  390. }
  391. var JSON::parse (const File& file)
  392. {
  393. return parse (file.loadFileAsString());
  394. }
  395. Result JSON::parse (const String& text, var& result)
  396. {
  397. String::CharPointerType t (text.getCharPointer());
  398. return JSONParser::parseAny (t, result);
  399. }
  400. String JSON::toString (const var& data, const bool allOnOneLine)
  401. {
  402. MemoryOutputStream mo (1024);
  403. JSONFormatter::write (mo, data, 0, allOnOneLine);
  404. return mo.toString();
  405. }
  406. void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine)
  407. {
  408. JSONFormatter::write (output, data, 0, allOnOneLine);
  409. }
  410. //==============================================================================
  411. //==============================================================================
  412. #if JUCE_UNIT_TESTS
  413. class JSONTests : public UnitTest
  414. {
  415. public:
  416. JSONTests() : UnitTest ("JSON") {}
  417. static String createRandomWideCharString (Random& r)
  418. {
  419. juce_wchar buffer[40] = { 0 };
  420. for (int i = 0; i < numElementsInArray (buffer) - 1; ++i)
  421. {
  422. if (r.nextBool())
  423. {
  424. do
  425. {
  426. buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
  427. }
  428. while (! CharPointer_UTF16::canRepresent (buffer[i]));
  429. }
  430. else
  431. buffer[i] = (juce_wchar) (1 + r.nextInt (0xff));
  432. }
  433. return CharPointer_UTF32 (buffer);
  434. }
  435. static String createRandomIdentifier (Random& r)
  436. {
  437. char buffer[30] = { 0 };
  438. for (int i = 0; i < numElementsInArray (buffer) - 1; ++i)
  439. {
  440. static const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
  441. buffer[i] = chars [r.nextInt (sizeof (chars) - 1)];
  442. }
  443. return CharPointer_ASCII (buffer);
  444. }
  445. static var createRandomVar (Random& r, int depth)
  446. {
  447. switch (r.nextInt (depth > 3 ? 6 : 8))
  448. {
  449. case 0: return var::null;
  450. case 1: return r.nextInt();
  451. case 2: return r.nextInt64();
  452. case 3: return r.nextBool();
  453. case 4: return r.nextDouble();
  454. case 5: return createRandomWideCharString (r);
  455. case 6:
  456. {
  457. var v (createRandomVar (r, depth + 1));
  458. for (int i = 1 + r.nextInt (30); --i >= 0;)
  459. v.append (createRandomVar (r, depth + 1));
  460. return v;
  461. }
  462. case 7:
  463. {
  464. DynamicObject* o = new DynamicObject();
  465. for (int i = r.nextInt (30); --i >= 0;)
  466. o->setProperty (createRandomIdentifier (r), createRandomVar (r, depth + 1));
  467. return o;
  468. }
  469. default:
  470. return var::null;
  471. }
  472. }
  473. void runTest()
  474. {
  475. beginTest ("JSON");
  476. Random r;
  477. r.setSeedRandomly();
  478. expect (JSON::parse (String::empty) == var::null);
  479. expect (JSON::parse ("{}").isObject());
  480. expect (JSON::parse ("[]").isArray());
  481. expect (JSON::parse ("1234").isInt());
  482. expect (JSON::parse ("12345678901234").isInt64());
  483. expect (JSON::parse ("1.123e3").isDouble());
  484. expect (JSON::parse ("-1234").isInt());
  485. expect (JSON::parse ("-12345678901234").isInt64());
  486. expect (JSON::parse ("-1.123e3").isDouble());
  487. for (int i = 100; --i >= 0;)
  488. {
  489. var v;
  490. if (i > 0)
  491. v = createRandomVar (r, 0);
  492. const bool oneLine = r.nextBool();
  493. String asString (JSON::toString (v, oneLine));
  494. var parsed = JSON::parse (asString);
  495. String parsedString (JSON::toString (parsed, oneLine));
  496. expect (asString.isNotEmpty() && parsedString == asString);
  497. }
  498. }
  499. };
  500. static JSONTests JSONUnitTests;
  501. #endif