protected AntPathStringMatcher getStringMatcher(String pattern) {
        AntPathStringMatcher matcher = null;
        Boolean cachepatterns = this.cachePatterns;
        if (cachepatterns == null || cachepatterns.booleanValue()) {
            matcher = this.stringMatcherCache.get(pattern);
        }
        if (matcher == null) {
            matcher = new AntPathStringMatcher(pattern, true);
            if (cachepatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) {
                // Try to adapt to the runtime situation that we're encountering:
                // There are obviously too many different patterns coming in here...
                // So let's turn off the cache since the patterns are unlikely to be reoccurring.
                deactivatePatternCache();
                return matcher;
            }
            if (cachepatterns == null || cachepatterns.booleanValue()) {
                this.stringMatcherCache.put(pattern, matcher);
            }
        }
        return matcher;
    }
Build or retrieve an {link AntPathStringMatcher} for the given pattern.
<p>The default implementation checks this AntPathMatcher's internal cache
(see {link #setCachePatterns}), creating a new AntPathStringMatcher instance
if no cached copy is found.
<p>When encountering too many patterns to cache at runtime (the threshold is 65536),
it turns the default cache off, assuming that arbitrary permutations of patterns
are coming in, with little chance for encountering a recurring pattern.
<p>This method may be overridden to implement a custom cache strategy.

@param pattern the pattern to match against (never {@code null})
@return a corresponding AntPathStringMatcher (never {@code null})
see #setCachePatterns
analyzer/src/main/java/nl/basjes/parse/useragent/utils/springframework/util/AntPathMatcher.java