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.

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