Skip to content

Commit be7c673

Browse files
Add abbreviate method to StringUtils, More enhancements (#10)
* Add abbreviate method to StringUtils * Fix code hygeine issues * Fix code hygeine issues
1 parent 70a99bb commit be7c673

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

pycommons/lang/function/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
from .consumer import Consumer, BiConsumer
44
from .function import Function
5-
from .predicate import Predicate
5+
from .predicate import Predicate, BiPredicate
66
from .runnable import Runnable
77
from .supplier import Supplier
88

9-
__all__ = ["BiConsumer", "Consumer", "Function", "Predicate", "Runnable", "Supplier"]
9+
__all__ = ["BiConsumer", "Consumer", "Function", "Predicate", "Runnable", "Supplier", "BiPredicate"]

pycommons/lang/function/predicate.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pycommons.lang.utils.objectutils import ObjectUtils
77

88
_T = TypeVar("_T")
9+
_U = TypeVar("_U")
910

1011

1112
class Predicate(Generic[_T]):
@@ -36,6 +37,34 @@ def __call__(self, t: _T, *args: Any, **kwargs: Any) -> bool:
3637
return self.test(t)
3738

3839

40+
class BiPredicate(Generic[_T, _U]):
41+
@classmethod
42+
def of(cls, predicate: Callable[[_T, _U], bool]) -> BiPredicate[_T, _U]:
43+
ObjectUtils.require_not_none(predicate)
44+
45+
class BasicBiPredicate(BiPredicate[_T, _U]):
46+
def test(self, t: _T, u: _U) -> bool:
47+
return predicate(t, u)
48+
49+
return BasicBiPredicate()
50+
51+
@abstractmethod
52+
def test(self, t: _T, u: _U) -> bool:
53+
pass
54+
55+
def negate(self) -> BiPredicate[_T, _U]:
56+
return self.of(lambda _t, _u: not self.test(_t, _u))
57+
58+
def do_and(self, predicate: BiPredicate[_T, _U]) -> BiPredicate[_T, _U]:
59+
return self.of(lambda _t, _u: self.test(_t, _u) and predicate.test(_t, _u))
60+
61+
def do_or(self, predicate: BiPredicate[_T, _U]) -> BiPredicate[_T, _U]:
62+
return self.of(lambda _t, _u: self.test(_t, _u) or predicate.test(_t, _u))
63+
64+
def __call__(self, t: _T, u: _U, *args: Any, **kwargs: Any) -> bool:
65+
return self.test(t, u)
66+
67+
3968
class PassingPredicate(Predicate[_T]):
4069
def test(self, value: _T) -> bool:
4170
return True

pycommons/lang/utils/stringutils.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,66 @@ class in the Apache Commons Lang package. Provides `None` safe methods to perfor
2222

2323
EMPTY: str = ""
2424

25+
@classmethod
26+
def abbreviate(
27+
cls,
28+
char_sequence: Optional[str],
29+
abbrev_marker: Optional[str] = "...",
30+
offset: int = 0,
31+
max_width: int = -1,
32+
) -> Optional[str]:
33+
if cls.is_not_empty(char_sequence) and cls.EMPTY == abbrev_marker and max_width > 0:
34+
return typing.cast(str, char_sequence)[:max_width]
35+
36+
if cls.is_any_empty(char_sequence, abbrev_marker):
37+
return char_sequence
38+
39+
char_sequence = typing.cast(str, abbrev_marker)
40+
abbrev_marker = typing.cast(str, abbrev_marker)
41+
abbrev_marker_length = len(abbrev_marker)
42+
min_abbrev_width = abbrev_marker_length + 1
43+
min_abbrev_width_offset = abbrev_marker_length + abbrev_marker_length + 1
44+
45+
if max_width == -1:
46+
max_width = len(char_sequence) - abbrev_marker_length
47+
48+
if max_width < min_abbrev_width:
49+
raise ValueError(f"Minimum abbreviation width is {min_abbrev_width}")
50+
51+
str_len = len(char_sequence)
52+
if str_len <= max_width:
53+
return char_sequence
54+
55+
offset = min(offset, str_len)
56+
57+
if str_len - offset < max_width - abbrev_marker_length:
58+
offset = str_len - (max_width - abbrev_marker_length)
59+
60+
if offset <= abbrev_marker_length + 1:
61+
return char_sequence[: max_width - abbrev_marker_length] + abbrev_marker
62+
63+
if max_width < min_abbrev_width_offset:
64+
raise ValueError(f"Minimum abbreviation width with offset is {min_abbrev_width_offset}")
65+
return char_sequence[offset : offset + max_width - abbrev_marker_length] + abbrev_marker
66+
67+
@classmethod
68+
def abbreviate_middle(
69+
cls, char_sequence: Optional[str], middle: Optional[str] = "...", length: int = 5
70+
) -> Optional[str]:
71+
if (
72+
not cls.is_any_empty(char_sequence, middle)
73+
and len(typing.cast(str, char_sequence)) > length >= len(typing.cast(str, middle)) + 2
74+
):
75+
middle = typing.cast(str, middle)
76+
char_sequence = typing.cast(str, char_sequence)
77+
78+
target_string = length - len(middle)
79+
start_offset = target_string // 2 + target_string % 2
80+
end_offset = len(char_sequence) - target_string // 2
81+
return char_sequence[:start_offset] + middle + char_sequence[end_offset:]
82+
83+
return char_sequence
84+
2585
@classmethod
2686
def contains(cls, char_sequence: Optional[str], search_string: Optional[str]) -> bool:
2787
if char_sequence is not None and search_string is not None:

0 commit comments

Comments
 (0)