#REFACTORING by cg
class: StringUtilities class
added: #stringMatchFunctionFor:glob:regex:caseSensitive:
refactored: multipattern search is useful for others
--- a/StringUtilities.st Thu Apr 04 10:48:03 2019 +0200
+++ b/StringUtilities.st Thu May 02 17:35:13 2019 +0200
@@ -318,6 +318,155 @@
"Modified (comment): / 09-08-2012 / 05:40:08 / cg"
! !
+!StringUtilities class methodsFor:'matching'!
+
+stringMatchFunctionFor:aMultiPattern glob:searchForGlobPattern regex:searchForRegexPattern caseSensitive:searchIsCaseSensitive
+ "generates a check function which - given a string - checks for a match;
+ The match may contain multiple patterns, separated by
+ ';' (for and-search) or '|' (for or-search).
+ If the pattern is invalid, nil is returned and an Information-notification
+ is signalled"
+
+ |combinator stringToSearchFor stringsToSearchFor checkFunction|
+
+ stringToSearchFor := aMultiPattern.
+ combinator := #conform:.
+ (stringToSearchFor includes:$|) ifTrue:[
+ stringsToSearchFor := stringToSearchFor asCollectionOfSubstringsSeparatedBy:$|.
+ combinator := #contains:.
+ ] ifFalse:[
+ (stringToSearchFor includes:$;) ifTrue:[
+ stringsToSearchFor := stringToSearchFor asCollectionOfSubstringsSeparatedBy:$;.
+ ] ifFalse:[
+ stringsToSearchFor := nil.
+"/ lastStringToSearchFor := self stringOfCurrentSearchList.
+"/ (lastStringToSearchFor notEmptyOrNil and:[stringToSearchFor startsWith:lastStringToSearchFor]) ifTrue:[
+"/ searchRoot := AbstractBrowserItemPO new.
+"/ self searchResultList do:[:each | searchRoot add:each treePO].
+"/ self stringOfCurrentSearchList:stringToSearchFor.
+"/ Transcript showCR:'fast'.
+"/ ].
+"/ self stringOfCurrentSearchList:stringToSearchFor.
+ ]
+ ].
+
+ Regex::RxParser regexErrorSignal handle:[:ex |
+ self notify:('Invalid regex Pattern.').
+ ^ nil
+ ] do:[
+ |lcString shortString regexPattern regexPatterns|
+
+ stringsToSearchFor isNil ifTrue:[
+ "/ single string search
+
+ (searchForGlobPattern and:[stringToSearchFor includesMatchCharacters]) ifTrue:[
+ |matchScanArray|
+
+ "/ verify if this is a valid pattern; if not, do not search
+ matchScanArray := String matchScanArrayFrom:stringToSearchFor escapeCharacter:String matchEscapeCharacter.
+ matchScanArray isNil ifTrue:[
+ self information:'Invalid search pattern'.
+ ^ nil.
+ ].
+
+ ((stringToSearchFor startsWith:$*) and:[(stringToSearchFor copyFrom:2) includesMatchCharacters not]) ifTrue:[
+ shortString := (stringToSearchFor copyFrom:2).
+ searchIsCaseSensitive ifTrue:[
+ checkFunction := [:s | s endsWith:shortString].
+ ] ifFalse:[
+ lcString := shortString asLowercase.
+ checkFunction := [:s | s asLowercase endsWith:lcString].
+ ]
+ ] ifFalse:[
+ ((stringToSearchFor endsWith:$*) and:[(stringToSearchFor copyButLast:1) includesMatchCharacters not]) ifTrue:[
+ shortString := (stringToSearchFor copyButLast:1).
+ searchIsCaseSensitive ifTrue:[
+ checkFunction := [:s | s startsWith:shortString].
+ ] ifFalse:[
+ lcString := shortString asLowercase.
+ checkFunction := [:s | s asLowercase startsWith:lcString].
+ ]
+ ] ifFalse:[
+ regexPattern := searchIsCaseSensitive ifTrue:[stringToSearchFor asRegex] ifFalse:[stringToSearchFor asRegexIgnoringCase].
+ checkFunction := [:s | regexPattern hasMatchesIn:s].
+ ].
+ ].
+ ] ifFalse:[
+ searchForRegexPattern ifTrue:[
+ regexPattern := searchIsCaseSensitive ifTrue:[stringToSearchFor asRegex] ifFalse:[stringToSearchFor asRegexIgnoringCase].
+ checkFunction := [:s | regexPattern hasMatchesIn:s].
+ ] ifFalse:[
+ checkFunction :=
+ [:s | s includesString:stringToSearchFor caseSensitive:searchIsCaseSensitive]
+ ]
+ ].
+ ] ifFalse:[
+ "/ multi string search
+
+ searchForGlobPattern ifTrue:[
+ stringsToSearchFor do:[:eachStringToSearchFor |
+ |matchScanArray|
+
+ "/ verify if this is a valid pattern; if not, do not search
+ matchScanArray := String matchScanArrayFrom:eachStringToSearchFor escapeCharacter:String matchEscapeCharacter.
+ matchScanArray isNil ifTrue:[
+ self information:'Invalid search pattern'.
+ ^ nil.
+ ].
+ ].
+
+ checkFunction :=
+ [:s |
+ stringsToSearchFor perform:combinator with:[:eachStringToSearchFor |
+ s includesMatchString:eachStringToSearchFor caseSensitive:searchIsCaseSensitive
+ ]
+ ].
+ ] ifFalse:[
+ searchForRegexPattern ifTrue:[
+ regexPatterns := stringsToSearchFor collect:[:eachPatternString|
+ searchIsCaseSensitive ifTrue:[eachPatternString asRegex] ifFalse:[eachPatternString asRegexIgnoringCase].
+ ].
+ checkFunction := [:s | regexPattern hasMatchesIn:s].
+ checkFunction :=
+ [:s |
+ regexPatterns perform:combinator with:[:eachRegexPattern |
+ eachRegexPattern hasMatchesIn:s
+ ]
+ ].
+ ] ifFalse:[
+ checkFunction :=
+ [:s |
+ stringsToSearchFor perform:combinator with:[:eachStringToSearchFor |
+ s includesString:eachStringToSearchFor caseSensitive:searchIsCaseSensitive
+ ]
+ ]
+ ]
+ ].
+ ].
+ ].
+ ^ checkFunction
+
+ "
+ |fn|
+ fn := self stringMatchFunctionFor:'aaa|bbb' glob:false regex:false caseSensitive:false.
+ fn value:' aaa '.
+ fn value:' aa '.
+ fn value:' bbb '.
+ fn value:' aa bb '.
+
+ fn := self stringMatchFunctionFor:'aaa;bbb' glob:false regex:false caseSensitive:false.
+ fn value:' aaa '.
+ fn value:' aa '.
+ fn value:' bbb '.
+ fn value:' aa bb '.
+ fn value:' aaa bb '.
+ fn value:' aaa bbb '.
+
+ "
+
+ "Created: / 02-05-2019 / 17:32:12 / Claus Gittinger"
+! !
+
!StringUtilities class methodsFor:'queries'!
isUtilityClass