1
- # Copyright (c) 2022 Oracle and/or its affiliates.
1
+ # Copyright (c) 2022, 2023, Oracle and/or its affiliates.
2
2
# Licensed under the Universal Permissive License v 1.0 as shown at
3
3
# https://oss.oracle.com/licenses/upl.
4
4
5
5
from __future__ import annotations
6
6
7
7
from abc import ABC , abstractmethod
8
- from typing import Any , Optional , Sequence , TypeVar , cast
8
+ from typing import Any , Generic , Optional , Sequence , TypeAlias , TypeVar , cast
9
9
10
10
from coherence .serialization import proxy
11
11
12
- K = TypeVar ("K" , covariant = True )
13
- V = TypeVar ("V" , covariant = True )
14
- R = TypeVar ("R" , covariant = True )
12
+ E = TypeVar ("E" )
13
+ K = TypeVar ("K" )
14
+ V = TypeVar ("V" )
15
+ R = TypeVar ("R" )
16
+ T = TypeVar ("T" )
15
17
16
18
17
- class ValueExtractor (ABC ):
19
+ class ValueExtractor (ABC , Generic [ T , E ] ):
18
20
def __init__ (self ) -> None :
19
21
"""
20
22
ValueExtractor is used to both extract values (for example, for sorting or filtering) from an object,
@@ -24,7 +26,7 @@ def __init__(self) -> None:
24
26
"""
25
27
super ().__init__ ()
26
28
27
- def compose (self , before : ValueExtractor ) -> ValueExtractor :
29
+ def compose (self , before : ValueExtractor [ T , E ] ) -> ValueExtractor [ T , E ] :
28
30
"""
29
31
Returns a composed extractor that first applies the *before* extractor to its input, and then applies this
30
32
extractor to the result. If evaluation of either extractor throws an exception, it is relayed to the caller
@@ -40,7 +42,7 @@ def compose(self, before: ValueExtractor) -> ValueExtractor:
40
42
else :
41
43
return ChainedExtractor ([before , self ])
42
44
43
- def and_then (self , after : ValueExtractor ) -> ValueExtractor :
45
+ def and_then (self , after : ValueExtractor [ T , E ] ) -> ValueExtractor [ T , E ] :
44
46
"""
45
47
Returns a composed extractor that first applies this extractor to its input, and then applies the *after*
46
48
extractor to the result. If evaluation of either extractor throws an exception, it is relayed to the caller
@@ -57,19 +59,19 @@ def and_then(self, after: ValueExtractor) -> ValueExtractor:
57
59
return after .compose (self )
58
60
59
61
@classmethod
60
- def extract (cls , from_field_or_method : str , params : Optional [list [Any ]] = None ) -> ValueExtractor :
62
+ def extract (cls , from_field_or_method : str , params : Optional [list [Any ]] = None ) -> ValueExtractor [ T , E ] :
61
63
"""
62
64
Returns an extractor that extracts the value of the specified field.
63
65
64
- :param from_field_or_method: he name of the field or method to extract the value from.
66
+ :param from_field_or_method: the name of the field or method to extract the value from.
65
67
:param params: the parameters to pass to the method.
66
- :return: an instance of :func :`coherence.extractor.UniversalExtractor`
68
+ :return: an instance of :class :`coherence.extractor.UniversalExtractor`
67
69
"""
68
70
return UniversalExtractor (from_field_or_method , params )
69
71
70
72
71
73
@proxy ("extractor.UniversalExtractor" )
72
- class UniversalExtractor (ValueExtractor ):
74
+ class UniversalExtractor (ValueExtractor [ T , Any ] ):
73
75
def __init__ (self , name : str , params : Optional [list [Any ]] = None ) -> None :
74
76
"""
75
77
Universal ValueExtractor implementation.
@@ -85,29 +87,29 @@ def __init__(self, name: str, params: Optional[list[Any]] = None) -> None:
85
87
this extractor is considered a method extractor.
86
88
87
89
:param name: A method or property name.
88
- :param params: he parameter array. Must be `null` or `zero length` for a property based extractor.
90
+ :param params: the parameter array. Must be `null` or `zero length` for a property based extractor.
89
91
"""
90
92
super ().__init__ ()
91
93
self .name : str = name
92
94
self .params = params
93
95
94
96
@classmethod
95
- def create (cls , name : str , params : Optional [list [Any ]] = None ) -> UniversalExtractor :
97
+ def create (cls , name : str , params : Optional [list [Any ]] = None ) -> UniversalExtractor [ T ] :
96
98
"""
97
- Class method to create an instance of :func :`coherence.extractor.UniversalExtractor`
99
+ Class method to create an instance of :class :`coherence.extractor.UniversalExtractor`
98
100
99
101
:param name: A method or property name.
100
- :param params: he parameter array. Must be `null` or `zero length` for a property based extractor.
101
- :return: an instance of :func :`coherence.extractor.UniversalExtractor`
102
+ :param params: the parameter array. Must be `null` or `zero length` for a property based extractor.
103
+ :return: an instance of :class :`coherence.extractor.UniversalExtractor`
102
104
"""
103
105
return cls (name , params )
104
106
105
107
106
- class AbstractCompositeExtractor (ValueExtractor ):
107
- def __init__ (self , extractors : Sequence [ValueExtractor ]) -> None :
108
+ class AbstractCompositeExtractor (ValueExtractor [ T , E ] ):
109
+ def __init__ (self , extractors : Sequence [ValueExtractor [ T , E ] ]) -> None :
108
110
"""
109
- Abstract super class for :func :`coherence.extractor.ValueExtractor` implementations that are based on an
110
- underlying array of :func :`coherence.extractor.ValueExtractor` objects.
111
+ Abstract super class for :class :`coherence.extractor.ValueExtractor` implementations that are based on an
112
+ underlying array of :class :`coherence.extractor.ValueExtractor` objects.
111
113
112
114
:param extractors: an array of extractors
113
115
"""
@@ -116,35 +118,58 @@ def __init__(self, extractors: Sequence[ValueExtractor]) -> None:
116
118
117
119
118
120
@proxy ("extractor.ChainedExtractor" )
119
- class ChainedExtractor (AbstractCompositeExtractor ):
120
- def __init__ (self , extractors_or_method : str | Sequence [ValueExtractor ]) -> None :
121
+ class ChainedExtractor (AbstractCompositeExtractor [ T , Any ] ):
122
+ def __init__ (self , extractors_or_method : str | Sequence [ValueExtractor [ T , Any ] ]) -> None :
121
123
"""
122
- Composite :func :`coherence.extractor.ValueExtractor` implementation based on an array of extractors. The
124
+ Composite :class :`coherence.extractor.ValueExtractor` implementation based on an array of extractors. The
123
125
extractors in the array are applied sequentially left-to-right, so a result of a previous extractor serves as
124
126
a target object for a next one.
125
127
126
- :param extractors_or_method: an array of :func :`coherence.extractor.ValueExtractor`, or a dot-delimited
128
+ :param extractors_or_method: an array of :class :`coherence.extractor.ValueExtractor`, or a dot-delimited
127
129
sequence of method names which results in a ChainedExtractor that is based on an array of corresponding
128
- :func :`coherence.extractor.UniversalExtractor` objects
130
+ :class :`coherence.extractor.UniversalExtractor` objects
129
131
"""
130
132
if type (extractors_or_method ) == str :
131
133
e = list ()
132
134
names = extractors_or_method .split ("." )
133
135
for name in names :
134
- v = UniversalExtractor (name )
136
+ v : UniversalExtractor [ T ] = UniversalExtractor (name )
135
137
e .append (v )
136
138
super ().__init__ (e )
137
139
else :
138
- super ().__init__ (cast (Sequence [ValueExtractor ], extractors_or_method ))
140
+ super ().__init__ (cast (Sequence [ValueExtractor [T , Any ]], extractors_or_method ))
141
+
142
+
143
+ @proxy ("extractor.MultiExtractor" )
144
+ class MultiExtractor (AbstractCompositeExtractor [Any , Any ]):
145
+ def __init__ (self , extractors_or_method : str | Sequence [ValueExtractor [Any , Any ]]) -> None :
146
+ """
147
+ Composite :class:`coherence.extractor.ValueExtractor` implementation based on an array of extractors. The
148
+ extractors in the array are applied sequentially left-to-right, so a result of a previous extractor serves as
149
+ a target object for a next one.
150
+
151
+ :param extractors_or_method: an array of :class:`coherence.extractor.ValueExtractor`, or a dot-delimited
152
+ sequence of method names which results in a ChainedExtractor that is based on an array of corresponding
153
+ :class:`coherence.extractor.UniversalExtractor` objects
154
+ """
155
+ if type (extractors_or_method ) == str :
156
+ e = list ()
157
+ names = extractors_or_method .split ("," )
158
+ for name in names :
159
+ v : UniversalExtractor [Any ] = UniversalExtractor (name )
160
+ e .append (v )
161
+ super ().__init__ (e )
162
+ else :
163
+ super ().__init__ (cast (Sequence [ValueExtractor [Any , Any ]], extractors_or_method ))
139
164
140
165
141
166
@proxy ("extractor.IdentityExtractor" )
142
- class IdentityExtractor (ValueExtractor ):
167
+ class IdentityExtractor (ValueExtractor [ T , Any ] ):
143
168
__instance = None
144
169
145
170
def __init__ (self ) -> None :
146
171
"""
147
- A Trivial :func :`coherence.extractor.ValueExtractor` implementation that does not actually extract anything
172
+ A Trivial :class :`coherence.extractor.ValueExtractor` implementation that does not actually extract anything
148
173
from the passed value, but returns the value itself.
149
174
150
175
Constructs a new `IdentityExtractor` instance.
@@ -163,11 +188,11 @@ def __init__(self) -> None:
163
188
164
189
165
190
class ValueManipulator (ValueUpdater ):
166
- """ValueManipulator represents a composition of :func :`coherence.extractor.ValueExtractor` and
167
- :func :`coherence.extractor.ValueUpdater` implementations."""
191
+ """ValueManipulator represents a composition of :class :`coherence.extractor.ValueExtractor` and
192
+ :class :`coherence.extractor.ValueUpdater` implementations."""
168
193
169
194
@abstractmethod
170
- def get_extractor (self ) -> ValueExtractor :
195
+ def get_extractor (self ) -> ValueExtractor [ T , E ] :
171
196
"""
172
197
Retrieve the underlying ValueExtractor reference.
173
198
@@ -188,7 +213,7 @@ class CompositeUpdater(ValueManipulator):
188
213
"""A ValueUpdater implementation based on an extractor-updater pair that could also be used as a
189
214
ValueManipulator."""
190
215
191
- def __init__ (self , method_or_extractor : str | ValueExtractor , updater : Optional [ValueUpdater ] = None ) -> None :
216
+ def __init__ (self , method_or_extractor : ExtractorExpression [ T , E ] , updater : Optional [ValueUpdater ] = None ) -> None :
192
217
"""
193
218
Constructs a new `CompositeUpdater`.
194
219
@@ -197,7 +222,7 @@ def __init__(self, method_or_extractor: str | ValueExtractor, updater: Optional[
197
222
"""
198
223
super ().__init__ ()
199
224
if updater is not None : # Two arg constructor
200
- self .extractor = cast (ValueExtractor , method_or_extractor )
225
+ self .extractor = cast (ValueExtractor [ T , E ] , method_or_extractor )
201
226
self .updater = updater
202
227
else : # One arg with method name
203
228
last = str (method_or_extractor ).rfind ("." )
@@ -207,7 +232,7 @@ def __init__(self, method_or_extractor: str | ValueExtractor, updater: Optional[
207
232
self .extractor = ChainedExtractor (str (method_or_extractor )[0 :last ])
208
233
self .updater = UniversalUpdater (str (method_or_extractor )[last + 1 :])
209
234
210
- def get_extractor (self ) -> ValueExtractor :
235
+ def get_extractor (self ) -> ValueExtractor [ T , E ] :
211
236
return self .extractor
212
237
213
238
def get_updator (self ) -> ValueUpdater :
@@ -227,10 +252,24 @@ def __init__(self, method: str) -> None:
227
252
228
253
If method ends in a '()', then the name is a method name. This implementation assumes that a target's class
229
254
will have one and only one method with the specified name and this method will have exactly one parameter; if
230
- the method is a property name, there should be a corresponding JavaBean property modifier method or it will
255
+ the method is a property name, there should be a corresponding JavaBean property modifier method, or it will
231
256
be used as a key in a Map.
232
257
233
258
:param method: a method or property name
234
259
"""
235
260
super ().__init__ ()
236
261
self .name = method
262
+
263
+
264
+ def extract (expression : str ) -> ValueExtractor [T , E ]:
265
+ if "." in expression :
266
+ return ChainedExtractor (expression )
267
+ elif "," in expression :
268
+ return MultiExtractor (expression )
269
+ else :
270
+ return UniversalExtractor (expression )
271
+
272
+
273
+ ExtractorExpression : TypeAlias = ValueExtractor [T , E ] | str
274
+ ManipulatorExpression : TypeAlias = ValueManipulator | str
275
+ UpdaterExpression : TypeAlias = ValueUpdater | str
0 commit comments