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.

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