JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

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