 25bd5d8adb
			
		
	
	25bd5d8adb
	
	
	
		
			
			subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
		
			
				
	
	
		
			793 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			793 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|   ==============================================================================
 | |
| 
 | |
|    This file is part of the JUCE library.
 | |
|    Copyright (c) 2020 - Raw Material Software Limited
 | |
| 
 | |
|    JUCE is an open source library subject to commercial or open-source
 | |
|    licensing.
 | |
| 
 | |
|    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | |
|    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | |
| 
 | |
|    End User License Agreement: www.juce.com/juce-6-licence
 | |
|    Privacy Policy: www.juce.com/juce-privacy-policy
 | |
| 
 | |
|    Or: You may also use this code under the terms of the GPL v3 (see
 | |
|    www.gnu.org/licenses).
 | |
| 
 | |
|    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | |
|    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | |
|    DISCLAIMED.
 | |
| 
 | |
|   ==============================================================================
 | |
| */
 | |
| 
 | |
| namespace juce
 | |
| {
 | |
| 
 | |
| namespace
 | |
| {
 | |
|     //==============================================================================
 | |
|     template <typename CharPointerType>
 | |
|     class OSCPatternMatcherImpl
 | |
|     {
 | |
|         using CharPtr = CharPointerType;
 | |
| 
 | |
|     public:
 | |
|         //==============================================================================
 | |
|         static bool match (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             if (pattern == patternEnd)
 | |
|                 return matchTerminator (target, targetEnd);
 | |
| 
 | |
|             auto c = pattern.getAndAdvance();
 | |
| 
 | |
|             switch (c)
 | |
|             {
 | |
|                 case '?':   return matchAnyChar (pattern, patternEnd, target, targetEnd);
 | |
|                 case '*':   return matchAnyOrNoChars (pattern, patternEnd, target, targetEnd);
 | |
|                 case '{':   return matchInsideStringSet (pattern, patternEnd, target, targetEnd);
 | |
|                 case '[':   return matchInsideCharSet (pattern, patternEnd, target, targetEnd);
 | |
|                 default:    return matchChar (c, pattern, patternEnd, target, targetEnd);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     private:
 | |
|         //==============================================================================
 | |
|         static bool matchTerminator (CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             return target == targetEnd;
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool matchChar (juce_wchar c, CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             if (target == targetEnd || c != target.getAndAdvance())
 | |
|                 return false;
 | |
| 
 | |
|             return match (pattern, patternEnd, target, targetEnd);
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool matchAnyChar (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             if (target == targetEnd)
 | |
|                 return false;
 | |
| 
 | |
|             return match (pattern, patternEnd, ++target, targetEnd);
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool matchAnyOrNoChars (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             if (target == targetEnd)
 | |
|                 return pattern == patternEnd;
 | |
| 
 | |
|             if (match (pattern, patternEnd, target, targetEnd))
 | |
|                 return true;
 | |
| 
 | |
|             return matchAnyOrNoChars (pattern, patternEnd, ++target, targetEnd);
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool matchInsideStringSet (CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             if (pattern == patternEnd)
 | |
|                 return false;
 | |
| 
 | |
|             // Note: In case this code is ever moved into the more generic CharPointerFunctions,
 | |
|             // the next two lines probably will not compile as soon as this class is used with a
 | |
|             // Char template type parameter that is not the same type as String::Char.
 | |
|             StringArray set;
 | |
|             String currentElement;
 | |
| 
 | |
|             while (pattern != patternEnd)
 | |
|             {
 | |
|                 auto c = pattern.getAndAdvance();
 | |
| 
 | |
|                 switch (c)
 | |
|                 {
 | |
|                     case '}':
 | |
|                         set.add (currentElement);
 | |
|                         currentElement.clear();
 | |
|                         return matchStringSet (set, pattern, patternEnd, target, targetEnd);
 | |
| 
 | |
|                     case ',':
 | |
|                         set.add (currentElement);
 | |
|                         currentElement.clear();
 | |
|                         continue;
 | |
| 
 | |
|                     default:
 | |
|                         currentElement += c;
 | |
|                         continue;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool matchStringSet (const StringArray& set, CharPtr pattern,
 | |
|                                     CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             if (set.size() == 0)
 | |
|                 return match (pattern, patternEnd, target, targetEnd);
 | |
| 
 | |
|             for (auto& str : set)
 | |
|                 if (str.getCharPointer().compareUpTo (target, str.length()) == 0)
 | |
|                     if (match (pattern, patternEnd, target + str.length(), targetEnd))
 | |
|                         return true;
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool matchInsideCharSet (CharPtr pattern, CharPtr patternEnd,
 | |
|                                         CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             if (pattern == patternEnd)
 | |
|                 return false;
 | |
| 
 | |
|             Array<juce_wchar> set;
 | |
|             bool setIsNegated = false;
 | |
| 
 | |
|             while (pattern != patternEnd)
 | |
|             {
 | |
|                 auto c = pattern.getAndAdvance();
 | |
| 
 | |
|                 switch (c)
 | |
|                 {
 | |
|                     case ']':
 | |
|                         return matchCharSet (set, setIsNegated, pattern, patternEnd, target, targetEnd);
 | |
| 
 | |
|                     case '-':
 | |
|                         if (! addCharRangeToSet (set, pattern, patternEnd, target, targetEnd))
 | |
|                             return false;
 | |
| 
 | |
|                         break;
 | |
| 
 | |
|                     case '!':
 | |
|                         if (set.size() == 0 && setIsNegated == false)
 | |
|                         {
 | |
|                             setIsNegated = true;
 | |
|                             break;
 | |
|                         }
 | |
|                         // else = special case: fall through to default and treat '!' as a non-special character.
 | |
|                         JUCE_FALLTHROUGH
 | |
| 
 | |
|                     default:
 | |
|                         set.add (c);
 | |
|                         break;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool matchCharSet (const Array<juce_wchar>& set, bool setIsNegated,
 | |
|                                   CharPtr pattern, CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             if (set.size() == 0)
 | |
|                 return match (pattern, patternEnd, target, targetEnd);
 | |
| 
 | |
|             if (target == targetEnd)
 | |
|                 return false;
 | |
| 
 | |
|             return setIsNegated ? matchCharSetNegated (set, pattern, patternEnd, target, targetEnd)
 | |
|                                 : matchCharSetNotNegated (set, pattern, patternEnd, target, targetEnd);
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool matchCharSetNegated (const Array<juce_wchar>& set, CharPtr pattern,
 | |
|                                          CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             for (auto c : set)
 | |
|                 if (*target == c)
 | |
|                     return false;
 | |
| 
 | |
|             return match (pattern, patternEnd, target + 1, targetEnd);
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool matchCharSetNotNegated (const Array<juce_wchar>& set, CharPtr pattern,
 | |
|                                             CharPtr patternEnd, CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             for (auto c : set)
 | |
|                 if (*target == c)
 | |
|                     if (match (pattern, patternEnd, target + 1, targetEnd))
 | |
|                         return true;
 | |
| 
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool addCharRangeToSet (Array<juce_wchar>& set, CharPtr pattern,
 | |
|                                        CharPtr /*patternEnd*/, CharPtr target, CharPtr targetEnd)
 | |
|         {
 | |
|             if (target == targetEnd)
 | |
|                 return false;
 | |
| 
 | |
|             auto rangeStart = set.getLast();
 | |
|             auto rangeEnd = pattern.getAndAdvance();
 | |
| 
 | |
|             if (rangeEnd == ']')
 | |
|             {
 | |
|                 set.add ('-');  // special case: '-' has no special meaning at the end.
 | |
|                 return true;
 | |
|             }
 | |
| 
 | |
|             if (rangeEnd == ',' || rangeEnd == '{' || rangeEnd == '}' || set.size() == 0)
 | |
|                 return false;
 | |
| 
 | |
|             while (rangeEnd > rangeStart)
 | |
|                 set.add (++rangeStart);
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     //==============================================================================
 | |
|     static bool matchOscPattern (const String& pattern, const String& target)
 | |
|     {
 | |
|         return OSCPatternMatcherImpl<String::CharPointerType>::match (pattern.getCharPointer(),
 | |
|                                                                       pattern.getCharPointer().findTerminatingNull(),
 | |
|                                                                       target.getCharPointer(),
 | |
|                                                                       target.getCharPointer().findTerminatingNull());
 | |
|     }
 | |
| 
 | |
|     //==============================================================================
 | |
|     template <typename OSCAddressType> struct OSCAddressTokeniserTraits;
 | |
|     template <> struct OSCAddressTokeniserTraits<OSCAddress>        { static const char* getDisallowedChars() { return " #*,?/[]{}"; } };
 | |
|     template <> struct OSCAddressTokeniserTraits<OSCAddressPattern> { static const char* getDisallowedChars() { return " #/"; } };
 | |
| 
 | |
|     //==============================================================================
 | |
|     template <typename OSCAddressType>
 | |
|     struct OSCAddressTokeniser
 | |
|     {
 | |
|         using Traits = OSCAddressTokeniserTraits<OSCAddressType>;
 | |
| 
 | |
|         //==============================================================================
 | |
|         static bool isPrintableASCIIChar (juce_wchar c) noexcept
 | |
|         {
 | |
|             return c >= ' ' && c <= '~';
 | |
|         }
 | |
| 
 | |
|         static bool isDisallowedChar (juce_wchar c) noexcept
 | |
|         {
 | |
|             return CharPointer_ASCII (Traits::getDisallowedChars()).indexOf (c, false) >= 0;
 | |
|         }
 | |
| 
 | |
|         static bool containsOnlyAllowedPrintableASCIIChars (const String& string) noexcept
 | |
|         {
 | |
|             for (auto charPtr = string.getCharPointer(); ! charPtr.isEmpty();)
 | |
|             {
 | |
|                 auto c = charPtr.getAndAdvance();
 | |
| 
 | |
|                 if (! isPrintableASCIIChar (c) || isDisallowedChar (c))
 | |
|                     return false;
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         static StringArray tokenise (const String& address)
 | |
|         {
 | |
|             if (address.isEmpty())
 | |
|                 throw OSCFormatError ("OSC format error: address string cannot be empty.");
 | |
| 
 | |
|             if (! address.startsWithChar ('/'))
 | |
|                 throw OSCFormatError ("OSC format error: address string must start with a forward slash.");
 | |
| 
 | |
|             StringArray oscSymbols;
 | |
|             oscSymbols.addTokens (address, "/", StringRef());
 | |
|             oscSymbols.removeEmptyStrings (false);
 | |
| 
 | |
|             for (auto& token : oscSymbols)
 | |
|                 if (! containsOnlyAllowedPrintableASCIIChars (token))
 | |
|                     throw OSCFormatError ("OSC format error: encountered characters not allowed in address string.");
 | |
| 
 | |
|             return oscSymbols;
 | |
|         }
 | |
|     };
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| //==============================================================================
 | |
| OSCAddress::OSCAddress (const String& address)
 | |
|     : oscSymbols (OSCAddressTokeniser<OSCAddress>::tokenise (address)),
 | |
|       asString (address.trimCharactersAtEnd ("/"))
 | |
| {
 | |
| }
 | |
| 
 | |
| OSCAddress::OSCAddress (const char* address)
 | |
|     : oscSymbols (OSCAddressTokeniser<OSCAddress>::tokenise (String (address))),
 | |
|       asString (String (address).trimCharactersAtEnd ("/"))
 | |
| {
 | |
| }
 | |
| 
 | |
| //==============================================================================
 | |
| bool OSCAddress::operator== (const OSCAddress& other) const noexcept
 | |
| {
 | |
|     return asString == other.asString;
 | |
| }
 | |
| 
 | |
| bool OSCAddress::operator!= (const OSCAddress& other) const noexcept
 | |
| {
 | |
|     return ! operator== (other);
 | |
| }
 | |
| 
 | |
| //==============================================================================
 | |
| String OSCAddress::toString() const noexcept
 | |
| {
 | |
|     return asString;
 | |
| }
 | |
| 
 | |
| //==============================================================================
 | |
| OSCAddressPattern::OSCAddressPattern (const String& address)
 | |
|     : oscSymbols (OSCAddressTokeniser<OSCAddressPattern>::tokenise (address)),
 | |
|       asString (address.trimCharactersAtEnd ("/")),
 | |
|       wasInitialisedWithWildcards (asString.containsAnyOf ("*?{}[]"))
 | |
| 
 | |
| {
 | |
| }
 | |
| 
 | |
| OSCAddressPattern::OSCAddressPattern (const char* address)
 | |
|     : oscSymbols (OSCAddressTokeniser<OSCAddressPattern>::tokenise (String (address))),
 | |
|       asString (String (address).trimCharactersAtEnd ("/")),
 | |
|       wasInitialisedWithWildcards (asString.containsAnyOf ("*?{}[]"))
 | |
| {
 | |
| }
 | |
| 
 | |
| //==============================================================================
 | |
| bool OSCAddressPattern::operator== (const OSCAddressPattern& other) const noexcept
 | |
| {
 | |
|     return asString == other.asString;
 | |
| }
 | |
| 
 | |
| bool OSCAddressPattern::operator!= (const OSCAddressPattern& other) const noexcept
 | |
| {
 | |
|     return ! operator== (other);
 | |
| }
 | |
| 
 | |
| //==============================================================================
 | |
| bool OSCAddressPattern::matches (const OSCAddress& address) const noexcept
 | |
| {
 | |
|     if (! containsWildcards())
 | |
|         return asString == address.asString;
 | |
| 
 | |
|     if (oscSymbols.size() != address.oscSymbols.size())
 | |
|         return false;
 | |
| 
 | |
|     for (int i = 0; i < oscSymbols.size(); ++i)
 | |
|         if (! matchOscPattern (oscSymbols[i], address.oscSymbols[i]))
 | |
|             return false;
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| //==============================================================================
 | |
| String OSCAddressPattern::toString() const noexcept
 | |
| {
 | |
|     return asString;
 | |
| }
 | |
| 
 | |
| 
 | |
| //==============================================================================
 | |
| //==============================================================================
 | |
| #if JUCE_UNIT_TESTS
 | |
| 
 | |
| class OSCAddressTests : public UnitTest
 | |
| {
 | |
| public:
 | |
|     OSCAddressTests()
 | |
|         : UnitTest ("OSCAddress class", UnitTestCategories::osc)
 | |
|     {}
 | |
| 
 | |
|     void runTest()
 | |
|     {
 | |
|         beginTest ("construction and parsing");
 | |
|         {
 | |
|             expectThrowsType (OSCAddress (""), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("noleadingslash"), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("/notallowedchar "), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("/notallowedchar#"), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("/notallowedchar*"), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("/notallowedchar,"), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("/notallowedchar?"), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("/notallowedchar["), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("/notallowedchar]"), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("/notallowedchar{"), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("/notallowedchar}andsomemorechars"), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress (String::fromUTF8 ("/nonasciicharacter\xc3\xbc""blabla")), OSCFormatError);
 | |
|             expectThrowsType (OSCAddress ("/nonprintableasciicharacter\t"), OSCFormatError);
 | |
| 
 | |
|             expectDoesNotThrow (OSCAddress ("/"));
 | |
|             expectDoesNotThrow (OSCAddress ("/a"));
 | |
|             expectDoesNotThrow (OSCAddress ("/a/"));
 | |
|             expectDoesNotThrow (OSCAddress ("/a/bcd/"));
 | |
|             expectDoesNotThrow (OSCAddress ("/abcd/efgh/ijkLMNOPq/666r/s"));
 | |
|             expectDoesNotThrow (OSCAddress ("/allowedprintablecharacters!$%&()+-.^_`|~"));
 | |
|             expectDoesNotThrow (OSCAddress ("/additonalslashes//will///be////ignored"));
 | |
|         }
 | |
| 
 | |
|         beginTest ("conversion to/from String");
 | |
|         {
 | |
|             OSCAddress address ("/this/is/a/very/long/address/");
 | |
|             expectEquals (address.toString(), String ("/this/is/a/very/long/address"));
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| static OSCAddressTests OSCAddressUnitTests;
 | |
| 
 | |
| //==============================================================================
 | |
| 
 | |
| class OSCAddressPatternTests  : public UnitTest
 | |
| {
 | |
| public:
 | |
|     OSCAddressPatternTests()
 | |
|         : UnitTest ("OSCAddressPattern class", UnitTestCategories::osc)
 | |
|     {}
 | |
| 
 | |
|     void runTest()
 | |
|     {
 | |
|         beginTest ("construction and parsing");
 | |
|         {
 | |
|             expectThrowsType (OSCAddressPattern (""), OSCFormatError);
 | |
|             expectThrowsType (OSCAddressPattern ("noleadingslash"), OSCFormatError);
 | |
|             expectThrowsType (OSCAddressPattern ("/notallowedchar "), OSCFormatError);
 | |
|             expectThrowsType (OSCAddressPattern ("/notallowedchar#andsomemorechars"), OSCFormatError);
 | |
|             expectThrowsType (OSCAddressPattern (String::fromUTF8 ("/nonasciicharacter\xc3\xbc""blabla")), OSCFormatError);
 | |
|             expectThrowsType (OSCAddressPattern ("/nonprintableasciicharacter\t"), OSCFormatError);
 | |
| 
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/a"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/a/"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/a/bcd/"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/abcd/efgh/ijkLMNOPq/666r/s"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/allowedprintablecharacters!$%&()+-.:;<=>@^_`|~"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/additonalslashes//will///be////ignored"));
 | |
|         }
 | |
| 
 | |
|         beginTest ("construction and parsing - with wildcards");
 | |
|         {
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/foo/b?r/"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/?????"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/foo/b*r"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/**"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/?/b/*c"));
 | |
|         }
 | |
| 
 | |
|         beginTest ("construction and parsing - with match expressions");
 | |
|         {
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/{}"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/{foo}"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/{foo,bar,baz}"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/[]"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/[abcde]"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/[a-e]"));
 | |
|             expectDoesNotThrow (OSCAddressPattern ("/foo/[a-z]x{foo,bar}/*BAZ42/"));
 | |
| 
 | |
|             /* Note: If malformed expressions are used, e.g. "bracenotclosed{" or "{a-e}" or "[-foo]",
 | |
|                this should not throw at construction time. Instead it should simply fail any pattern match later.
 | |
|                So there is no need to test for those.
 | |
|                The reason is that we do not actually parse the expressions now, but only during matching.
 | |
|             */
 | |
|         }
 | |
| 
 | |
|         beginTest ("equality comparison");
 | |
|         {
 | |
|             {
 | |
|                 OSCAddressPattern lhs ("/test/1");
 | |
|                 OSCAddressPattern rhs ("/test/1");
 | |
|                 expect (lhs == rhs);
 | |
|                 expect (! (lhs != rhs));
 | |
|             }
 | |
|             {
 | |
|                 OSCAddressPattern lhs ("/test/1");
 | |
|                 OSCAddressPattern rhs ("/test/1/");
 | |
|                 expect (lhs == rhs);
 | |
|                 expect (! (lhs != rhs));
 | |
|             }
 | |
|             {
 | |
|                 OSCAddressPattern lhs ("/test/1");
 | |
|                 OSCAddressPattern rhs ("/test/2");
 | |
|                 expect (! (lhs == rhs));
 | |
|                 expect (lhs != rhs);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         beginTest ("basic string matching");
 | |
|         {
 | |
|             /* Note: The actual expression matching is tested in OSCPatternMatcher, so here we just
 | |
|                do some basic tests and check if the matching works with multi-part addresses.
 | |
|              */
 | |
|             {
 | |
|                 OSCAddressPattern pattern ("/foo/bar");
 | |
|                 expect (! pattern.containsWildcards());
 | |
| 
 | |
|                 OSCAddress address ("/foo/bar");
 | |
|                 expect (pattern.matches (address));
 | |
|             }
 | |
|             {
 | |
|                 OSCAddressPattern pattern ("/foo/bar/");
 | |
|                 expect (! pattern.containsWildcards());
 | |
| 
 | |
|                 OSCAddress address ("/foo/bar");
 | |
|                 expect (pattern.matches (address));
 | |
|             }
 | |
|             {
 | |
|                 OSCAddressPattern pattern ("/");
 | |
|                 expect (! pattern.containsWildcards());
 | |
| 
 | |
|                 OSCAddress address ("/");
 | |
|                 expect (pattern.matches (address));
 | |
|             }
 | |
|             {
 | |
|                 OSCAddressPattern pattern ("/foo/bar");
 | |
|                 expect (! pattern.containsWildcards());
 | |
| 
 | |
|                 expect (! pattern.matches (OSCAddress ("/foo/baz")));
 | |
|                 expect (! pattern.matches (OSCAddress ("/foo/bar/baz")));
 | |
|                 expect (! pattern.matches (OSCAddress ("/foo")));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         beginTest ("string matching with wildcards");
 | |
|         {
 | |
|             OSCAddressPattern pattern ("/*/*put/slider[0-9]");
 | |
|             expect (pattern.containsWildcards());
 | |
| 
 | |
|             expect (pattern.matches (OSCAddress ("/mypatch/input/slider0")));
 | |
|             expect (pattern.matches (OSCAddress ("/myotherpatch/output/slider9")));
 | |
|             expect (! pattern.matches (OSCAddress ("/myotherpatch/output/slider10")));
 | |
|             expect (! pattern.matches (OSCAddress ("/output/slider9")));
 | |
|             expect (! pattern.matches (OSCAddress ("/myotherpatch/output/slider9/position")));
 | |
|         }
 | |
| 
 | |
|         beginTest ("conversion to/from String");
 | |
|         {
 | |
|             {
 | |
|                 OSCAddressPattern ap ("/this/is/a/very/long/address/");
 | |
|                 expectEquals (ap.toString(), String ("/this/is/a/very/long/address"));
 | |
|             }
 | |
|             {
 | |
|                 OSCAddressPattern ap ("/*/*put/{fader,slider,knob}[0-9]/ba?/");
 | |
|                 expectEquals (ap.toString(), String ("/*/*put/{fader,slider,knob}[0-9]/ba?"));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| static OSCAddressPatternTests OSCAddressPatternUnitTests;
 | |
| 
 | |
| //==============================================================================
 | |
| 
 | |
| class OSCPatternMatcherTests : public UnitTest
 | |
| {
 | |
| public:
 | |
|     OSCPatternMatcherTests()
 | |
|         : UnitTest ("OSCAddress class / pattern matching", UnitTestCategories::osc)
 | |
|     {}
 | |
| 
 | |
|     void runTest()
 | |
|     {
 | |
|         beginTest ("basic string matching");
 | |
|         {
 | |
|             expect (matchOscPattern ("", ""));
 | |
|             expect (! matchOscPattern ("", "x"));
 | |
|             expect (! matchOscPattern ("x", ""));
 | |
|             expect (matchOscPattern ("foo", "foo"));
 | |
|             expect (! matchOscPattern ("foo", "bar"));
 | |
|             expect (! matchOscPattern ("ooooo", "oooooo"));
 | |
|         }
 | |
| 
 | |
|         beginTest ("string matching with '?' wildcard");
 | |
|         {
 | |
|             expect (matchOscPattern ("?", "x"));
 | |
|             expect (! matchOscPattern ("?", ""));
 | |
|             expect (! matchOscPattern ("?", "xx"));
 | |
|             expect (! matchOscPattern ("b?r", "br"));
 | |
|             expect (matchOscPattern ("b?r", "bar"));
 | |
|             expect (! matchOscPattern ("b?r", "baar"));
 | |
|             expect (matchOscPattern ("f???o", "fabco"));
 | |
|             expect (! matchOscPattern ("f???o", "fabo"));
 | |
|         }
 | |
| 
 | |
|         beginTest ("string matching with '*' wildcard");
 | |
|         {
 | |
|             expect (matchOscPattern ("*", ""));
 | |
|             expect (matchOscPattern ("*", "x"));
 | |
|             expect (matchOscPattern ("*", "foo"));
 | |
|             expect (matchOscPattern ("*c", "aaaaaaabc"));
 | |
|             expect (matchOscPattern ("*c", "aaaaaaabbbcccc"));
 | |
|             expect (! matchOscPattern ("*c", "aaaaaaabbbcccca"));
 | |
|             expect (matchOscPattern ("c*", "ccccbbbbaaaa"));
 | |
|             expect (! matchOscPattern ("c*", "accccbbbaaaa"));
 | |
| 
 | |
|             expect (matchOscPattern ("f*o", "fo"));
 | |
|             expect (matchOscPattern ("f*o", "fuo"));
 | |
|             expect (matchOscPattern ("f*o", "fuvwxyzo"));
 | |
| 
 | |
|             expect (matchOscPattern ("*reallyreallylongstringstringstring", "reallyreallylongstringstringstrNOT"
 | |
|                                                                             "reallyreallylongstringstringstrNOT"
 | |
|                                                                             "reallyreallylongstringstringstrNOT"
 | |
|                                                                             "reallyreallylongstringstringstrNOT"
 | |
|                                                                             "reallyreallylongstringstringstrNOT"
 | |
|                                                                             "reallyreallylongstringstringstring"));
 | |
|         }
 | |
| 
 | |
|         beginTest ("string matching with '{..., ...}' pattern");
 | |
|         {
 | |
|             expect (matchOscPattern ("{}", ""));
 | |
|             expect (! matchOscPattern ("{}", "x"));
 | |
|             expect (matchOscPattern ("{abcde}", "abcde"));
 | |
|             expect (matchOscPattern ("{abcde,f}", "f"));
 | |
|             expect (! matchOscPattern ("{abcde,f}", "ff"));
 | |
|             expect (matchOscPattern ("a{bcd}e", "abcde"));
 | |
|             expect (matchOscPattern ("a{bcd,bce}e", "abcde"));
 | |
|             expect (! matchOscPattern ("a{bce,bcf}e", "abcde"));
 | |
|             expect (! matchOscPattern ("a{bce,bcf}e", "ae"));
 | |
|             expect (matchOscPattern ("a{bce,,bcf}e", "ae"));
 | |
|             expect (matchOscPattern ("a{bcd,bcd,bcd}e", "abcde"));
 | |
|             expect (matchOscPattern ("aaa{bc,def,ghij,klmnopqrstu}eee", "aaaghijeee"));
 | |
|             expect (matchOscPattern ("{a,b,c}bcde", "abcde"));
 | |
|             expect (! matchOscPattern ("{abc}bcde", "abcde"));
 | |
|             expect (matchOscPattern ("bcde{a,b,c}", "bcdea"));
 | |
|             expect (! matchOscPattern ("bcde{abc}", "bcdea"));
 | |
|             expect (matchOscPattern ("f{o,}o", "fo"));
 | |
|             expect (matchOscPattern ("f{,,,,,}o", "fo"));
 | |
|             expect (matchOscPattern ("foo{b,ba,bar}x", "foobarx"));
 | |
|             expect (matchOscPattern ("a{bc,de}fg{hij,klm}{n}{}", "adefghijn"));
 | |
| 
 | |
|             // should fail gracefully in case of wrong syntax:
 | |
|             expect (! matchOscPattern ("not{closing", "notclosing"));
 | |
|             expect (! matchOscPattern ("not}opening", "notopening"));
 | |
|             expect (! matchOscPattern ("{{nested}}", "nested"));
 | |
|             expect (! matchOscPattern ("{a-c}bcde", "abcde"));
 | |
|             expect (! matchOscPattern ("bcde{a-c}", "abcde"));
 | |
|         }
 | |
| 
 | |
| 
 | |
|         beginTest ("string matching with '[...]' pattern");
 | |
|         {
 | |
|             // using [] for a set of chars:
 | |
| 
 | |
|             expect (matchOscPattern ("[]", ""));
 | |
|             expect (! matchOscPattern ("[]", "x"));
 | |
|             expect (! matchOscPattern ("[abcde]", "abcde"));
 | |
|             expect (matchOscPattern ("[abcde]", "a"));
 | |
|             expect (matchOscPattern ("[abcde]", "b"));
 | |
|             expect (matchOscPattern ("[abcde]", "c"));
 | |
|             expect (matchOscPattern ("[abcde]", "d"));
 | |
|             expect (matchOscPattern ("[abcde]", "e"));
 | |
|             expect (! matchOscPattern ("[abcde]", "f"));
 | |
| 
 | |
|             expect (matchOscPattern ("f[oo]", "fo"));
 | |
|             expect (! matchOscPattern ("f[oo]", "foo"));
 | |
| 
 | |
|             expect (matchOscPattern ("fooba[rxz]foo", "foobarfoo"));
 | |
|             expect (matchOscPattern ("fooba[rxz]foo", "foobaxfoo"));
 | |
|             expect (matchOscPattern ("fooba[rxz]foo", "foobazfoo"));
 | |
|             expect (! matchOscPattern ("fooba[rxz]foo", "foobasfoo"));
 | |
| 
 | |
|             expect (matchOscPattern ("foo[abc]foo[defgh]foo[i]foo[]foo", "foobfoohfooifoofoo"));
 | |
| 
 | |
|             // using [] for a range of chars:
 | |
| 
 | |
|             expect (matchOscPattern ("fooba[r-z]foo", "foobarfoo"));
 | |
|             expect (matchOscPattern ("fooba[r-z]foo", "foobaxfoo"));
 | |
|             expect (matchOscPattern ("fooba[r-z]foo", "foobazfoo"));
 | |
|             expect (matchOscPattern ("fooba[r-z]foo", "foobasfoo"));
 | |
|             expect (! matchOscPattern ("fooba[r-z]foo", "foobaRfoo"));
 | |
| 
 | |
|             expect (! matchOscPattern ("foo[1-8]bar", "foo0bar"));
 | |
|             expect (matchOscPattern ("foo[1-8]bar", "foo1bar"));
 | |
|             expect (matchOscPattern ("foo[1-8]bar", "foo6bar"));
 | |
|             expect (matchOscPattern ("foo[1-8]bar", "foo8bar"));
 | |
|             expect (! matchOscPattern ("foo[1-8]bar", "foo9bar"));
 | |
| 
 | |
|             // special case: '-' does not have a special meaning if it is at the end of the set.
 | |
| 
 | |
|             expect (matchOscPattern ("foo[abc-]bar", "fooabar"));
 | |
|             expect (matchOscPattern ("foo[abc-]bar", "foo-bar"));
 | |
|             expect (matchOscPattern ("foo[-]bar", "foo-bar"));
 | |
| 
 | |
|             // mixing both set and range:
 | |
| 
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]b[a-b]r", "fooabar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]b[a-a]r", "foobbar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]b[aaaa-aaaa-aaaa]r", "foodbar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooebar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foogbar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooibar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foojbar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fookbar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foolbar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooobar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foopbar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooubar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooybar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foozbar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo0bar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo1bar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo5bar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo8bar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "foo9bar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooCbar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooDbar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooEbar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooFbar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooGbar"));
 | |
|             expect (matchOscPattern ("foo[ae-iko-uz1-8D-FX]bar", "fooXbar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]ba[Rr]", "fooZbar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]ba[Rr]", "foobar"));
 | |
|             expect (! matchOscPattern ("foo[ae-iko-uz1-8D-FX]ba[Rr]", "fooFXbar"));
 | |
| 
 | |
|             // using [!...] for a negated set or range of chars:
 | |
| 
 | |
|             expect (! matchOscPattern ("fooba[!rxz]foo", "foobarfoo"));
 | |
|             expect (! matchOscPattern ("fooba[!rxz]foo", "foobaxfoo"));
 | |
|             expect (! matchOscPattern ("fooba[!rxz]foo", "foobazfoo"));
 | |
|             expect (matchOscPattern ("fooba[!rxz]foo", "foobasfoo"));
 | |
| 
 | |
|             expect (! matchOscPattern ("fooba[!r-z]foo", "foobarfoo"));
 | |
|             expect (! matchOscPattern ("fooba[!r-z]foo", "foobaxfoo"));
 | |
|             expect (! matchOscPattern ("fooba[!r-z]foo", "foobazfoo"));
 | |
|             expect (! matchOscPattern ("fooba[!r-z]foo", "foobasfoo"));
 | |
|             expect (matchOscPattern ("fooba[!r-z]foo", "foobaRfoo"));
 | |
| 
 | |
|             // special case: '!' does not have a special meaning if it is not the first char in the set.
 | |
| 
 | |
|             expect (matchOscPattern ("foo[ab!c]bar", "fooabar"));
 | |
|             expect (matchOscPattern ("foo[ab!c]bar", "foo!bar"));
 | |
|             expect (! matchOscPattern ("foo[ab!c]bar", "fooxbar"));
 | |
|             expect (! matchOscPattern ("foo[!!]bar", "foo!bar"));
 | |
|             expect (matchOscPattern ("foo[!!]bar", "fooxbar"));
 | |
|             expect (! matchOscPattern ("foo[!!]bar", "foobar"));
 | |
| 
 | |
|             // should fail gracefully in case of wrong syntax:
 | |
| 
 | |
|             expect (! matchOscPattern ("notclosin[g", "notclosing"));
 | |
|             expect (! matchOscPattern ("n]otopening", "notopening"));
 | |
|             expect (! matchOscPattern ("[[nested]]", "nested"));
 | |
|             expect (! matchOscPattern ("norangestar[-t]", "norangestart"));
 | |
|             expect (! matchOscPattern ("norangestar[-t]", "norangestar-"));
 | |
|         }
 | |
| 
 | |
|         beginTest ("string matching combining patterns");
 | |
|         {
 | |
|             expect (matchOscPattern ("*ea*ll[y-z0-9X-Zvwx]??m[o-q]l[e]x{fat,mat,pat}te{}r*?", "reallycomplexpattern"));
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| static OSCPatternMatcherTests OSCPatternMatcherUnitTests;
 | |
| 
 | |
| #endif
 | |
| 
 | |
| } // namespace juce
 |