/* Copyright 2010 Aaron J. Radke Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package cc.drx object Glob{ /**smart vim/magic like constructor to not ignore the case if an upper case character exists in the pattern*/ def apply(glob:String):Glob = new Glob(glob, !glob.exists{c => c >= 'A' && c <= 'Z'}) def ignoreCase(glob:String):Glob = new Glob(glob, true) def matchCase(glob:String):Glob = new Glob(glob, false) //def apply(glob:String,ignoreCase:Boolean):Glob = new Glob(glob,ignoreCase) implicit object ParsableGlob extends Parsable[Glob]{ def apply(glob:String):Glob = Glob(glob) } final class GlobStringContext(val sc:StringContext) extends AnyVal{ def glob(args:Any*):Glob = Glob(sc.s(args:_*)) } } /**Glob version of a regex where * zero or more char and ? is any single character * see https://en.wikipedia.org/wiki/Glob_(programming) */ class Glob(val glob: String, useIgnoreCase:Boolean, starPat:String="""[^\\/]"""){ override def toString = "Glob." + (if(useIgnoreCase) "ignoreCase" else "matchCase") + "(" + glob + ")" def ignoreCase:Glob = new Glob(glob, true) def matchCase:Glob = new Glob(glob, false) //--List of characters that need to be escaped in regex https://stackoverflow.com/a/26228852/622016 lazy val regex:scala.util.matching.Regex = { val pat = glob .trim .replace("\\*","@LiteralStar") .replace("\\?","@LiteralQuestion") .replace("\\\\","@LiteralBackslash") .replace("**","(Invalid)") .replace("*","@SingleStar") .replace("\\","\\\\") //literal slash .replace(".","\\.") //literal dot .replace("-","\\-") //stop the - character .replace("?","(.?)") //zero or one .replace("@LiteralStar","\\*") .replace("@LiteralQuestion","\\?") .replace("@LiteralBackslash","\\") .replace("@SingleStar",s"($starPat*)") //match zero or more equivocally .replace("(Invalid)","(.*)") //match zero or more (unequivocally) val flags = if(useIgnoreCase) "(?i)" else "" ("^" + flags + pat + "$").r //use ^ and $ to anchor the match to the full string } def matches(str:String):Boolean = find(str).isDefined def doesNotMatch(str:String):Boolean = !matches(str) def find(str:String) = regex findFirstMatchIn str def subgroups(str:String):Option[List[String]] = regex.findFirstMatchIn(str).map{_.subgroups} //retrieve subgroups of a match def unapplySeq(str:String):Option[List[String]] = subgroups(str) // def unapply(str:String):Option[String] = if(matches(str)) Some(str) else None //conflicts with unnaplySeq }