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.

780 lines
34KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace
  20. {
  21. //==============================================================================
  22. template <typename CharPointerType>
  23. class OSCPatternMatcherImpl
  24. {
  25. typedef CharPointerType CharPtr;
  26. public:
  27. //==============================================================================
  28. static bool match (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
  29. {
  30. if (pattern == patternEnd)
  31. return matchTerminator (target, targetEnd);
  32. const juce_wchar c = pattern.getAndAdvance();
  33. switch (c)
  34. {
  35. case '?': return matchAnyChar (pattern, patternEnd, target, targetEnd);
  36. case '*': return matchAnyOrNoChars (pattern, patternEnd, target, targetEnd);
  37. case '{': return matchInsideStringSet (pattern, patternEnd, target, targetEnd);
  38. case '[': return matchInsideCharSet (pattern, patternEnd, target, targetEnd);
  39. default: return matchChar (c, pattern, patternEnd, target, targetEnd);
  40. }
  41. }
  42. private:
  43. //==============================================================================
  44. static bool matchTerminator (CharPtr target, CharPtr targetEnd)
  45. {
  46. return target == targetEnd;
  47. }
  48. //==============================================================================
  49. static bool matchChar (juce_wchar c, CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
  50. {
  51. if (target == targetEnd || c != target.getAndAdvance())
  52. return false;
  53. return match (pattern, patternEnd, target, targetEnd);
  54. }
  55. //==============================================================================
  56. static bool matchAnyChar (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
  57. {
  58. if (target == targetEnd)
  59. return false;
  60. return match (pattern, patternEnd, ++target, targetEnd);
  61. }
  62. //==============================================================================
  63. static bool matchAnyOrNoChars (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
  64. {
  65. if (target == targetEnd)
  66. return pattern == patternEnd;
  67. if (match (pattern, patternEnd, target, targetEnd))
  68. return true;
  69. return matchAnyOrNoChars (pattern, patternEnd, ++target, targetEnd);
  70. }
  71. //==============================================================================
  72. static bool matchInsideStringSet (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
  73. {
  74. if (pattern == patternEnd)
  75. return false;
  76. // Note: in case this code is ever moved into the more generic CharPointerFunctions,
  77. // the next two lines probably will not compile as soon as this class is used with a
  78. // Char template type parameter that is not the same type as String::Char.
  79. StringArray set;
  80. String currentElement;
  81. while (pattern != patternEnd)
  82. {
  83. const juce_wchar c = pattern.getAndAdvance();
  84. switch (c)
  85. {
  86. case '}':
  87. set.add (currentElement);
  88. currentElement.clear();
  89. return matchStringSet (set, pattern, patternEnd, target, targetEnd);
  90. case ',':
  91. set.add (currentElement);
  92. currentElement.clear();
  93. continue;
  94. default:
  95. currentElement += c;
  96. continue;
  97. }
  98. }
  99. return false;
  100. }
  101. //==============================================================================
  102. static bool matchStringSet (const StringArray& set, CharPtr pattern,
  103. CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
  104. {
  105. if (set.size() == 0)
  106. return match (pattern, patternEnd, target, targetEnd);
  107. for (const String* str = set.begin(); str != set.end(); ++str)
  108. if (str->getCharPointer().compareUpTo (target, str->length()) == 0)
  109. if (match (pattern, patternEnd, target + str->length(), targetEnd))
  110. return true;
  111. return false;
  112. }
  113. //==============================================================================
  114. static bool matchInsideCharSet (CharPtr pattern, CharPtr patternEnd,
  115. CharPtr target, CharPtr targetEnd)
  116. {
  117. if (pattern == patternEnd)
  118. return false;
  119. Array<juce_wchar> set;
  120. bool setIsNegated = false;
  121. while (pattern != patternEnd)
  122. {
  123. const juce_wchar c = pattern.getAndAdvance();
  124. switch (c)
  125. {
  126. case ']':
  127. return matchCharSet (set, setIsNegated, pattern, patternEnd, target, targetEnd);
  128. case '-':
  129. if (! addCharRangeToSet (set, pattern, patternEnd, target, targetEnd))
  130. return false;
  131. break;
  132. case '!':
  133. if (set.size() == 0 && setIsNegated == false)
  134. {
  135. setIsNegated = true;
  136. break;
  137. }
  138. // else = special case: fall through to default and treat '!' as a non-special character.
  139. default:
  140. set.add (c);
  141. break;
  142. }
  143. }
  144. return false;
  145. }
  146. //==============================================================================
  147. static bool matchCharSet (const Array<juce_wchar>& set, bool setIsNegated,
  148. CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
  149. {
  150. if (set.size() == 0)
  151. return match (pattern, patternEnd, target, targetEnd);
  152. if (target == targetEnd)
  153. return false;
  154. return setIsNegated ? matchCharSetNegated (set, pattern, patternEnd, target, targetEnd)
  155. : matchCharSetNotNegated (set, pattern, patternEnd, target, targetEnd);
  156. }
  157. //==============================================================================
  158. static bool matchCharSetNegated (const Array<juce_wchar>& set, CharPtr pattern,
  159. CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
  160. {
  161. for (juce_wchar* c = set.begin(); c != set.end(); ++c)
  162. if (*target == *c)
  163. return false;
  164. return match (pattern, patternEnd, target + 1, targetEnd);
  165. }
  166. //==============================================================================
  167. static bool matchCharSetNotNegated (const Array<juce_wchar>& set, CharPtr pattern,
  168. CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
  169. {
  170. for (juce_wchar* c = set.begin(); c != set.end(); ++c)
  171. if (*target == *c)
  172. if (match (pattern, patternEnd, target + 1, targetEnd))
  173. return true;
  174. return false;
  175. }
  176. //==============================================================================
  177. static bool addCharRangeToSet (Array<juce_wchar>& set, CharPtr pattern,
  178. CharPtr /*patternEnd*/, CharPtr target, CharPtr targetEnd)
  179. {
  180. if (target == targetEnd)
  181. return false;
  182. juce_wchar rangeStart = set.getLast();
  183. juce_wchar rangeEnd = pattern.getAndAdvance();
  184. if (rangeEnd == ']')
  185. {
  186. set.add ('-'); // special case: '-' has no special meaning at the end.
  187. return true;
  188. }
  189. if (rangeEnd == ',' || rangeEnd == '{' || rangeEnd == '}' || set.size() == 0)
  190. return false;
  191. while (rangeEnd > rangeStart)
  192. set.add (++rangeStart);
  193. return true;
  194. }
  195. };
  196. //==============================================================================
  197. static bool matchOscPattern (const String& pattern, const String& target)
  198. {
  199. return OSCPatternMatcherImpl<String::CharPointerType>::match (pattern.getCharPointer(),
  200. pattern.getCharPointer().findTerminatingNull(),
  201. target.getCharPointer(),
  202. target.getCharPointer().findTerminatingNull());
  203. }
  204. //==============================================================================
  205. template <typename OSCAddressType> struct OSCAddressTokeniserTraits;
  206. template <> struct OSCAddressTokeniserTraits<OSCAddress> { static const char* getDisallowedChars() { return " #*,?/[]{}"; } };
  207. template <> struct OSCAddressTokeniserTraits<OSCAddressPattern> { static const char* getDisallowedChars() { return " #/"; } };
  208. //==============================================================================
  209. template <typename OSCAddressType>
  210. struct OSCAddressTokeniser
  211. {
  212. typedef OSCAddressTokeniserTraits<OSCAddressType> Traits;
  213. //==============================================================================
  214. static bool isPrintableASCIIChar (juce_wchar c) noexcept
  215. {
  216. return c >= ' ' && c <= '~';
  217. }
  218. static bool isDisallowedChar (juce_wchar c) noexcept
  219. {
  220. return CharPointer_ASCII (Traits::getDisallowedChars()).indexOf (c, false) >= 0;
  221. }
  222. static bool containsOnlyAllowedPrintableASCIIChars (const String& string) noexcept
  223. {
  224. for (String::CharPointerType charPtr (string.getCharPointer()); ! charPtr.isEmpty();)
  225. {
  226. juce_wchar c = charPtr.getAndAdvance();
  227. if (! isPrintableASCIIChar (c) || isDisallowedChar (c))
  228. return false;
  229. }
  230. return true;
  231. }
  232. //==============================================================================
  233. static StringArray tokenise (const String& address)
  234. {
  235. if (address.isEmpty())
  236. throw OSCFormatError ("OSC format error: address string cannot be empty.");
  237. if (! address.startsWithChar ('/'))
  238. throw OSCFormatError ("OSC format error: address string must start with a forward slash.");
  239. StringArray oscSymbols;
  240. oscSymbols.addTokens (address, "/", StringRef());
  241. oscSymbols.removeEmptyStrings (false);
  242. for (String* token = oscSymbols.begin(); token != oscSymbols.end(); ++token)
  243. if (! containsOnlyAllowedPrintableASCIIChars (*token))
  244. throw OSCFormatError ("OSC format error: encountered characters not allowed in address string.");
  245. return oscSymbols;
  246. }
  247. };
  248. } // namespace
  249. //==============================================================================
  250. OSCAddress::OSCAddress (const String& address)
  251. : oscSymbols (OSCAddressTokeniser<OSCAddress>::tokenise (address)),
  252. asString (address.trimCharactersAtEnd ("/"))
  253. {
  254. }
  255. OSCAddress::OSCAddress (const char* address)
  256. : oscSymbols (OSCAddressTokeniser<OSCAddress>::tokenise (String (address))),
  257. asString (String (address).trimCharactersAtEnd ("/"))
  258. {
  259. }
  260. //==============================================================================
  261. bool OSCAddress::operator== (const OSCAddress& other) const noexcept
  262. {
  263. return asString == other.asString;
  264. }
  265. bool OSCAddress::operator!= (const OSCAddress& other) const noexcept
  266. {
  267. return ! operator== (other);
  268. }
  269. //==============================================================================
  270. String OSCAddress::toString() const noexcept
  271. {
  272. return asString;
  273. }
  274. //==============================================================================
  275. OSCAddressPattern::OSCAddressPattern (const String& address)
  276. : oscSymbols (OSCAddressTokeniser<OSCAddressPattern>::tokenise (address)),
  277. asString (address.trimCharactersAtEnd ("/")),
  278. wasInitialisedWithWildcards (asString.containsAnyOf ("*?{}[]"))
  279. {
  280. }
  281. OSCAddressPattern::OSCAddressPattern (const char* address)
  282. : oscSymbols (OSCAddressTokeniser<OSCAddressPattern>::tokenise (String (address))),
  283. asString (String (address).trimCharactersAtEnd ("/")),
  284. wasInitialisedWithWildcards (asString.containsAnyOf ("*?{}[]"))
  285. {
  286. }
  287. //==============================================================================
  288. bool OSCAddressPattern::operator== (const OSCAddressPattern& other) const noexcept
  289. {
  290. return asString == other.asString;
  291. }
  292. bool OSCAddressPattern::operator!= (const OSCAddressPattern& other) const noexcept
  293. {
  294. return ! operator== (other);
  295. }
  296. //==============================================================================
  297. bool OSCAddressPattern::matches (const OSCAddress& address) const noexcept
  298. {
  299. if (! containsWildcards())
  300. return asString == address.asString;
  301. if (oscSymbols.size() != address.oscSymbols.size())
  302. return false;
  303. for (int i = 0; i < oscSymbols.size(); ++i)
  304. if (! matchOscPattern (oscSymbols[i], address.oscSymbols[i]))
  305. return false;
  306. return true;
  307. }
  308. //==============================================================================
  309. String OSCAddressPattern::toString() const noexcept
  310. {
  311. return asString;
  312. }
  313. //==============================================================================
  314. //==============================================================================
  315. #if JUCE_UNIT_TESTS
  316. class OSCAddressTests : public UnitTest
  317. {
  318. public:
  319. OSCAddressTests() : UnitTest ("OSCAddress class") {}
  320. void runTest()
  321. {
  322. beginTest ("construction and parsing");
  323. {
  324. expectThrowsType (OSCAddress (""), OSCFormatError);
  325. expectThrowsType (OSCAddress ("noleadingslash"), OSCFormatError);
  326. expectThrowsType (OSCAddress ("/notallowedchar "), OSCFormatError);
  327. expectThrowsType (OSCAddress ("/notallowedchar#"), OSCFormatError);
  328. expectThrowsType (OSCAddress ("/notallowedchar*"), OSCFormatError);
  329. expectThrowsType (OSCAddress ("/notallowedchar,"), OSCFormatError);
  330. expectThrowsType (OSCAddress ("/notallowedchar?"), OSCFormatError);
  331. expectThrowsType (OSCAddress ("/notallowedchar["), OSCFormatError);
  332. expectThrowsType (OSCAddress ("/notallowedchar]"), OSCFormatError);
  333. expectThrowsType (OSCAddress ("/notallowedchar{"), OSCFormatError);
  334. expectThrowsType (OSCAddress ("/notallowedchar}andsomemorechars"), OSCFormatError);
  335. expectThrowsType (OSCAddress (String::fromUTF8 ("/nonasciicharacter\xc3\xbc""blabla")), OSCFormatError);
  336. expectThrowsType (OSCAddress ("/nonprintableasciicharacter\t"), OSCFormatError);
  337. expectDoesNotThrow (OSCAddress ("/"));
  338. expectDoesNotThrow (OSCAddress ("/a"));
  339. expectDoesNotThrow (OSCAddress ("/a/"));
  340. expectDoesNotThrow (OSCAddress ("/a/bcd/"));
  341. expectDoesNotThrow (OSCAddress ("/abcd/efgh/ijkLMNOPq/666r/s"));
  342. expectDoesNotThrow (OSCAddress ("/allowedprintablecharacters!$%&()+-.^_`|~"));
  343. expectDoesNotThrow (OSCAddress ("/additonalslashes//will///be////ignored"));
  344. }
  345. beginTest ("conversion to/from String");
  346. {
  347. OSCAddress address ("/this/is/a/very/long/address/");
  348. expectEquals (address.toString(), String ("/this/is/a/very/long/address"));
  349. }
  350. }
  351. };
  352. static OSCAddressTests OSCAddressUnitTests;
  353. //==============================================================================
  354. class OSCAddressPatternTests : public UnitTest
  355. {
  356. public:
  357. OSCAddressPatternTests() : UnitTest ("OSCAddressPattern class") {}
  358. void runTest()
  359. {
  360. beginTest ("construction and parsing");
  361. {
  362. expectThrowsType (OSCAddressPattern (""), OSCFormatError);
  363. expectThrowsType (OSCAddressPattern ("noleadingslash"), OSCFormatError);
  364. expectThrowsType (OSCAddressPattern ("/notallowedchar "), OSCFormatError);
  365. expectThrowsType (OSCAddressPattern ("/notallowedchar#andsomemorechars"), OSCFormatError);
  366. expectThrowsType (OSCAddressPattern (String::fromUTF8 ("/nonasciicharacter\xc3\xbc""blabla")), OSCFormatError);
  367. expectThrowsType (OSCAddressPattern ("/nonprintableasciicharacter\t"), OSCFormatError);
  368. expectDoesNotThrow (OSCAddressPattern ("/"));
  369. expectDoesNotThrow (OSCAddressPattern ("/a"));
  370. expectDoesNotThrow (OSCAddressPattern ("/a/"));
  371. expectDoesNotThrow (OSCAddressPattern ("/a/bcd/"));
  372. expectDoesNotThrow (OSCAddressPattern ("/abcd/efgh/ijkLMNOPq/666r/s"));
  373. expectDoesNotThrow (OSCAddressPattern ("/allowedprintablecharacters!$%&()+-.:;<=>@^_`|~"));
  374. expectDoesNotThrow (OSCAddressPattern ("/additonalslashes//will///be////ignored"));
  375. }
  376. beginTest ("construction and parsing - with wildcards");
  377. {
  378. expectDoesNotThrow (OSCAddressPattern ("/foo/b?r/"));
  379. expectDoesNotThrow (OSCAddressPattern ("/?????"));
  380. expectDoesNotThrow (OSCAddressPattern ("/foo/b*r"));
  381. expectDoesNotThrow (OSCAddressPattern ("/**"));
  382. expectDoesNotThrow (OSCAddressPattern ("/?/b/*c"));
  383. }
  384. beginTest ("construction and parsing - with match expressions");
  385. {
  386. expectDoesNotThrow (OSCAddressPattern ("/{}"));
  387. expectDoesNotThrow (OSCAddressPattern ("/{foo}"));
  388. expectDoesNotThrow (OSCAddressPattern ("/{foo,bar,baz}"));
  389. expectDoesNotThrow (OSCAddressPattern ("/[]"));
  390. expectDoesNotThrow (OSCAddressPattern ("/[abcde]"));
  391. expectDoesNotThrow (OSCAddressPattern ("/[a-e]"));
  392. expectDoesNotThrow (OSCAddressPattern ("/foo/[a-z]x{foo,bar}/*BAZ42/"));
  393. /* Note: if malformed expressions are used, e.g. "bracenotclosed{" or "{a-e}" or "[-foo]",
  394. this should not throw at construction time. Instead it should simply fail any pattern match later.
  395. So there is no need to test for those.
  396. The reason is that we do not actually parse the expressions now, but only during matching.
  397. */
  398. }
  399. beginTest ("equality comparison");
  400. {
  401. {
  402. OSCAddressPattern lhs ("/test/1");
  403. OSCAddressPattern rhs ("/test/1");
  404. expect (lhs == rhs);
  405. expect (! (lhs != rhs));
  406. }
  407. {
  408. OSCAddressPattern lhs ("/test/1");
  409. OSCAddressPattern rhs ("/test/1/");
  410. expect (lhs == rhs);
  411. expect (! (lhs != rhs));
  412. }
  413. {
  414. OSCAddressPattern lhs ("/test/1");
  415. OSCAddressPattern rhs ("/test/2");
  416. expect (! (lhs == rhs));
  417. expect (lhs != rhs);
  418. }
  419. }
  420. beginTest ("basic string matching");
  421. {
  422. /* Note: the actual expression matching is tested in OSCPatternMatcher, so here we just
  423. do some basic tests and check if the matching works with multi-part addresses.
  424. */
  425. {
  426. OSCAddressPattern pattern ("/foo/bar");
  427. expect (! pattern.containsWildcards());
  428. OSCAddress address ("/foo/bar");
  429. expect (pattern.matches (address));
  430. }
  431. {
  432. OSCAddressPattern pattern ("/foo/bar/");
  433. expect (! pattern.containsWildcards());
  434. OSCAddress address ("/foo/bar");
  435. expect (pattern.matches (address));
  436. }
  437. {
  438. OSCAddressPattern pattern ("/");
  439. expect (! pattern.containsWildcards());
  440. OSCAddress address ("/");
  441. expect (pattern.matches (address));
  442. }
  443. {
  444. OSCAddressPattern pattern ("/foo/bar");
  445. expect (! pattern.containsWildcards());
  446. expect (! pattern.matches (OSCAddress ("/foo/baz")));
  447. expect (! pattern.matches (OSCAddress ("/foo/bar/baz")));
  448. expect (! pattern.matches (OSCAddress ("/foo")));
  449. }
  450. }
  451. beginTest ("string matching with wildcards");
  452. {
  453. OSCAddressPattern pattern ("/*/*put/slider[0-9]");
  454. expect (pattern.containsWildcards());
  455. expect (pattern.matches (OSCAddress ("/mypatch/input/slider0")));
  456. expect (pattern.matches (OSCAddress ("/myotherpatch/output/slider9")));
  457. expect (! pattern.matches (OSCAddress ("/myotherpatch/output/slider10")));
  458. expect (! pattern.matches (OSCAddress ("/output/slider9")));
  459. expect (! pattern.matches (OSCAddress ("/myotherpatch/output/slider9/position")));
  460. }
  461. beginTest ("conversion to/from String");
  462. {
  463. {
  464. OSCAddressPattern ap ("/this/is/a/very/long/address/");
  465. expectEquals (ap.toString(), String ("/this/is/a/very/long/address"));
  466. }
  467. {
  468. OSCAddressPattern ap ("/*/*put/{fader,slider,knob}[0-9]/ba?/");
  469. expectEquals (ap.toString(), String ("/*/*put/{fader,slider,knob}[0-9]/ba?"));
  470. }
  471. }
  472. }
  473. };
  474. static OSCAddressPatternTests OSCAddressPatternUnitTests;
  475. //==============================================================================
  476. class OSCPatternMatcherTests : public UnitTest
  477. {
  478. public:
  479. OSCPatternMatcherTests() : UnitTest ("OSCAddress class / pattern matching") {}
  480. void runTest()
  481. {
  482. beginTest ("basic string matching");
  483. {
  484. expect (matchOscPattern ("", ""));
  485. expect (! matchOscPattern ("", "x"));
  486. expect (! matchOscPattern ("x", ""));
  487. expect (matchOscPattern ("foo", "foo"));
  488. expect (! matchOscPattern ("foo", "bar"));
  489. expect (! matchOscPattern ("ooooo", "oooooo"));
  490. }
  491. beginTest ("string matching with '?' wildcard");
  492. {
  493. expect (matchOscPattern ("?", "x"));
  494. expect (! matchOscPattern ("?", ""));
  495. expect (! matchOscPattern ("?", "xx"));
  496. expect (! matchOscPattern ("b?r", "br"));
  497. expect (matchOscPattern ("b?r", "bar"));
  498. expect (! matchOscPattern ("b?r", "baar"));
  499. expect (matchOscPattern ("f???o", "fabco"));
  500. expect (! matchOscPattern ("f???o", "fabo"));
  501. }
  502. beginTest ("string matching with '*' wildcard");
  503. {
  504. expect (matchOscPattern ("*", ""));
  505. expect (matchOscPattern ("*", "x"));
  506. expect (matchOscPattern ("*", "foo"));
  507. expect (matchOscPattern ("*c", "aaaaaaabc"));
  508. expect (matchOscPattern ("*c", "aaaaaaabbbcccc"));
  509. expect (! matchOscPattern ("*c", "aaaaaaabbbcccca"));
  510. expect (matchOscPattern ("c*", "ccccbbbbaaaa"));
  511. expect (! matchOscPattern ("c*", "accccbbbaaaa"));
  512. expect (matchOscPattern ("f*o", "fo"));
  513. expect (matchOscPattern ("f*o", "fuo"));
  514. expect (matchOscPattern ("f*o", "fuvwxyzo"));
  515. expect (matchOscPattern ("*reallyreallylongstringstringstring", "reallyreallylongstringstringstrNOT"
  516. "reallyreallylongstringstringstrNOT"
  517. "reallyreallylongstringstringstrNOT"
  518. "reallyreallylongstringstringstrNOT"
  519. "reallyreallylongstringstringstrNOT"
  520. "reallyreallylongstringstringstring"));
  521. }
  522. beginTest ("string matching with '{..., ...}' pattern");
  523. {
  524. expect (matchOscPattern ("{}", ""));
  525. expect (! matchOscPattern ("{}", "x"));
  526. expect (matchOscPattern ("{abcde}", "abcde"));
  527. expect (matchOscPattern ("{abcde,f}", "f"));
  528. expect (! matchOscPattern ("{abcde,f}", "ff"));
  529. expect (matchOscPattern ("a{bcd}e", "abcde"));
  530. expect (matchOscPattern ("a{bcd,bce}e", "abcde"));
  531. expect (! matchOscPattern ("a{bce,bcf}e", "abcde"));
  532. expect (! matchOscPattern ("a{bce,bcf}e", "ae"));
  533. expect (matchOscPattern ("a{bce,,bcf}e", "ae"));
  534. expect (matchOscPattern ("a{bcd,bcd,bcd}e", "abcde"));
  535. expect (matchOscPattern ("aaa{bc,def,ghij,klmnopqrstu}eee", "aaaghijeee"));
  536. expect (matchOscPattern ("{a,b,c}bcde", "abcde"));
  537. expect (! matchOscPattern ("{abc}bcde", "abcde"));
  538. expect (matchOscPattern ("bcde{a,b,c}", "bcdea"));
  539. expect (! matchOscPattern ("bcde{abc}", "bcdea"));
  540. expect (matchOscPattern ("f{o,}o", "fo"));
  541. expect (matchOscPattern ("f{,,,,,}o", "fo"));
  542. expect (matchOscPattern ("foo{b,ba,bar}x", "foobarx"));
  543. expect (matchOscPattern ("a{bc,de}fg{hij,klm}{n}{}", "adefghijn"));
  544. // should fail gracefully in case of wrong syntax:
  545. expect (! matchOscPattern ("not{closing", "notclosing"));
  546. expect (! matchOscPattern ("not}opening", "notopening"));
  547. expect (! matchOscPattern ("{{nested}}", "nested"));
  548. expect (! matchOscPattern ("{a-c}bcde", "abcde"));
  549. expect (! matchOscPattern ("bcde{a-c}", "abcde"));
  550. }
  551. beginTest ("string matching with '[...]' pattern");
  552. {
  553. // using [] for a set of chars:
  554. expect (matchOscPattern ("[]", ""));
  555. expect (! matchOscPattern ("[]", "x"));
  556. expect (! matchOscPattern ("[abcde]", "abcde"));
  557. expect (matchOscPattern ("[abcde]", "a"));
  558. expect (matchOscPattern ("[abcde]", "b"));
  559. expect (matchOscPattern ("[abcde]", "c"));
  560. expect (matchOscPattern ("[abcde]", "d"));
  561. expect (matchOscPattern ("[abcde]", "e"));
  562. expect (! matchOscPattern ("[abcde]", "f"));
  563. expect (matchOscPattern ("f[oo]", "fo"));
  564. expect (! matchOscPattern ("f[oo]", "foo"));
  565. expect (matchOscPattern ("fooba[rxz]foo", "foobarfoo"));
  566. expect (matchOscPattern ("fooba[rxz]foo", "foobaxfoo"));
  567. expect (matchOscPattern ("fooba[rxz]foo", "foobazfoo"));
  568. expect (! matchOscPattern ("fooba[rxz]foo", "foobasfoo"));
  569. expect (matchOscPattern ("foo[abc]foo[defgh]foo[i]foo[]foo", "foobfoohfooifoofoo"));
  570. // using [] for a range of chars:
  571. expect (matchOscPattern ("fooba[r-z]foo", "foobarfoo"));
  572. expect (matchOscPattern ("fooba[r-z]foo", "foobaxfoo"));
  573. expect (matchOscPattern ("fooba[r-z]foo", "foobazfoo"));
  574. expect (matchOscPattern ("fooba[r-z]foo", "foobasfoo"));
  575. expect (! matchOscPattern ("fooba[r-z]foo", "foobaRfoo"));
  576. expect (! matchOscPattern ("foo[1-8]bar", "foo0bar"));
  577. expect (matchOscPattern ("foo[1-8]bar", "foo1bar"));
  578. expect (matchOscPattern ("foo[1-8]bar", "foo6bar"));
  579. expect (matchOscPattern ("foo[1-8]bar", "foo8bar"));
  580. expect (! matchOscPattern ("foo[1-8]bar", "foo9bar"));
  581. // special case: '-' does not have a special meaning if it is at the end of the set.
  582. expect (matchOscPattern ("foo[abc-]bar", "fooabar"));
  583. expect (matchOscPattern ("foo[abc-]bar", "foo-bar"));
  584. expect (matchOscPattern ("foo[-]bar", "foo-bar"));
  585. // mixing both set and range:
  586. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]b[a-b]r", "fooabar"));
  587. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]b[a-a]r", "foobbar"));
  588. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]b[aaaa-aaaa-aaaa]r", "foodbar"));
  589. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooebar"));
  590. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foogbar"));
  591. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooibar"));
  592. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foojbar"));
  593. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fookbar"));
  594. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foolbar"));
  595. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooobar"));
  596. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foopbar"));
  597. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooubar"));
  598. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooybar"));
  599. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foozbar"));
  600. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo0bar"));
  601. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo1bar"));
  602. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo5bar"));
  603. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo8bar"));
  604. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo9bar"));
  605. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooCbar"));
  606. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooDbar"));
  607. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooEbar"));
  608. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooFbar"));
  609. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooGbar"));
  610. expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooXbar"));
  611. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]ba[Rr]", "fooZbar"));
  612. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]ba[Rr]", "foobar"));
  613. expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]ba[Rr]", "fooFXbar"));
  614. // using [!...] for a negated set or range of chars:
  615. expect (! matchOscPattern ("fooba[!rxz]foo", "foobarfoo"));
  616. expect (! matchOscPattern ("fooba[!rxz]foo", "foobaxfoo"));
  617. expect (! matchOscPattern ("fooba[!rxz]foo", "foobazfoo"));
  618. expect (matchOscPattern ("fooba[!rxz]foo", "foobasfoo"));
  619. expect (! matchOscPattern ("fooba[!r-z]foo", "foobarfoo"));
  620. expect (! matchOscPattern ("fooba[!r-z]foo", "foobaxfoo"));
  621. expect (! matchOscPattern ("fooba[!r-z]foo", "foobazfoo"));
  622. expect (! matchOscPattern ("fooba[!r-z]foo", "foobasfoo"));
  623. expect (matchOscPattern ("fooba[!r-z]foo", "foobaRfoo"));
  624. // special case: '!' does not have a special meaning if it is not the first char in the set.
  625. expect (matchOscPattern ("foo[ab!c]bar", "fooabar"));
  626. expect (matchOscPattern ("foo[ab!c]bar", "foo!bar"));
  627. expect (! matchOscPattern ("foo[ab!c]bar", "fooxbar"));
  628. expect (! matchOscPattern ("foo[!!]bar", "foo!bar"));
  629. expect (matchOscPattern ("foo[!!]bar", "fooxbar"));
  630. expect (! matchOscPattern ("foo[!!]bar", "foobar"));
  631. // should fail gracefully in case of wrong syntax:
  632. expect (! matchOscPattern ("notclosin[g", "notclosing"));
  633. expect (! matchOscPattern ("n]otopening", "notopening"));
  634. expect (! matchOscPattern ("[[nested]]", "nested"));
  635. expect (! matchOscPattern ("norangestar[-t]", "norangestart"));
  636. expect (! matchOscPattern ("norangestar[-t]", "norangestar-"));
  637. }
  638. beginTest ("string matching combining patterns");
  639. {
  640. expect (matchOscPattern ("*ea*ll[y-z0-9X-Zvwx]??m[o-q]l[e]x{fat,mat,pat}te{}r*?", "reallycomplexpattern"));
  641. }
  642. }
  643. };
  644. static OSCPatternMatcherTests OSCPatternMatcherUnitTests;
  645. #endif // JUCE_UNIT_TESTS