""" Java regex standard library APIs implemented in Python. java.util.regex package - Pattern, Matcher, MatchResult, PatternSyntaxException. """ from .._core import Stub from .io import IOException from .lang import RuntimeException import re as _re IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) IllegalStateException = type("IllegalStateException", (RuntimeException,), {}) __all__ = [ "Pattern", "Matcher", "MatchResult", "PatternSyntaxException", ] # ============================================================ # PatternSyntaxException # ============================================================ class PatternSyntaxException(IllegalArgumentException): def __init__(self, desc, regex, index): super().__init__(f"{desc}: {regex} at index {index}") self._desc = desc self._regex = regex self._index = index def getDescription(self): return self._desc def getPattern(self): return self._regex def getIndex(self): return self._index def getMessage(self): return f"{self._desc} near index {self._index}: {self._regex}" # ============================================================ # MatchResult # ============================================================ class MatchResult(Stub): def start(self, group=None): raise NotImplementedError def end(self, group=None): raise NotImplementedError def group(self, group=None): raise NotImplementedError def groupCount(self): raise NotImplementedError # ============================================================ # Matcher # ============================================================ class Matcher(MatchResult): def __init__(self, pattern, input_str): super().__init__() self._pattern = pattern self._input = input_str self._re_pattern = pattern._pattern self._match = None self._region_start = 0 self._region_end = len(input_str) self._transparent_bounds = False self._anchoring_bounds = True self._last_match = None def matches(self): self._match = self._re_pattern.fullmatch(self._input, self._region_start, self._region_end) self._last_match = self._match return self._match is not None def find(self, start=None): if start is not None: self._match = self._re_pattern.search(self._input, start, self._region_end) else: if self._match is None: self._match = self._re_pattern.search(self._input, self._region_start, self._region_end) else: pos = self._match.end() self._match = self._re_pattern.search(self._input, pos, self._region_end) if self._match: self._last_match = self._match return self._match is not None def lookingAt(self): self._match = self._re_pattern.match(self._input, self._region_start, self._region_end) self._last_match = self._match return self._match is not None def start(self, group=0): if self._match is None: raise IllegalStateException("No match available") if group == 0: return self._match.start() return self._match.start(group) def end(self, group=0): if self._match is None: raise IllegalStateException("No match available") if group == 0: return self._match.end() return self._match.end(group) def group(self, group=0): if self._match is None: raise IllegalStateException("No match available") if isinstance(group, str): return self._match.group(group) return self._match.group(group) def groupCount(self): return self._re_pattern.groups def replaceAll(self, replacement): return self._re_pattern.sub(replacement, self._input) def replaceFirst(self, replacement): return self._re_pattern.sub(replacement, self._input, count=1) def appendReplacement(self, sb, replacement): if self._match is None: raise IllegalStateException("No match available") self._append_pos = getattr(self, '_append_pos', 0) sb.append(self._input[self._append_pos:self._match.start()]) sb.append(self._re_pattern.sub(replacement, self._match.group(), count=1)) self._append_pos = self._match.end() return self def appendTail(self, sb): self._append_pos = getattr(self, '_append_pos', 0) sb.append(self._input[self._append_pos:]) return sb def reset(self, input_str=None): if input_str is not None: self._input = input_str self._region_end = len(input_str) self._match = None self._last_match = None return self def region(self, start, end): self._region_start = start self._region_end = end return self def regionStart(self): return self._region_start def regionEnd(self): return self._region_end def hasTransparentBounds(self): return self._transparent_bounds def hasAnchoringBounds(self): return self._anchoring_bounds def useAnchoringBounds(self, value): self._anchoring_bounds = value return self def useTransparentBounds(self, value): self._transparent_bounds = value return self def hitEnd(self): return self._match is not None and self._match.end() >= self._region_end def requireEnd(self): return self._match is not None and not self.hitEnd() def toMatchResult(self): result = Matcher(self._pattern, self._input) result._match = self._match result._last_match = self._last_match return result def pattern(self): return self._pattern def toString(self): return f"Matcher[pattern={self._pattern.pattern()}]" def namedGroups(self): return self._re_pattern.groupindex class IllegalStateException(RuntimeException): pass # ============================================================ # Pattern # ============================================================ class Pattern(Stub): CANON_EQ = 128 CASE_INSENSITIVE = 2 COMMENTS = 4 DOTALL = 32 LITERAL = 16 MULTILINE = 8 UNICODE_CASE = 64 UNICODE_CHARACTER_CLASS = 256 UNIX_LINES = 1 _re_flags_map = { 0: 0, # default 2: _re.IGNORECASE, 8: _re.MULTILINE, 32: _re.DOTALL, 64: _re.UNICODE, 4: _re.VERBOSE, } def __init__(self, regex, flags=0): super().__init__() self._regex = str(regex) self._flags = flags re_flags = 0 for java_flag, py_flag in self._re_flags_map.items(): if flags & java_flag: re_flags |= py_flag try: self._pattern = _re.compile(self._regex, re_flags) except _re.error as e: raise PatternSyntaxException(str(e), self._regex, e.pos if hasattr(e, 'pos') else 0) @staticmethod def compile(regex, flags=0): return Pattern(regex, flags) def matcher(self, input_str): return Matcher(self, input_str) def split(self, input_str, limit=0): if limit <= 0: parts = self._pattern.split(str(input_str)) if limit == 0: while parts and parts[-1] == "": parts.pop() else: parts = self._pattern.split(str(input_str), maxsplit=limit - 1) while len(parts) < limit: parts.append("") return parts def splitAsStream(self, input_str): return iter(self.split(input_str)) @staticmethod def matches(regex, input_str): return bool(_re.fullmatch(regex, str(input_str))) @staticmethod def quote(s): return _re.escape(str(s)) def flags(self): return self._flags def pattern(self): return self._regex def toString(self): return self._regex def predicate(self): return lambda s: bool(self._pattern.fullmatch(str(s))) def asPredicate(self): return self.predicate() def asMatchPredicate(self): return lambda s: bool(self._pattern.fullmatch(str(s)))